Skip to content

Commit 3d662fc

Browse files
committed
refactor: api
Signed-off-by: Lexus Drumgold <[email protected]>
1 parent d5a7d01 commit 3d662fc

File tree

106 files changed

+7972
-1498
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+7972
-1498
lines changed

.codecov.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ component_management:
2828
name: constructs
2929
paths:
3030
- src/constructs/*.mts
31+
- component_id: internal
32+
name: internal
33+
paths:
34+
- src/internal/*.mts
3135
- component_id: utils
3236
name: utils
3337
paths:
@@ -100,3 +104,4 @@ profiling:
100104
- src/constructs/initialize.mts
101105
- src/create-tokenizer.mts
102106
- src/preprocess.mts
107+
- src/tokenize.mts

.dictionary.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ docast
88
dohm
99
dprint
1010
fbca
11+
gemoji
1112
ggshield
1213
gpgsign
1314
hmarr

__fixtures__/constructs/flag-long.mts

Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
/**
2+
* @file Constructs - longFlag
3+
* @module fixtures/constructs/longFlag
4+
*/
5+
6+
import chars from '#enums/chars'
7+
import codes from '#enums/codes'
8+
import ev from '#enums/ev'
9+
import operand from '#fixtures/constructs/operand'
10+
import tt from '#fixtures/tt'
11+
import isBreak from '#tests/utils/is-break'
12+
import type {
13+
Code,
14+
Construct,
15+
Effects,
16+
Event,
17+
State,
18+
TokenizeContext
19+
} from '@flex-development/vfile-tokenizer'
20+
import { ok as assert } from 'devlop'
21+
import { asciiAlphanumeric } from 'micromark-util-character'
22+
23+
/**
24+
* Long flag construct.
25+
*
26+
* A long flag starts with two hyphens ([HYPHEN-MINUS `U+002D`][hyphen])
27+
* followed by one or more case-insensitive alphanumeric characters.
28+
*
29+
* Hyphens and full stops ([FULL STOP `U+002E`][full-stop]) can be used to
30+
* separate words, as well as camelCase format.
31+
*
32+
* [full-stop]: https://www.fileformat.info/info/unicode/char/002e/index.htm
33+
* [hyphen]: https://www.fileformat.info/info/unicode/char/002d/index.htm
34+
* [lowline]: https://www.fileformat.info/info/unicode/char/005f/index.htm
35+
*
36+
* @const {Construct} longFlag
37+
*/
38+
const longFlag: Construct = {
39+
name: 'longFlag',
40+
previous: isBreak,
41+
resolveTo: resolveToLongFlag,
42+
test: testLongFlag,
43+
tokenize: tokenizeLongFlag
44+
}
45+
46+
export default longFlag
47+
48+
declare module '@flex-development/vfile-tokenizer' {
49+
interface TokenInfo {
50+
value?: string | null | undefined
51+
}
52+
53+
interface TokenFields {
54+
chunk?: number | null | undefined
55+
long?: boolean | null | undefined
56+
}
57+
58+
interface TokenTypeMap {
59+
flag: tt.flag
60+
flagId: tt.id
61+
}
62+
63+
interface TokenizeContext {
64+
delimiter?: boolean | null | undefined
65+
}
66+
}
67+
68+
/**
69+
* Resolve the events parsed from the start of the content (which may include
70+
* other constructs) to the last one parsed by {@linkcode tokenizeLongFlag}.
71+
*
72+
* @this {void}
73+
*
74+
* @param {Event[]} events
75+
* List of events
76+
* @param {TokenizeContext} context
77+
* Tokenize context
78+
* @return {Event[]}
79+
* List of changed events
80+
*/
81+
function resolveToLongFlag(
82+
this: void,
83+
events: Event[],
84+
context: TokenizeContext
85+
): Event[] {
86+
/**
87+
* Index of current event.
88+
*
89+
* @var {number} index
90+
*/
91+
let index: number = -1
92+
93+
while (++index < events.length) {
94+
assert(events[index], 'expected `events[index]`')
95+
const [event, token] = events[index]!
96+
97+
// merge flag id events into flag events.
98+
if (event === ev.enter && token.type === tt.flag) {
99+
assert(token.long, 'expected long flag')
100+
101+
/**
102+
* Next event.
103+
*
104+
* @const {Event | undefined} next
105+
*/
106+
const next: Event | undefined = events[index + 1]
107+
108+
if (next && next[0] === ev.enter && next[1].type === tt.id) {
109+
events.splice(index + 1, 2)
110+
const [, id] = next
111+
112+
// capture option flag id.
113+
id.value = context.sliceSerialize(id)
114+
assert(id.value, 'expected non-empty flag id token value')
115+
116+
// set option flag.
117+
token.value = chars.hyphen + chars.hyphen + id.value
118+
}
119+
}
120+
}
121+
122+
return events
123+
}
124+
125+
/**
126+
* Check if the current character `code` can start a long flag.
127+
*
128+
* @this {TokenizeContext}
129+
*
130+
* @param {Code} code
131+
* The current character code
132+
* @return {boolean}
133+
* `true` if `code` can start long flag construct
134+
*/
135+
function testLongFlag(this: TokenizeContext, code: Code): boolean {
136+
return !this.delimiter && code === codes.hyphen
137+
}
138+
139+
/**
140+
* Tokenize a long flag.
141+
*
142+
* @example
143+
* ```markdown
144+
* > | --long
145+
* ```
146+
*
147+
* @example
148+
* ```markdown
149+
* > | --long.flag
150+
* ```
151+
*
152+
* @example
153+
* ```markdown
154+
* > | --long-flag
155+
* ```
156+
*
157+
* @example
158+
* ```markdown
159+
* > | --long=value
160+
* ```
161+
*
162+
* @example
163+
* ```markdown
164+
* > | --long.flag=value
165+
* ```
166+
*
167+
* @example
168+
* ```markdown
169+
* > | --long-flag=value
170+
* ```
171+
*
172+
* @this {TokenizeContext}
173+
*
174+
* @param {Effects} effects
175+
* Context object to transition state machine
176+
* @param {State} ok
177+
* Successful tokenization state
178+
* @param {State} nok
179+
* Failed tokenization state
180+
* @return {State}
181+
* Initial state
182+
*/
183+
function tokenizeLongFlag(
184+
this: TokenizeContext,
185+
effects: Effects,
186+
ok: State,
187+
nok: State
188+
): State {
189+
/**
190+
* Tokenize context.
191+
*
192+
* @const {TokenizeContext} self
193+
*/
194+
const self: TokenizeContext = this
195+
196+
return longFlag
197+
198+
/**
199+
* Start of a long flag.
200+
*
201+
* @example
202+
* ```markdown
203+
* > | --long-flag
204+
* ^
205+
* ```
206+
*
207+
* @this {void}
208+
*
209+
* @param {Code} code
210+
* The current character code
211+
* @return {State | undefined}
212+
* Next state
213+
*/
214+
function longFlag(this: void, code: Code): State | undefined {
215+
assert(code === codes.hyphen, 'expected `-`')
216+
effects.enter(tt.flag, { chunk: self.chunk, long: true })
217+
effects.consume(code)
218+
return after
219+
}
220+
221+
/**
222+
* After first long flag marker.
223+
*
224+
* @example
225+
* ```markdown
226+
* > | --long-flag
227+
* ^
228+
* ```
229+
*
230+
* @this {void}
231+
*
232+
* @param {Code} code
233+
* The current character code
234+
* @return {State | undefined}
235+
* Next state
236+
*/
237+
function after(this: void, code: Code): State | undefined {
238+
if (code !== codes.hyphen) return nok(code)
239+
return effects.consume(code), beforeId
240+
}
241+
242+
/**
243+
* Before long flag id.
244+
*
245+
* @example
246+
* ```markdown
247+
* > | --long-flag
248+
* ^
249+
* ```
250+
*
251+
* @this {void}
252+
*
253+
* @param {Code} code
254+
* The current character code
255+
* @return {State | undefined}
256+
* Next state
257+
*/
258+
function beforeId(this: void, code: Code): State | undefined {
259+
if (!asciiAlphanumeric(code) && code !== codes.hyphen) return nok(code)
260+
return effects.enter(tt.id, { chunk: self.chunk }), id(code)
261+
}
262+
263+
/**
264+
* Inside long flag id.
265+
*
266+
* @example
267+
* ```markdown
268+
* > | --long-flag
269+
* ^^^^^^^^^
270+
* ```
271+
* @example
272+
* ```markdown
273+
* > | --long.flag
274+
* ^^^^^^^^^
275+
* ```
276+
*
277+
* @example
278+
* ```markdown
279+
* > | --long-flag=value
280+
* ^^^^^^^^^
281+
* ```
282+
* @example
283+
* ```markdown
284+
* > | --long.flag=value
285+
* ^^^^^^^^^
286+
* ```
287+
*
288+
* @this {void}
289+
*
290+
* @param {Code} code
291+
* The current character code
292+
* @return {State | undefined}
293+
* Next state
294+
*/
295+
function id(this: void, code: Code): State | undefined {
296+
switch (true) {
297+
case asciiAlphanumeric(code):
298+
case code === codes.dot:
299+
case code === codes.hyphen:
300+
return effects.consume(code), id
301+
case code === codes.equal:
302+
effects.exit(tt.id)
303+
effects.exit(tt.flag)
304+
effects.consume(code)
305+
return effects.attempt(operand, ok, ok)
306+
case isBreak(code):
307+
/**
308+
* Flag id codes.
309+
*
310+
* @const {Code[]} flagId
311+
*/
312+
const flagId: Code[] = self.sliceStream(effects.exit(tt.id))
313+
314+
// ensure flag is not actually a delimiter.
315+
if (flagId.length === 1 && flagId[0] === codes.hyphen) break
316+
return effects.exit(tt.flag), ok(code)
317+
default:
318+
break
319+
}
320+
321+
return nok(code)
322+
}
323+
}
Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
/**
22
* @file Constructs - lineEnding
3-
* @module tests/constructs/lineEnding
3+
* @module fixtures/constructs/lineEnding
44
*/
55

6-
import resolveSlice from '#utils/resolve-slice'
7-
import type { Construct, Tokenizer } from '@flex-development/vfile-tokenizer'
6+
import type { Construct } from '@flex-development/vfile-tokenizer'
87
import * as micromark from 'micromark-core-commonmark'
98
import { markdownLineEnding } from 'micromark-util-character'
109

@@ -15,9 +14,11 @@ import { markdownLineEnding } from 'micromark-util-character'
1514
*/
1615
const lineEnding: Construct = {
1716
name: micromark.lineEnding.name,
18-
resolve: resolveSlice,
1917
test: markdownLineEnding,
20-
tokenize: micromark.lineEnding.tokenize as unknown as Tokenizer
18+
19+
// @ts-expect-error [2332] micromark tokenizers have custom fields, which
20+
// users are supposed to manually type.
21+
tokenize: micromark.lineEnding.tokenize
2122
}
2223

2324
export default lineEnding

0 commit comments

Comments
 (0)