@fuzdev/mdz 0.8.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Mdz.svelte +37 -1
- package/dist/Mdz.svelte.d.ts +35 -0
- package/dist/Mdz.svelte.d.ts.map +1 -1
- package/dist/MdzNodeView.svelte +143 -27
- package/dist/MdzNodeView.svelte.d.ts +13 -1
- package/dist/MdzNodeView.svelte.d.ts.map +1 -1
- package/dist/MdzPrecompiled.svelte.d.ts.map +1 -1
- package/dist/MdzRoot.svelte.d.ts.map +1 -1
- package/dist/MdzStream.svelte.d.ts.map +1 -1
- package/dist/MdzStreamNodeView.svelte.d.ts.map +1 -1
- package/dist/context_helpers.d.ts.map +1 -1
- package/dist/mdz.d.ts +4 -1
- package/dist/mdz.d.ts.map +1 -1
- package/dist/mdz.js +4 -1
- package/dist/mdz_contexts.d.ts +14 -2
- package/dist/mdz_contexts.d.ts.map +1 -1
- package/dist/mdz_debug_work.d.ts.map +1 -1
- package/dist/mdz_helpers.d.ts +57 -7
- package/dist/mdz_helpers.d.ts.map +1 -1
- package/dist/mdz_helpers.js +113 -50
- package/dist/mdz_lexer.d.ts.map +1 -1
- package/dist/mdz_lexer.js +21 -17
- package/dist/mdz_opcodes.d.ts.map +1 -1
- package/dist/mdz_opcodes_to_nodes.d.ts.map +1 -1
- package/dist/mdz_stream_parser.d.ts +16 -3
- package/dist/mdz_stream_parser.d.ts.map +1 -1
- package/dist/mdz_stream_parser.js +16 -3
- package/dist/mdz_stream_parser_block.d.ts +2 -2
- package/dist/mdz_stream_parser_block.d.ts.map +1 -1
- package/dist/mdz_stream_parser_block.js +2 -2
- package/dist/mdz_stream_parser_inline.d.ts.map +1 -1
- package/dist/mdz_stream_parser_inline.js +4 -4
- package/dist/mdz_stream_parser_link.d.ts.map +1 -1
- package/dist/mdz_stream_parser_list.d.ts.map +1 -1
- package/dist/mdz_stream_parser_state.d.ts +32 -12
- package/dist/mdz_stream_parser_state.d.ts.map +1 -1
- package/dist/mdz_stream_parser_state.js +21 -14
- package/dist/mdz_stream_parser_table.d.ts.map +1 -1
- package/dist/mdz_stream_parser_text.d.ts.map +1 -1
- package/dist/mdz_stream_parser_text.js +7 -4
- package/dist/mdz_stream_parser_url.d.ts +3 -2
- package/dist/mdz_stream_parser_url.d.ts.map +1 -1
- package/dist/mdz_stream_parser_url.js +114 -42
- package/dist/mdz_stream_state.svelte.d.ts.map +1 -1
- package/dist/mdz_to_svelte.d.ts.map +1 -1
- package/dist/mdz_token_parser.d.ts.map +1 -1
- package/dist/svelte_preprocess_mdz.d.ts.map +1 -1
- package/dist/svelte_preprocess_mdz.js +8 -0
- package/dist/tsdoc_mdz.d.ts +8 -4
- package/dist/tsdoc_mdz.d.ts.map +1 -1
- package/dist/tsdoc_mdz.js +14 -4
- package/package.json +7 -7
- package/src/lib/mdz.ts +4 -1
- package/src/lib/mdz_contexts.ts +20 -2
- package/src/lib/mdz_helpers.ts +117 -49
- package/src/lib/mdz_lexer.ts +31 -14
- package/src/lib/mdz_stream_parser.ts +16 -3
- package/src/lib/mdz_stream_parser_block.ts +2 -2
- package/src/lib/mdz_stream_parser_inline.ts +4 -4
- package/src/lib/mdz_stream_parser_state.ts +34 -13
- package/src/lib/mdz_stream_parser_text.ts +13 -6
- package/src/lib/mdz_stream_parser_url.ts +130 -49
- package/src/lib/svelte_preprocess_mdz.ts +8 -0
- package/src/lib/tsdoc_mdz.ts +15 -4
|
@@ -18,8 +18,14 @@ import {
|
|
|
18
18
|
SPACE,
|
|
19
19
|
TAB,
|
|
20
20
|
PERIOD,
|
|
21
|
+
LEFT_PAREN,
|
|
22
|
+
RIGHT_PAREN,
|
|
21
23
|
SLASH,
|
|
22
24
|
ascii_to_lower,
|
|
25
|
+
is_bare_path_prefix,
|
|
26
|
+
is_italic_boundary_blocker,
|
|
27
|
+
scan_uri_chars_end,
|
|
28
|
+
is_path_boundary,
|
|
23
29
|
is_valid_path_char,
|
|
24
30
|
is_word_char,
|
|
25
31
|
match_url_prefix_case_insensitive,
|
|
@@ -33,7 +39,9 @@ import {
|
|
|
33
39
|
emit,
|
|
34
40
|
ensure_paragraph,
|
|
35
41
|
flush_text,
|
|
36
|
-
|
|
42
|
+
is_run_boundary,
|
|
43
|
+
offset,
|
|
44
|
+
revert_failed_close
|
|
37
45
|
} from './mdz_stream_parser_state.ts';
|
|
38
46
|
|
|
39
47
|
const HTTPS_PREFIX = 'https://';
|
|
@@ -62,15 +70,8 @@ export const try_auto_url_forced = (state: MdzStreamParserState): TryResult => {
|
|
|
62
70
|
|
|
63
71
|
/** @nodocs */
|
|
64
72
|
export const try_auto_path_absolute = (state: MdzStreamParserState, forced = false): TryResult => {
|
|
65
|
-
// must be at
|
|
66
|
-
if (
|
|
67
|
-
state.prev_char !== -1 &&
|
|
68
|
-
state.prev_char !== SPACE &&
|
|
69
|
-
state.prev_char !== NEWLINE &&
|
|
70
|
-
state.prev_char !== TAB
|
|
71
|
-
) {
|
|
72
|
-
return 'not_match';
|
|
73
|
-
}
|
|
73
|
+
// must be at a path boundary (start, whitespace, or `(`)
|
|
74
|
+
if (!is_path_boundary(state.prev_char, state.before_prev_char)) return 'not_match';
|
|
74
75
|
|
|
75
76
|
// reject // and /space
|
|
76
77
|
if (state.pos + 1 >= state.buffer.length) return forced ? 'not_match' : 'need_more';
|
|
@@ -86,14 +87,8 @@ export const try_auto_path_absolute = (state: MdzStreamParserState, forced = fal
|
|
|
86
87
|
|
|
87
88
|
/** @nodocs */
|
|
88
89
|
export const try_auto_path_relative = (state: MdzStreamParserState, forced = false): TryResult => {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
state.prev_char !== SPACE &&
|
|
92
|
-
state.prev_char !== NEWLINE &&
|
|
93
|
-
state.prev_char !== TAB
|
|
94
|
-
) {
|
|
95
|
-
return 'not_match';
|
|
96
|
-
}
|
|
90
|
+
// must be at a path boundary (start, whitespace, or `(`)
|
|
91
|
+
if (!is_path_boundary(state.prev_char, state.before_prev_char)) return 'not_match';
|
|
97
92
|
|
|
98
93
|
// check for ./ or ../
|
|
99
94
|
const remaining = state.buffer.length - state.pos;
|
|
@@ -147,7 +142,9 @@ export const start_pending_url = (
|
|
|
147
142
|
link_type,
|
|
148
143
|
confirmed: true,
|
|
149
144
|
viable_https: false,
|
|
150
|
-
viable_http: false
|
|
145
|
+
viable_http: false,
|
|
146
|
+
// a path opened by `(` ends at the `)` matching it
|
|
147
|
+
paren_depth: state.prev_char === LEFT_PAREN ? 0 : -1
|
|
151
148
|
};
|
|
152
149
|
// don't advance pos — URL chars start at current position, handled by process_url_content
|
|
153
150
|
};
|
|
@@ -169,7 +166,8 @@ export const start_speculative_url = (state: MdzStreamParserState): void => {
|
|
|
169
166
|
link_type: 'external',
|
|
170
167
|
confirmed: false,
|
|
171
168
|
viable_https: true,
|
|
172
|
-
viable_http: true
|
|
169
|
+
viable_http: true,
|
|
170
|
+
paren_depth: -1
|
|
173
171
|
};
|
|
174
172
|
};
|
|
175
173
|
|
|
@@ -227,8 +225,9 @@ export const process_url_prefix = (state: MdzStreamParserState): void => {
|
|
|
227
225
|
|
|
228
226
|
/**
|
|
229
227
|
* Process URL/path chars in text-first mode. Scans path chars,
|
|
230
|
-
* accumulates as visible text. Terminates on space/newline/non-path-char
|
|
231
|
-
*
|
|
228
|
+
* accumulates as visible text. Terminates on space/newline/non-path-char —
|
|
229
|
+
* or, for a path opened by `(`, on the `)` matching that opener — and emits
|
|
230
|
+
* a `wrap` opcode.
|
|
232
231
|
*
|
|
233
232
|
* @nodocs
|
|
234
233
|
*/
|
|
@@ -239,37 +238,108 @@ export const process_url_content = (state: MdzStreamParserState, forced: boolean
|
|
|
239
238
|
return;
|
|
240
239
|
}
|
|
241
240
|
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
// URL terminator
|
|
245
|
-
if (char_code === SPACE || char_code === NEWLINE || !is_valid_path_char(char_code)) {
|
|
246
|
-
flush_text(state);
|
|
247
|
-
complete_pending_url(state);
|
|
248
|
-
// don't consume terminator — outer loop handles it
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// scan URL chars
|
|
241
|
+
const pu = state.pending_url!;
|
|
253
242
|
const start = state.pos;
|
|
254
|
-
state.pos++;
|
|
255
243
|
while (state.pos < state.buffer.length) {
|
|
256
244
|
const c = state.buffer.charCodeAt(state.pos);
|
|
257
|
-
if (
|
|
245
|
+
if (!is_valid_path_char(c)) break;
|
|
246
|
+
if (pu.paren_depth !== -1) {
|
|
247
|
+
if (c === LEFT_PAREN) pu.paren_depth++;
|
|
248
|
+
else if (c === RIGHT_PAREN) {
|
|
249
|
+
if (pu.paren_depth === 0) break;
|
|
250
|
+
pu.paren_depth--;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
258
253
|
state.pos++;
|
|
259
254
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
255
|
+
|
|
256
|
+
if (state.pos > start) {
|
|
257
|
+
const text = state.buffer.slice(start, state.pos);
|
|
258
|
+
accumulate_text(state, text, offset(state, start));
|
|
259
|
+
pu.url_text += text;
|
|
260
|
+
state.prev_char = state.buffer.charCodeAt(state.pos - 1);
|
|
261
|
+
state.column += state.pos - start;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// a terminator in the buffer ends the URL (not consumed — the outer loop
|
|
265
|
+
// handles it); at EOF in forced mode the buffer end does
|
|
266
|
+
if (state.pos < state.buffer.length || forced) {
|
|
268
267
|
flush_text(state);
|
|
269
268
|
complete_pending_url(state);
|
|
270
269
|
}
|
|
271
270
|
};
|
|
272
271
|
|
|
272
|
+
// the char at the parse position, or `-1` at the end of the buffer
|
|
273
|
+
const char_after_pos = (state: MdzStreamParserState): number =>
|
|
274
|
+
state.pos < state.buffer.length ? state.buffer.charCodeAt(state.pos) : -1;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Resolve the open spans whose first closer candidate an auto-link swallowed,
|
|
278
|
+
* the way the one-shot parse does. The lexer pairs each `**`/`~~`/`_` opener
|
|
279
|
+
* with the first occurrence of its delimiter after it
|
|
280
|
+
* (`#tokenize_double_delimiter`, `#tokenize_italic`). Streaming resolves each
|
|
281
|
+
* open span's earlier candidates as they arrive, so a delimiter inside the
|
|
282
|
+
* link's reference is that first occurrence, and the lexer's outcome depends
|
|
283
|
+
* on the span:
|
|
284
|
+
*
|
|
285
|
+
* - `**` and `~~` pair with the occurrence unconditionally; the span's children
|
|
286
|
+
* lex bounded by it, the link overruns it, and the opener stays literal —
|
|
287
|
+
* as does every delimiter, tag, and code opener lexed inside the span (each
|
|
288
|
+
* bounded by the swallowed closer). Markdown links aren't bounded by an
|
|
289
|
+
* enclosing span, so Link frames survive.
|
|
290
|
+
* - `_` first checks the candidate's closing boundary (`is_italic_boundary_blocker`
|
|
291
|
+
* on the char after it). A blocked candidate rejects only the opener — the
|
|
292
|
+
* span's content then lexes unbounded, so inner spans survive — while a
|
|
293
|
+
* valid one pairs and overruns like the doubled delimiters.
|
|
294
|
+
*
|
|
295
|
+
* So: the lowest span in the current run that pairs inside the reference is
|
|
296
|
+
* reverted along with every non-Link frame above it, and an italic below it
|
|
297
|
+
* with a blocked candidate is reverted alone (mid-stack, frames above it kept).
|
|
298
|
+
* Called after the link's `wrap`/`close` is emitted, so a pending wrap never
|
|
299
|
+
* outlives an enclosing revert.
|
|
300
|
+
*
|
|
301
|
+
* @param scanned - the scanned URL/path text, starting with `reference` and
|
|
302
|
+
* including any trimmed tail
|
|
303
|
+
* @param after_scanned - the char code after `scanned`, or `-1` at end of input
|
|
304
|
+
*/
|
|
305
|
+
const revert_spans_closed_inside = (
|
|
306
|
+
state: MdzStreamParserState,
|
|
307
|
+
reference: string,
|
|
308
|
+
scanned: string,
|
|
309
|
+
after_scanned: number
|
|
310
|
+
): void => {
|
|
311
|
+
let run_start = 0;
|
|
312
|
+
for (let i = state.stack.length - 1; i >= 0; i--) {
|
|
313
|
+
if (is_run_boundary(state.stack[i]!.node_type)) {
|
|
314
|
+
run_start = i + 1;
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
let paired = -1; // lowest span pairing inside the reference
|
|
319
|
+
let rejected_italic = -1; // an italic whose candidate's boundary is blocked
|
|
320
|
+
for (let i = run_start; i < state.stack.length && paired === -1; i++) {
|
|
321
|
+
const entry = state.stack[i]!;
|
|
322
|
+
if (entry.node_type === 'Bold' || entry.node_type === 'Strikethrough') {
|
|
323
|
+
if (reference.includes(entry.delimiter)) paired = i;
|
|
324
|
+
} else if (entry.node_type === 'Italic') {
|
|
325
|
+
const k = reference.indexOf('_');
|
|
326
|
+
if (k === -1) continue;
|
|
327
|
+
const after = k + 1 < scanned.length ? scanned.charCodeAt(k + 1) : after_scanned;
|
|
328
|
+
if (after !== -1 && is_italic_boundary_blocker(after)) rejected_italic = i;
|
|
329
|
+
else paired = i;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (paired !== -1) {
|
|
333
|
+
for (let i = state.stack.length - 1; i >= paired; i--) {
|
|
334
|
+
if (state.stack[i]!.node_type !== 'Link') revert_failed_close(state, i);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
// below `paired` (or with nothing paired) — indices below it are unchanged
|
|
338
|
+
if (rejected_italic !== -1 && (paired === -1 || rejected_italic < paired)) {
|
|
339
|
+
revert_failed_close(state, rejected_italic);
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
|
|
273
343
|
/**
|
|
274
344
|
* Complete a pending URL: compute reference with trimmed punctuation,
|
|
275
345
|
* emit a `wrap` opcode to retroactively wrap the text node in a Link.
|
|
@@ -287,6 +357,8 @@ export const complete_pending_url = (state: MdzStreamParserState): void => {
|
|
|
287
357
|
|
|
288
358
|
const reference = mdz_trim_url_punctuation(pu.url_text);
|
|
289
359
|
if (reference.length === 0) return; // empty after trimming — text stays as plain text
|
|
360
|
+
// a bare path prefix (`./`, `/`) left after trimming isn't a path — text stays
|
|
361
|
+
if (pu.link_type === 'internal' && is_bare_path_prefix(reference)) return;
|
|
290
362
|
|
|
291
363
|
// for external URLs, must have content after protocol prefix
|
|
292
364
|
if (pu.link_type === 'external') {
|
|
@@ -316,6 +388,7 @@ export const complete_pending_url = (state: MdzStreamParserState): void => {
|
|
|
316
388
|
trim_id
|
|
317
389
|
});
|
|
318
390
|
state.active_text_id = trim_id;
|
|
391
|
+
revert_spans_closed_inside(state, reference, pu.url_text, char_after_pos(state));
|
|
319
392
|
} else {
|
|
320
393
|
emit(state, {
|
|
321
394
|
type: 'wrap',
|
|
@@ -328,6 +401,7 @@ export const complete_pending_url = (state: MdzStreamParserState): void => {
|
|
|
328
401
|
end: url_end
|
|
329
402
|
});
|
|
330
403
|
state.active_text_id = null;
|
|
404
|
+
revert_spans_closed_inside(state, reference, pu.url_text, char_after_pos(state));
|
|
331
405
|
}
|
|
332
406
|
};
|
|
333
407
|
|
|
@@ -341,16 +415,17 @@ export const scan_auto_link_forced = (
|
|
|
341
415
|
state: MdzStreamParserState,
|
|
342
416
|
link_type: 'external' | 'internal'
|
|
343
417
|
): TryResult => {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
418
|
+
// a path opened by `(` ends at the `)` matching it
|
|
419
|
+
const i = scan_uri_chars_end(
|
|
420
|
+
state.buffer,
|
|
421
|
+
state.pos,
|
|
422
|
+
link_type === 'internal' && state.prev_char === LEFT_PAREN
|
|
423
|
+
);
|
|
350
424
|
|
|
351
425
|
let reference = state.buffer.slice(state.pos, i);
|
|
352
426
|
reference = mdz_trim_url_punctuation(reference);
|
|
353
427
|
if (reference.length === 0) return 'not_match';
|
|
428
|
+
if (link_type === 'internal' && is_bare_path_prefix(reference)) return 'not_match';
|
|
354
429
|
|
|
355
430
|
// for external URLs, must have content after protocol prefix
|
|
356
431
|
if (link_type === 'external') {
|
|
@@ -382,5 +457,11 @@ export const scan_auto_link_forced = (
|
|
|
382
457
|
state.column += end_pos - state.pos;
|
|
383
458
|
state.prev_char = reference.charCodeAt(reference.length - 1);
|
|
384
459
|
state.pos = end_pos;
|
|
460
|
+
revert_spans_closed_inside(
|
|
461
|
+
state,
|
|
462
|
+
reference,
|
|
463
|
+
state.buffer.slice(link_start - state.base_offset, i),
|
|
464
|
+
i < state.buffer.length ? state.buffer.charCodeAt(i) : -1
|
|
465
|
+
);
|
|
385
466
|
return 'consumed';
|
|
386
467
|
};
|
|
@@ -397,9 +397,17 @@ const find_mdz_usages = (
|
|
|
397
397
|
const inline = inline_attr ? extract_static_boolean(inline_attr.value) : false;
|
|
398
398
|
if (inline === null) return; // dynamic inline — fall back to runtime
|
|
399
399
|
|
|
400
|
+
// precompiled markup carries no source offsets, so content that asks for
|
|
401
|
+
// them renders at runtime; a static `false` asks for nothing and is dropped
|
|
402
|
+
const source_offsets_attr = find_attribute(node, 'source_offsets');
|
|
403
|
+
if (source_offsets_attr && extract_static_boolean(source_offsets_attr.value) !== false) {
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
|
|
400
407
|
// Collect attributes to exclude from precompiled output
|
|
401
408
|
const exclude_attrs: Set<AST.Attribute> = new Set([content_attr]);
|
|
402
409
|
if (base_attr) exclude_attrs.add(base_attr);
|
|
410
|
+
if (source_offsets_attr) exclude_attrs.add(source_offsets_attr);
|
|
403
411
|
|
|
404
412
|
// Extract static string value
|
|
405
413
|
const content_value = extract_static_string(content_attr.value, context.bindings);
|
package/src/lib/tsdoc_mdz.ts
CHANGED
|
@@ -20,11 +20,12 @@ const format_reference = (ref: string): string => (mdz_is_url(ref) ? ref : `\`${
|
|
|
20
20
|
* - `{@link url|text}` → `[text](url)` (markdown link, TSDoc canonical form)
|
|
21
21
|
* - `{@link url text}` → `[text](url)` (TS-lenient space-separated form)
|
|
22
22
|
* - `{@link https://...}` → `https://...` (bare URL, auto-linked by mdz)
|
|
23
|
-
* - `{@link identifier}` →
|
|
23
|
+
* - `{@link identifier}` → the identifier as a code span
|
|
24
24
|
* - Bare URLs → returned as-is
|
|
25
25
|
* - Bare markdown links (`[text](url)` ...) → returned as-is
|
|
26
|
+
* - Content that opens with a backtick (a code span, then description text) → returned as-is
|
|
26
27
|
* - Bare identifiers → wrapped in backticks
|
|
27
|
-
* - `identifier description text` →
|
|
28
|
+
* - `identifier description text` → the first token wrapped in backticks, the rest as written
|
|
28
29
|
*
|
|
29
30
|
* @param content - raw `@see` tag content in TSDoc format
|
|
30
31
|
* @returns mdz-formatted string ready for `Mdz` component
|
|
@@ -40,11 +41,14 @@ const format_reference = (ref: string): string => (mdz_is_url(ref) ? ref : `\`${
|
|
|
40
41
|
* mdz_from_tsdoc('https://fuz.dev')
|
|
41
42
|
* // → 'https://fuz.dev'
|
|
42
43
|
*
|
|
43
|
-
* mdz_from_tsdoc('[svelte-docinfo](https://github.com/
|
|
44
|
-
* // → '[svelte-docinfo](https://github.com/
|
|
44
|
+
* mdz_from_tsdoc('[svelte-docinfo](https://github.com/fuzdev/svelte-docinfo) for the analysis library')
|
|
45
|
+
* // → '[svelte-docinfo](https://github.com/fuzdev/svelte-docinfo) for the analysis library'
|
|
45
46
|
*
|
|
46
47
|
* mdz_from_tsdoc('tome.ts for the documentation system')
|
|
47
48
|
* // → '`tome.ts` for the documentation system'
|
|
49
|
+
*
|
|
50
|
+
* mdz_from_tsdoc('`tome.ts` for the `Tome` type')
|
|
51
|
+
* // → '`tome.ts` for the `Tome` type'
|
|
48
52
|
* ```
|
|
49
53
|
*/
|
|
50
54
|
export const mdz_from_tsdoc = (content: string): string => {
|
|
@@ -85,6 +89,13 @@ export const mdz_from_tsdoc = (content: string): string => {
|
|
|
85
89
|
return trimmed;
|
|
86
90
|
}
|
|
87
91
|
|
|
92
|
+
// Pass through content that opens with a backtick: a leading code span is already mdz,
|
|
93
|
+
// and wrapping it again would leave both spans literal. An unclosed one is returned as
|
|
94
|
+
// written too, since adding a backtick the author didn't write can't make it a span.
|
|
95
|
+
if (trimmed.charCodeAt(0) === 96 /* ` */) {
|
|
96
|
+
return trimmed;
|
|
97
|
+
}
|
|
98
|
+
|
|
88
99
|
// Split at first whitespace: first token is the reference, rest is description
|
|
89
100
|
const space_index = trimmed.indexOf(' ');
|
|
90
101
|
if (space_index === -1) {
|