Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

types: add type definitions for micromark #17

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions buffer.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// TypeScript Version: 3.0

import buffer = require('./lib')

export = buffer
5 changes: 5 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// TypeScript Version: 3.0

import buffer = require('./buffer')

export = buffer
3 changes: 3 additions & 0 deletions lib/compile/html.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare function compileHTML(): void
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

compileHtml?


export = compileHTML
5 changes: 5 additions & 0 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {ParserOptions} from './parse'

declare function buffer(options?: ParserOptions): void

export = buffer
11 changes: 11 additions & 0 deletions lib/parse.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {Parser} from '../shared-types'

declare namespace createParser {
interface ParserOptions {
extensions: unknown[]
}
}

declare function createParser(options?: createParser.ParserOptions): Parser

export = createParser
3 changes: 3 additions & 0 deletions lib/postprocess.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare function postprocess(): void

export = postprocess
3 changes: 3 additions & 0 deletions lib/preprocess.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare function preprocess(): void

export = preprocess
5 changes: 5 additions & 0 deletions lib/steam.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {ParserOptions} from './parse'

declare function stream(options?: ParserOptions): void

export = stream
3 changes: 3 additions & 0 deletions lib/util/classify-character.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare function classifyCharacter(code: number): number

export = classifyCharacter
9 changes: 9 additions & 0 deletions lib/util/create-tokenizer.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {Parser, Point, Tokenizer} from '../../shared-types'

declare function createTokenizer(
parser: Parser,
initialize: unknown,
from: Point
): Tokenizer

export = createTokenizer
15 changes: 15 additions & 0 deletions lib/util/flat-map.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* @param array array to flatten
* @param map mapping function
* @param a passed to map function
* @typeParam T shape of item input to flatMap
* @typeParam U shape of item returned by flatMap
* @typeParam A shape of additional attribute passed
*/
declare function flatMap<T, U, A>(
array: T[][],
map: (array: T[], a: A) => U[],
a?: A
): U[]

export = flatMap
3 changes: 3 additions & 0 deletions lib/util/lowercase.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare function lowercase(code: number): number

export = lowercase
5 changes: 5 additions & 0 deletions lib/util/move-point.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {Point} from 'shared-types'

declare function movePoint(point: Point, offset: number): Point

export = movePoint
3 changes: 3 additions & 0 deletions lib/util/normalize-identifier.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare function normalizeIdentifier(value: string): string

export = normalizeIdentifier
3 changes: 3 additions & 0 deletions lib/util/normalize-uri.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare function normalizeUri(url: string): string

export = normalizeUri
5 changes: 5 additions & 0 deletions lib/util/prefix-size.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {Event, Type} from '../../shared-types'

declare function prefixSize(events: Event[], type: Type): number

export = prefixSize
3 changes: 3 additions & 0 deletions lib/util/safe-from-int.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare function safeFromInt(value: string, base?: number): string

export = safeFromInt
3 changes: 3 additions & 0 deletions lib/util/serialize-chunks.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare function serializeChunks(chunks: Array<string | number>): string

export = serializeChunks
3 changes: 3 additions & 0 deletions lib/util/shallow.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare function shallow<T>(object: T): T

export = shallow
5 changes: 5 additions & 0 deletions lib/util/slice-chunks.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {Token} from '../../shared-types'

declare function sliceChunks(chunks: string[], token: Token): string[]

export = sliceChunks
5 changes: 5 additions & 0 deletions lib/util/subtokenize.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {Event} from '../../shared-types'

declare function subtokenize(events: Event[]): {done: boolean; events: Event[]}

export = subtokenize
5 changes: 5 additions & 0 deletions micromark.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import micromarkBuffer = require('micromark')
import micromarkStream = require('micromark/stream')

micromarkBuffer()
micromarkStream()
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"browserify": "^16.0.0",
"commonmark.json": "^0.29.0",
"concat-stream": "^2.0.0",
"dtslint": "^4.0.0",
"gzip-size-cli": "^3.0.0",
"ms": "^2.0.0",
"nyc": "^15.0.0",
Expand All @@ -85,7 +86,8 @@
"format": "remark . -qfo && prettier . --write && xo --fix",
"test-api": "node test",
"test-coverage": "nyc --reporter lcov tape test/index.js",
"test": "npm run generate && npm run format && npm run test-coverage"
"test-types": "dtslint .",
"test": "npm run generate && npm run format && npm run test-coverage && npm run test-types"
},
"nyc": {
"check-coverage": true,
Expand Down
220 changes: 220 additions & 0 deletions shared-types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
// TypeScript Version: 3.0

/**
* A location in a string or buffer
*/
export interface Point {
line: number
column: number
offset: number
_index?: number
_bufferIndex?: number
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the underscore props, as those are kinda for internal use in this project, should they be left undocumented?

Is there some comment to add here to say to TS that it shouldn’t be exported to library users?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could tag these with tsdoc

/**
 * @internal
 */

I'm cautious about removing them entirely, since extensions/plugins may leverage some of the internal attributes.

}

/**
* A token type
*
* TODO: enumerate token types
*/
export type Type = string

/**
*
*/
export interface Token {
type: Type
start: Point
end: Point

previous: Token
next: Token

/**
* Declares a token as having content of a certain type.
* Because markdown requires to first parse containers, flow, content completely,
* and then later go on to phrasing and such, it needs to be declared somewhere on the tokens.
*/
contentType: 'flow' | 'content' | 'string' | 'text'

// TODO move these to interfaces extending Token, or move these to a `data` property similar to unist (would require code refactor)
/**
* Used for whitespace in several places which needs to account for tab stops
*/
_size?: number

/**
* ends with a CR, LF, or CRLF.
*/
_break?: boolean

/**
* Used when dealing with linked tokens. A child tokenizer is needed to tokenize them, which is stored on those tokens
*/
_tokenizer?: Tokenizer

/**
* Used for attention (emphasis, strong).
*
* could be (enter emphasis, enter emphasisMarker, exit emphasisMarker, enter strong, enter strongMarker, exit strongMarker, enter data, exit data)
*/
_events?: Event[]

/**
* This is used for tokens that are already “subtokenized”.
*
* E.g., when parsing flow, there are content tokens, but those are directly tokenized into definitions/setext/paragraphs
*/
_subevents?: Event[]

/**
* Set to true to mark that a token (e.g., with subevents) is already handled
*/
_contentTokenized?: boolean

/**
* close and open are also used in attention:
* depending on the characters before and after sequences (**),
* the sequence can open, close, both, or none
*/
_open?: boolean

/**
* close and open are also used in attention:
* depending on the characters before and after sequences (**),
* the sequence can open, close, both, or none
*/
_close?: boolean
_marker?: number
_side?: number

/**
* Generally, tabs and spaces behave the same, but in the case of a hard break through trailing spaces ( \n), tabs
*/
_tabs?: boolean
}

/**
*
*/
export type Event = [string, Token, unknown]

/**
* These these are transitions to update the CommonMark State Machine (CSMS)
*/
export interface Effects {
/**
* Enter and exit define where tokens start and end
*/
enter: (type: Type) => void

/**
* Enter and exit define where tokens start and end
*/
exit: (type: Type) => void

/**
* Consume deals with a character, and moves to the next
*/
consume: (code: number) => void
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be a good idea to make a specific type for the character codes?

It can be any result (positive int) from String.charCodeAt (except NaN). And the following special codes are used:

// This module is compiled away!
exports.carriageReturn = -5
exports.lineFeed = -4
exports.carriageReturnLineFeed = -3
exports.horizontalTab = -2
exports.virtualSpace = -1
exports.eof = null

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could be a good idea.
Both the known types and character codes could be turned in a union of known types or turned into an opaque type, currently both are transparent primitives.

Copy link
Contributor

@ocavue ocavue Oct 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If micromark is written in native TypeScript, we can use TypeScript's const enum feature for these constant values.

By using const enum, those import statements will be removed while compiling (just like what script/babel-transform-constants.js does). And we also have an easy way to define types for character codes.

However, TypeScript doesn't allow null as a value of const enum, so we have to do something special for eof.


/**
* Attempt deals with several values, and tries to parse according to those values.
* If a value resulted in `ok`, it worked, the tokens that were made are used,
* and `returnState` is switched to.
* If the result is `nok`, the attempt failed,
* so we revert to the original state, and `bogusState` is used.
*/
attempt: (
notSureWhatThisIs:
| Construct
| Construct[]
| {[code: number]: Construct | Construct[]},
Comment on lines +128 to +131
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this construct like parameter called?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dunno!

I’ve been calling the third form “hooks”, as it defines which character codes are “hooked”. But that doesn’t say too much.

Maybe something like “usable” in unified? Attempt?

returnState: unknown,
bogusState?: unknown
) => (code: number) => void

/**
* interrupt is used for stuff right after a line of content.
*/
interrupt: (
notSureWhatThisIs:
| Construct
| Construct[]
| {[code: number]: Construct | Construct[]},
ok: Okay,
nok?: NotOkay
) => (code: number) => void

check: (
notSureWhatThisIs:
| Construct
| Construct[]
| {[code: number]: Construct | Construct[]},
ok: Okay,
nok?: NotOkay
) => (code: number) => void

/**
* lazy is used for lines that were not properly preceded by the container.
*/
lazy: (notSureWhatThisIs:
| Construct
| Construct[]
| {[code: number]: Construct | Construct[]},
ok: Okay,
nok?: NotOkay) => void
}

/**
*
*/
export type Okay = (code: number) => () => void
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the inner function have any parameters?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the params in check/lazy/interrupt also be called “returnState” and “bogusState”.

The tokenizer function gets effects, ok, nok, which is kind of like the executor function given to Promise: resolve, reject.
On the outside, you can pass returnState, bogusState, which is kind of like the functions given to then: onfulfill, onreject

They all get character codes

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these functions also should return another function: the next state-as-a-function to go to.
But there is one case where they return void: for the eof character code (at the end of a value)
The reason being: well, there isn’t any state that makes sense, so void works well. Practically that has also helped: if for some reason it was a mistake, then an exception is throw because there is no next function, meaning it surfaces early.


/**
*
*/
export type NotOkay = Okay

/**
*
*/
export interface Tokenizer {
previous: Token
events: Event[]
parser: Parser
sliceStream: (token: Token) => string[]
sliceSerialize: (token: Token) => string
now: () => Point
defineSkip: (value: Point) => void
write: (value: number) => Event[]
}

export type Construct = unknown
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is a construct?
Is it a function?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A construct is an object with:

  • a tokenize function
  • resolve, resolveTo, and resolveAll functions (which take a certain slice of events and can mutate them)
  • partial (bit ugly, used for things that aren’t really constructs, like whitespace)
  • concrete, used for multiline constructs such as HTML and fenced code that and how they mix with containers (e.g., > <div>\n> > this is html, not a second block quote)
  • Content has a name


/**
*
*/
export interface Parser {
hooks: {
[key: string]: Construct | Construct[]
}
flow: (something: unknown) => unknown
defined: unknown[]
}

/**
*
*/
export interface TokenizerThis {
events: Event[]
interrupt: unknown
lazy: unknown
containerState: {
marker: number
type: Type
initialBlankLine: unknown
size: number
_closeFlow: unknown
furtherBlankLines: unknown
}
}
5 changes: 5 additions & 0 deletions stream.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// TypeScript Version: 3.0

import stream = require('./lib/steam')

export = stream
18 changes: 18 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"moduleResolution": "node",
"lib": [
"ES5"
],
"strict": true,
"baseUrl": ".",
"paths": {
"micromark": [
"index.d.ts"
],
"micromark/stream": [
"stream.d.ts"
]
}
}
}