@agentshouse/mdmodel 0.1.0-alpha.17 → 0.1.0-alpha.18
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/API.md +8 -8
- package/dist/links.d.ts.map +1 -1
- package/dist/links.js +529 -148
- package/dist/links.js.map +1 -1
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +10 -2
- package/dist/project.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/fixtures/README.md +9 -3
- package/fixtures/cases/shortcut-reference-link/context.json +1 -0
- package/fixtures/cases/shortcut-reference-link/expected.json +6 -0
- package/fixtures/cases/shortcut-reference-link/library/item.md +10 -0
- package/fixtures/cases/shortcut-reference-link/schema.yaml +6 -0
- package/fixtures/cases/text-field-kind/context.json +1 -0
- package/fixtures/cases/text-field-kind/expected.json +7 -0
- package/fixtures/cases/text-field-kind/library/page.md +4 -0
- package/fixtures/cases/text-field-kind/schema.yaml +4 -0
- package/fixtures/projection.json +61 -0
- package/package.json +1 -1
package/dist/links.js
CHANGED
|
@@ -1,11 +1,49 @@
|
|
|
1
1
|
const TAB_STOP = 4;
|
|
2
2
|
const CLOSING_FENCE_INDENT = 3;
|
|
3
|
+
const CODE_INDENT = 4;
|
|
3
4
|
const HEADING = /^#{1,6}(?:[ \t]|$)/;
|
|
4
5
|
const BREAK = /^(?:(?:\*[ \t]*){3,}|(?:-[ \t]*){3,}|(?:_[ \t]*){3,})$/;
|
|
5
6
|
const SETEXT = /^(?:=+|-+)[ \t]*$/;
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
7
|
+
const BLANK = /^[ \t\v\f]*\r?$/;
|
|
8
|
+
const HTML_CLOSED = [
|
|
9
|
+
[
|
|
10
|
+
/^<(?:script|pre|style)(?:[ \t\v\f>]|$)/i,
|
|
11
|
+
/<\/(?:script|pre|style)>/i
|
|
12
|
+
],
|
|
13
|
+
[
|
|
14
|
+
/^<!--/,
|
|
15
|
+
/-->/
|
|
16
|
+
],
|
|
17
|
+
[
|
|
18
|
+
/^<\?/,
|
|
19
|
+
/\?>/
|
|
20
|
+
],
|
|
21
|
+
[
|
|
22
|
+
/^<![A-Z]/,
|
|
23
|
+
/>/
|
|
24
|
+
],
|
|
25
|
+
[
|
|
26
|
+
/^<!\[CDATA\[/,
|
|
27
|
+
/\]\]>/
|
|
28
|
+
]
|
|
29
|
+
];
|
|
30
|
+
const HTML_BLOCK_TAG = /^<\/?(?:address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul)(?:[ \t\v\f>]|\/>|$)/i;
|
|
31
|
+
const HTML_TAG_LINE = /^(?:<[A-Za-z][A-Za-z0-9-]*(?:[ \t\v\f]+[A-Za-z_:][A-Za-z0-9_.:-]*(?:[ \t\v\f]*=[ \t\v\f]*(?:[^ \t\v\f\r\n"'=<>`]+|'[^']*'|"[^"]*"))?)*[ \t\v\f]*\/?>|<\/[A-Za-z][A-Za-z0-9-]*[ \t\v\f]*>)[ \t\v\f]*$/;
|
|
32
|
+
const ORDERED = /^(\d{1,9})[.)]/;
|
|
33
|
+
const LABEL_LENGTH = 999;
|
|
34
|
+
const PUNCTUATION = /^[!-/:-@[-`{-~]$/;
|
|
35
|
+
const INLINE_RAW = new RegExp([
|
|
36
|
+
'<[A-Za-z][A-Za-z0-9-]*(?:[ \\t\\n\\v\\f\\r]+[A-Za-z_:][A-Za-z0-9_.:-]*(?:[ \\t\\n\\v\\f\\r]*=[ \\t\\n\\v\\f\\r]*(?:[^ \\t\\n\\v\\f\\r"\'=<>`]+|\'[^\']*\'|"[^"]*"))?)*[ \\t\\n\\v\\f\\r]*\\/?>',
|
|
37
|
+
'<\\/[A-Za-z][A-Za-z0-9-]*[ \\t\\n\\v\\f\\r]*>',
|
|
38
|
+
'<!--(?!-?>)(?:-?[^-])*-->',
|
|
39
|
+
'<\\?[\\s\\S]*?\\?>',
|
|
40
|
+
'<![A-Z]+[ \\t\\n\\v\\f\\r]+[^>]*>',
|
|
41
|
+
'<!\\[CDATA\\[[\\s\\S]*?\\]\\]>',
|
|
42
|
+
'<[A-Za-z][A-Za-z0-9+.-]{1,31}:[^\\x00-\\x20<>]*>',
|
|
43
|
+
"<[A-Za-z0-9.!#$%&'*+/=?^_`{|}~-]+@[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*>"
|
|
44
|
+
].join('|'), 'y');
|
|
45
|
+
const DELIMITER_ROW = /^\|?[ \t]*:?-+:?[ \t]*(?:\|[ \t]*:?-+:?[ \t]*)*\|?[ \t]*$/;
|
|
46
|
+
const GAP = /^[ \t\v\f]+/;
|
|
9
47
|
function endOfLine(text, from) {
|
|
10
48
|
const nl = text.indexOf('\n', from);
|
|
11
49
|
return nl === -1 ? text.length : nl;
|
|
@@ -123,159 +161,435 @@ function fenceSpan(text, line, open) {
|
|
|
123
161
|
}
|
|
124
162
|
return text.length;
|
|
125
163
|
}
|
|
126
|
-
function skipInlineCode(text, index) {
|
|
164
|
+
function skipInlineCode(text, index, joins) {
|
|
127
165
|
let ticks = 0;
|
|
128
166
|
while(text[index + ticks] === '`')ticks += 1;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
167
|
+
let i = index + ticks;
|
|
168
|
+
while(i < text.length){
|
|
169
|
+
const ch = text[i];
|
|
170
|
+
if (ch === '`') {
|
|
171
|
+
let run = 0;
|
|
172
|
+
while(text[i + run] === '`')run += 1;
|
|
173
|
+
if (run === ticks) return i + run;
|
|
174
|
+
i += run;
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (ch === '\r' && text[i + 1] !== '\n') break;
|
|
178
|
+
if (ch === '\n' && !joins.has(i)) break;
|
|
179
|
+
i += 1;
|
|
180
|
+
}
|
|
181
|
+
return index + ticks;
|
|
132
182
|
}
|
|
133
183
|
function skipLinkSpace(text, start) {
|
|
134
184
|
let i = start;
|
|
135
185
|
while(text[i] === ' ' || text[i] === '\t')i += 1;
|
|
136
186
|
return i;
|
|
137
187
|
}
|
|
138
|
-
function
|
|
139
|
-
|
|
140
|
-
if (
|
|
188
|
+
function skipInlineSpace(text, start, joins) {
|
|
189
|
+
let i = skipLinkSpace(text, start);
|
|
190
|
+
if (text[i] === '\r' && text[i + 1] === '\n') i += 1;
|
|
191
|
+
if (text[i] !== '\n' || !joins.has(i)) return i;
|
|
192
|
+
return afterIndent(text, readLine(text, i + 1, endOfLine(text, i + 1)));
|
|
193
|
+
}
|
|
194
|
+
function closeEnd(text, start, open, joins) {
|
|
141
195
|
const close = open === '(' ? ')' : open;
|
|
142
|
-
let i = start
|
|
196
|
+
let i = start;
|
|
143
197
|
while(i < text.length){
|
|
144
198
|
const ch = text[i];
|
|
199
|
+
if (escapes(text, i)) {
|
|
200
|
+
i += 2;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
145
203
|
if (ch === close) return i + 1;
|
|
146
|
-
if (
|
|
204
|
+
if (open === '(' && ch === '(') return null;
|
|
205
|
+
if (ch === '\r' && text[i + 1] !== '\n') return null;
|
|
206
|
+
if (ch === '\n' && !joins.has(i)) return null;
|
|
147
207
|
i += 1;
|
|
148
208
|
}
|
|
149
209
|
return null;
|
|
150
210
|
}
|
|
151
|
-
function
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
211
|
+
function skipTitle(text, start, joins) {
|
|
212
|
+
const open = text[start];
|
|
213
|
+
if (open !== '"' && open !== "'" && open !== '(') return start;
|
|
214
|
+
return closeEnd(text, start + 1, open, joins);
|
|
215
|
+
}
|
|
216
|
+
function destinationAt(text, at, end) {
|
|
217
|
+
if (text[at] === '<') {
|
|
218
|
+
let close = at + 1;
|
|
219
|
+
while(close < end && text[close] !== '>'){
|
|
220
|
+
if (text[close] === '<') return null;
|
|
221
|
+
close += escapes(text, close) ? 2 : 1;
|
|
222
|
+
}
|
|
223
|
+
if (close >= end) return null;
|
|
224
|
+
return {
|
|
225
|
+
href: text.slice(at + 1, close),
|
|
226
|
+
start: at,
|
|
160
227
|
end: close + 1,
|
|
161
228
|
bracketed: true
|
|
162
229
|
};
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if (ch === '(') depth += 1;
|
|
171
|
-
else if (ch === ')') {
|
|
172
|
-
if (depth === 0) break;
|
|
173
|
-
depth -= 1;
|
|
174
|
-
}
|
|
175
|
-
i += 1;
|
|
230
|
+
}
|
|
231
|
+
let i = at;
|
|
232
|
+
let depth = 0;
|
|
233
|
+
while(i < end){
|
|
234
|
+
if (escapes(text, i)) {
|
|
235
|
+
i += 2;
|
|
236
|
+
continue;
|
|
176
237
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
238
|
+
const ch = text[i];
|
|
239
|
+
if (ch <= ' ' || ch === '\x7f') break;
|
|
240
|
+
if (ch === '(') depth += 1;
|
|
241
|
+
else if (ch === ')') {
|
|
242
|
+
if (depth === 0) break;
|
|
243
|
+
depth -= 1;
|
|
244
|
+
}
|
|
245
|
+
i += 1;
|
|
183
246
|
}
|
|
184
|
-
|
|
185
|
-
|
|
247
|
+
if (depth !== 0) return null;
|
|
248
|
+
return {
|
|
249
|
+
href: text.slice(at, i),
|
|
250
|
+
start: at,
|
|
251
|
+
end: i,
|
|
252
|
+
bracketed: false
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
function destination(text, start, joins) {
|
|
256
|
+
let i = skipInlineSpace(text, start + 1, joins);
|
|
257
|
+
const occurrence = destinationAt(text, i, endOfLine(text, i));
|
|
258
|
+
if (occurrence === null) return null;
|
|
259
|
+
i = occurrence.end;
|
|
260
|
+
const spaced = skipInlineSpace(text, i, joins);
|
|
261
|
+
if (spaced === i && text[i] !== ')') return null;
|
|
262
|
+
i = spaced;
|
|
263
|
+
const afterTitle = skipTitle(text, i, joins);
|
|
186
264
|
if (afterTitle === null) return null;
|
|
187
|
-
i =
|
|
265
|
+
i = skipInlineSpace(text, afterTitle, joins);
|
|
188
266
|
if (text[i] !== ')') return null;
|
|
189
267
|
return {
|
|
190
268
|
occurrence,
|
|
191
269
|
end: i + 1
|
|
192
270
|
};
|
|
193
271
|
}
|
|
194
|
-
function
|
|
272
|
+
function cellCount(row) {
|
|
273
|
+
return row.trim().replace(/^\|/, '').replace(/(?<!\\)\|$/, '').split(/(?<!\\)\|/).length;
|
|
274
|
+
}
|
|
275
|
+
function headsTable(text, header, end) {
|
|
276
|
+
if (end >= text.length) return false;
|
|
277
|
+
const row = lineText(text, readLine(text, end + 1, endOfLine(text, end + 1)));
|
|
278
|
+
return row.includes('|') && DELIMITER_ROW.test(row) && cellCount(row) === cellCount(header);
|
|
279
|
+
}
|
|
280
|
+
function blockText(text, line) {
|
|
281
|
+
if (!line.item) return lineText(text, line);
|
|
282
|
+
const raw = text.slice(afterIndent(text, line), line.end);
|
|
283
|
+
return raw.endsWith('\r') ? raw.slice(0, -1) : raw;
|
|
284
|
+
}
|
|
285
|
+
function interruptsWithHtml(head) {
|
|
286
|
+
return HTML_CLOSED.some(([open])=>open.test(head)) || HTML_BLOCK_TAG.test(head);
|
|
287
|
+
}
|
|
288
|
+
function markerColumn(text, line) {
|
|
289
|
+
return widthOf(text, line.inner, afterIndent(text, line));
|
|
290
|
+
}
|
|
291
|
+
function markerEnd(text, at, end) {
|
|
292
|
+
const after = itemMarker(text, at, end);
|
|
293
|
+
if (after === null) return null;
|
|
294
|
+
return after > at && (text[after - 1] === ' ' || text[after - 1] === '\t') ? after - 1 : after;
|
|
295
|
+
}
|
|
296
|
+
function itemContent(text, line) {
|
|
297
|
+
let marker = markerEnd(text, afterIndent(text, line), line.end);
|
|
298
|
+
for(;;){
|
|
299
|
+
let next = marker + 1;
|
|
300
|
+
let spaces = 0;
|
|
301
|
+
while(spaces < 3 && text[next] === ' '){
|
|
302
|
+
next += 1;
|
|
303
|
+
spaces += 1;
|
|
304
|
+
}
|
|
305
|
+
const nested = next < line.end ? markerEnd(text, next, line.end) : null;
|
|
306
|
+
if (nested === null) break;
|
|
307
|
+
marker = nested;
|
|
308
|
+
}
|
|
309
|
+
const at = skipLinkSpace(text, marker);
|
|
310
|
+
const rest = text.slice(at, line.end).trim();
|
|
311
|
+
const column = widthOf(text, line.inner, at);
|
|
312
|
+
const start = widthOf(text, line.inner, marker);
|
|
313
|
+
if (rest !== '' && column - start <= CODE_INDENT) return {
|
|
314
|
+
column,
|
|
315
|
+
code: null
|
|
316
|
+
};
|
|
317
|
+
return {
|
|
318
|
+
column: start + 1,
|
|
319
|
+
code: rest === '' ? null : marker + 1
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
function joinsParagraph(text, line, depth, item) {
|
|
323
|
+
if (line.depth > depth) return false;
|
|
324
|
+
const head = blockText(text, line);
|
|
325
|
+
if (head.trim() === '' || endsBlock(head) || interruptsWithHtml(head)) return false;
|
|
326
|
+
if (fenceAt(text, line) !== null) return false;
|
|
327
|
+
if (!line.item) return true;
|
|
328
|
+
if (item !== undefined && markerColumn(text, line) < item) return false;
|
|
329
|
+
if (lineText(text, line).trim() === '') return true;
|
|
330
|
+
const ordered = ORDERED.exec(head);
|
|
331
|
+
return ordered !== null && Number(ordered[1]) !== 1;
|
|
332
|
+
}
|
|
333
|
+
function startsParagraph(text, line) {
|
|
334
|
+
return lineText(text, line).trim() !== '' && !endsBlock(blockText(text, line));
|
|
335
|
+
}
|
|
336
|
+
function lineColumn(text, line) {
|
|
337
|
+
return line.item ? markerColumn(text, line) : indentWidth(text, line);
|
|
338
|
+
}
|
|
339
|
+
function endsTable(text, line, depth) {
|
|
340
|
+
const head = blockText(text, line);
|
|
341
|
+
return head.trim() === '' || line.depth !== depth || line.item || endsBlock(head) || interruptsWithHtml(head) || fenceAt(text, line) !== null;
|
|
342
|
+
}
|
|
343
|
+
function quoteInner(text, start, end, depth) {
|
|
195
344
|
let i = start;
|
|
196
|
-
let
|
|
197
|
-
|
|
345
|
+
for(let level = 0; level < depth; level += 1){
|
|
346
|
+
let spaces = 0;
|
|
347
|
+
while(spaces < 3 && i < end && text[i] === ' '){
|
|
348
|
+
i += 1;
|
|
349
|
+
spaces += 1;
|
|
350
|
+
}
|
|
351
|
+
if (text[i] !== '>') return null;
|
|
352
|
+
i += 1;
|
|
353
|
+
if (text[i] === ' ') i += 1;
|
|
354
|
+
}
|
|
355
|
+
return i;
|
|
356
|
+
}
|
|
357
|
+
function htmlEnd(text, line, item) {
|
|
358
|
+
const head = lineText(text, line);
|
|
359
|
+
const closed = HTML_CLOSED.find(([open])=>open.test(head));
|
|
360
|
+
if (closed === undefined && !HTML_BLOCK_TAG.test(head) && !HTML_TAG_LINE.test(head)) return null;
|
|
361
|
+
if (closed !== undefined && closed[1].test(head)) return afterLine(text, line.end);
|
|
362
|
+
let cursor = afterLine(text, line.end);
|
|
363
|
+
while(cursor < text.length){
|
|
364
|
+
const end = endOfLine(text, cursor);
|
|
365
|
+
const inner = quoteInner(text, cursor, end, line.depth);
|
|
366
|
+
if (inner === null) return cursor;
|
|
367
|
+
const at = skipLinkSpace(text, inner);
|
|
368
|
+
const body = text.slice(inner, end);
|
|
369
|
+
const blank = BLANK.test(text.slice(at, end));
|
|
370
|
+
if (item !== undefined && !blank && widthOf(text, inner, at) < item) return cursor;
|
|
371
|
+
if (closed === undefined && blank) return cursor;
|
|
372
|
+
if (closed !== undefined && closed[1].test(body)) return afterLine(text, end);
|
|
373
|
+
cursor = afterLine(text, end);
|
|
374
|
+
if (cursor === end) break;
|
|
375
|
+
}
|
|
376
|
+
return text.length;
|
|
377
|
+
}
|
|
378
|
+
function codeEnd(text, line, base) {
|
|
379
|
+
let stop = afterLine(text, line.end);
|
|
380
|
+
let cursor = stop;
|
|
381
|
+
while(cursor < text.length){
|
|
382
|
+
const end = endOfLine(text, cursor);
|
|
383
|
+
const current = readLine(text, cursor, end);
|
|
384
|
+
if (current.depth !== line.depth) break;
|
|
385
|
+
const blank = lineText(text, current).trim() === '' && !current.item;
|
|
386
|
+
if (!blank && (current.item || indentWidth(text, current) < base + CODE_INDENT)) break;
|
|
387
|
+
cursor = afterLine(text, end);
|
|
388
|
+
if (!blank) stop = cursor;
|
|
389
|
+
if (cursor === end) break;
|
|
390
|
+
}
|
|
391
|
+
return stop;
|
|
392
|
+
}
|
|
393
|
+
function structure(text) {
|
|
394
|
+
const joins = new Set();
|
|
395
|
+
const raw = [];
|
|
396
|
+
const items = [];
|
|
397
|
+
let paragraph = null;
|
|
398
|
+
let table = null;
|
|
399
|
+
let depth = 0;
|
|
400
|
+
let cursor = 0;
|
|
401
|
+
while(cursor < text.length){
|
|
402
|
+
const end = endOfLine(text, cursor);
|
|
403
|
+
const line = readLine(text, cursor, end);
|
|
404
|
+
const content = lineText(text, line);
|
|
405
|
+
const blank = content.trim() === '' && !line.item;
|
|
406
|
+
if (table !== null && endsTable(text, line, table)) table = null;
|
|
407
|
+
if (table !== null) {
|
|
408
|
+
cursor = afterLine(text, end);
|
|
409
|
+
if (cursor === end) break;
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
if (paragraph !== null && joinsParagraph(text, line, paragraph, items.at(-1))) {
|
|
413
|
+
if (headsTable(text, content, end)) {
|
|
414
|
+
table = line.depth;
|
|
415
|
+
paragraph = null;
|
|
416
|
+
} else joins.add(cursor - 1);
|
|
417
|
+
cursor = afterLine(text, end);
|
|
418
|
+
if (cursor === end) break;
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
paragraph = null;
|
|
422
|
+
if (line.depth !== depth) items.length = 0;
|
|
423
|
+
depth = line.depth;
|
|
424
|
+
if (!blank) {
|
|
425
|
+
while(items.length > 0 && items.at(-1) > lineColumn(text, line))items.pop();
|
|
426
|
+
}
|
|
427
|
+
if (line.item && !BREAK.test(blockText(text, line))) {
|
|
428
|
+
const opened = itemContent(text, line);
|
|
429
|
+
items.push(opened.column);
|
|
430
|
+
if (opened.code !== null) {
|
|
431
|
+
const stop = codeEnd(text, line, opened.column);
|
|
432
|
+
raw.push([
|
|
433
|
+
opened.code,
|
|
434
|
+
stop
|
|
435
|
+
]);
|
|
436
|
+
cursor = stop;
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
const open = fenceAt(text, line);
|
|
441
|
+
if (open !== null) {
|
|
442
|
+
const stop = fenceSpan(text, line, open);
|
|
443
|
+
raw.push([
|
|
444
|
+
cursor,
|
|
445
|
+
stop
|
|
446
|
+
]);
|
|
447
|
+
cursor = stop;
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
const base = items.at(-1) ?? 0;
|
|
451
|
+
if (!blank && !line.item && indentWidth(text, line) >= base + CODE_INDENT) {
|
|
452
|
+
const stop = codeEnd(text, line, base);
|
|
453
|
+
raw.push([
|
|
454
|
+
cursor,
|
|
455
|
+
stop
|
|
456
|
+
]);
|
|
457
|
+
cursor = stop;
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
const html = htmlEnd(text, line, items.at(-1));
|
|
461
|
+
if (html !== null) {
|
|
462
|
+
raw.push([
|
|
463
|
+
cursor,
|
|
464
|
+
html
|
|
465
|
+
]);
|
|
466
|
+
cursor = html;
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
if (startsParagraph(text, line)) {
|
|
470
|
+
if (headsTable(text, content, end)) table = line.depth;
|
|
471
|
+
else paragraph = line.depth;
|
|
472
|
+
}
|
|
473
|
+
cursor = afterLine(text, end);
|
|
474
|
+
if (cursor === end) break;
|
|
475
|
+
}
|
|
476
|
+
return {
|
|
477
|
+
joins,
|
|
478
|
+
raw
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
function escapes(text, index) {
|
|
482
|
+
return text[index] === '\\' && PUNCTUATION.test(text[index + 1] ?? '');
|
|
483
|
+
}
|
|
484
|
+
function referenceEnd(text, start, joins) {
|
|
485
|
+
let i = start;
|
|
486
|
+
while(i < text.length){
|
|
198
487
|
const ch = text[i];
|
|
199
|
-
if (
|
|
488
|
+
if (escapes(text, i)) {
|
|
200
489
|
i += 2;
|
|
201
490
|
continue;
|
|
202
491
|
}
|
|
203
|
-
if (ch === '
|
|
204
|
-
|
|
205
|
-
|
|
492
|
+
if (ch === ']') return i + 1;
|
|
493
|
+
if (ch === '[') return -1;
|
|
494
|
+
if (ch === '\r' && text[i + 1] !== '\n') return -1;
|
|
495
|
+
if (ch === '\n' && !joins.has(i)) return -1;
|
|
206
496
|
i += 1;
|
|
207
497
|
}
|
|
208
|
-
return
|
|
209
|
-
}
|
|
210
|
-
function
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
498
|
+
return -1;
|
|
499
|
+
}
|
|
500
|
+
function labelText(text, from, to) {
|
|
501
|
+
let label = '';
|
|
502
|
+
let cursor = from;
|
|
503
|
+
let newline = text.indexOf('\n', cursor);
|
|
504
|
+
while(newline !== -1 && newline < to){
|
|
505
|
+
label += `${text.slice(cursor, newline)}\n`;
|
|
506
|
+
cursor = afterIndent(text, readLine(text, newline + 1, endOfLine(text, newline + 1)));
|
|
507
|
+
newline = text.indexOf('\n', cursor);
|
|
508
|
+
}
|
|
509
|
+
return label + text.slice(cursor, to);
|
|
510
|
+
}
|
|
511
|
+
function labelKey(label) {
|
|
512
|
+
return label.replace(/[ \t\n\v\f\r]+/g, ' ').replace(/^ | $/g, '').toLowerCase().split('\u0131').map((part)=>part.toUpperCase()).join('\u0131');
|
|
513
|
+
}
|
|
514
|
+
function labelKeyAt(text, from, to) {
|
|
515
|
+
const label = labelText(text, from, to);
|
|
516
|
+
if (label.length > LABEL_LENGTH && [
|
|
517
|
+
...label
|
|
518
|
+
].length > LABEL_LENGTH) return '';
|
|
519
|
+
return labelKey(label);
|
|
520
|
+
}
|
|
521
|
+
function closeLink(text, opener, close, defs, joins) {
|
|
522
|
+
const end = close + 1;
|
|
523
|
+
const use = (destination, to)=>({
|
|
524
|
+
start: opener.start,
|
|
525
|
+
end: to,
|
|
526
|
+
labelStart: opener.at + 1,
|
|
527
|
+
labelEnd: close,
|
|
528
|
+
destination
|
|
224
529
|
});
|
|
225
|
-
|
|
530
|
+
if (text[end] === '(') {
|
|
531
|
+
const dest = destination(text, end, joins);
|
|
532
|
+
if (dest !== null) return use(dest.occurrence, dest.end);
|
|
226
533
|
}
|
|
227
|
-
if (text[
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
start,
|
|
235
|
-
end: close,
|
|
236
|
-
labelStart: index + 1,
|
|
237
|
-
labelEnd: end - 1,
|
|
238
|
-
destination: defined
|
|
239
|
-
});
|
|
534
|
+
if (text[end] === '[') {
|
|
535
|
+
const second = referenceEnd(text, end + 1, joins);
|
|
536
|
+
const collapsed = second === end + 2;
|
|
537
|
+
const named = collapsed ? opener.bracketAfter ? '' : labelKeyAt(text, opener.at + 1, close) : labelKeyAt(text, end + 1, second - 1);
|
|
538
|
+
if (second !== -1 && (named !== '' || collapsed)) {
|
|
539
|
+
const defined = defs.get(named);
|
|
540
|
+
return defined === undefined ? null : use(defined, second);
|
|
240
541
|
}
|
|
241
|
-
return close;
|
|
242
542
|
}
|
|
243
|
-
return
|
|
543
|
+
if (opener.bracketAfter) return null;
|
|
544
|
+
const shortcut = defs.get(labelKeyAt(text, opener.at + 1, close));
|
|
545
|
+
return shortcut === undefined ? null : use(shortcut, end);
|
|
244
546
|
}
|
|
245
547
|
function definitionDestination(text, from, to) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
const
|
|
253
|
-
if (
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
start: i + span[0],
|
|
257
|
-
end: i + span[1],
|
|
258
|
-
bracketed: bracketed !== undefined
|
|
259
|
-
};
|
|
548
|
+
const occurrence = destinationAt(text, skipLinkSpace(text, from), to);
|
|
549
|
+
return occurrence === null || occurrence.href === '' && !occurrence.bracketed ? null : occurrence;
|
|
550
|
+
}
|
|
551
|
+
function titleEnd(text, at, joins) {
|
|
552
|
+
const open = text[at];
|
|
553
|
+
if (open !== '"' && open !== "'" && open !== '(') return null;
|
|
554
|
+
const closed = closeEnd(text, at + 1, open, joins);
|
|
555
|
+
if (closed === null) return null;
|
|
556
|
+
const end = endOfLine(text, closed);
|
|
557
|
+
return BLANK.test(text.slice(closed, end)) ? afterLine(text, end) : null;
|
|
260
558
|
}
|
|
261
|
-
function definitionAt(text, line) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
let
|
|
559
|
+
function definitionAt(text, line, joins) {
|
|
560
|
+
if (text[line.content] !== '[') return null;
|
|
561
|
+
const close = referenceEnd(text, line.content + 1, joins);
|
|
562
|
+
if (close === -1 || text[close] !== ':') return null;
|
|
563
|
+
const id = labelKeyAt(text, line.content + 1, close - 1);
|
|
564
|
+
if (id === '') return null;
|
|
565
|
+
const from = close + 1;
|
|
566
|
+
let end = endOfLine(text, from);
|
|
567
|
+
let occurrence = definitionDestination(text, from, end);
|
|
269
568
|
if (occurrence === null) {
|
|
270
|
-
if (text.slice(from,
|
|
271
|
-
const
|
|
272
|
-
|
|
569
|
+
if (text.slice(from, end).trim() !== '' || !joins.has(end)) return null;
|
|
570
|
+
const start = end + 1;
|
|
571
|
+
end = endOfLine(text, start);
|
|
572
|
+
occurrence = definitionDestination(text, afterIndent(text, readLine(text, start, end)), end);
|
|
273
573
|
if (occurrence === null) return null;
|
|
274
|
-
next = afterLine(text, end);
|
|
275
574
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
575
|
+
const rest = text.slice(occurrence.end, end);
|
|
576
|
+
if (!BLANK.test(rest)) {
|
|
577
|
+
const gap = GAP.exec(rest);
|
|
578
|
+
const titled = gap === null ? null : titleEnd(text, occurrence.end + gap[0].length, joins);
|
|
579
|
+
return titled === null ? null : {
|
|
580
|
+
id,
|
|
581
|
+
occurrence,
|
|
582
|
+
next: titled
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
const next = afterLine(text, end);
|
|
586
|
+
if (joins.has(end)) {
|
|
587
|
+
const titled = titleEnd(text, afterIndent(text, readLine(text, next, endOfLine(text, next))), joins);
|
|
588
|
+
if (titled !== null) return {
|
|
589
|
+
id,
|
|
590
|
+
occurrence,
|
|
591
|
+
next: titled
|
|
592
|
+
};
|
|
279
593
|
}
|
|
280
594
|
return {
|
|
281
595
|
id,
|
|
@@ -287,34 +601,19 @@ function endsBlock(content) {
|
|
|
287
601
|
return HEADING.test(content) || BREAK.test(content) || SETEXT.test(content);
|
|
288
602
|
}
|
|
289
603
|
function scanBlocks(text) {
|
|
290
|
-
const
|
|
604
|
+
const { joins, raw } = structure(text);
|
|
291
605
|
const definitions = new Map();
|
|
292
606
|
const blocks = [];
|
|
293
|
-
let
|
|
294
|
-
let depth = 0;
|
|
607
|
+
let fresh = 0;
|
|
295
608
|
let i = 0;
|
|
296
609
|
while(i < text.length){
|
|
297
|
-
const
|
|
298
|
-
if (
|
|
299
|
-
|
|
300
|
-
const open = fenceAt(text, line);
|
|
301
|
-
if (open !== null) {
|
|
302
|
-
const stop = fenceSpan(text, line, open);
|
|
303
|
-
fences.push([
|
|
304
|
-
i,
|
|
305
|
-
stop
|
|
306
|
-
]);
|
|
307
|
-
paragraph = false;
|
|
308
|
-
i = stop;
|
|
309
|
-
continue;
|
|
310
|
-
}
|
|
311
|
-
const content = lineText(text, line);
|
|
312
|
-
if (content.trim() === '') {
|
|
313
|
-
paragraph = false;
|
|
314
|
-
i = afterLine(text, line.end);
|
|
610
|
+
const skip = spanEndAt(raw, i);
|
|
611
|
+
if (skip !== undefined) {
|
|
612
|
+
i = skip;
|
|
315
613
|
continue;
|
|
316
614
|
}
|
|
317
|
-
const
|
|
615
|
+
const line = readLine(text, i, endOfLine(text, i));
|
|
616
|
+
const definition = lineText(text, line).trim() === '' || i !== fresh && joins.has(i - 1) ? null : definitionAt(text, line, joins);
|
|
318
617
|
if (definition !== null) {
|
|
319
618
|
if (!definitions.has(definition.id)) definitions.set(definition.id, definition.occurrence);
|
|
320
619
|
blocks.push({
|
|
@@ -323,46 +622,128 @@ function scanBlocks(text) {
|
|
|
323
622
|
destination: definition.occurrence
|
|
324
623
|
});
|
|
325
624
|
i = definition.next;
|
|
625
|
+
fresh = i;
|
|
326
626
|
continue;
|
|
327
627
|
}
|
|
328
|
-
paragraph = !endsBlock(content);
|
|
329
628
|
i = afterLine(text, line.end);
|
|
330
629
|
}
|
|
331
630
|
return {
|
|
332
|
-
|
|
631
|
+
raw,
|
|
333
632
|
definitions,
|
|
334
|
-
blocks
|
|
633
|
+
blocks,
|
|
634
|
+
joins
|
|
335
635
|
};
|
|
336
636
|
}
|
|
337
|
-
function
|
|
637
|
+
function spanEndAt(spans, index) {
|
|
338
638
|
for (const [start, end] of spans){
|
|
339
639
|
if (index >= start && index < end) return end;
|
|
340
640
|
}
|
|
341
641
|
return undefined;
|
|
342
642
|
}
|
|
343
643
|
export function fenceSpans(text) {
|
|
344
|
-
|
|
644
|
+
const fences = [];
|
|
645
|
+
let i = 0;
|
|
646
|
+
while(i < text.length){
|
|
647
|
+
const line = readLine(text, i, endOfLine(text, i));
|
|
648
|
+
const open = fenceAt(text, line);
|
|
649
|
+
if (open === null) {
|
|
650
|
+
i = afterLine(text, line.end);
|
|
651
|
+
continue;
|
|
652
|
+
}
|
|
653
|
+
const stop = fenceSpan(text, line, open);
|
|
654
|
+
fences.push([
|
|
655
|
+
i,
|
|
656
|
+
stop
|
|
657
|
+
]);
|
|
658
|
+
i = stop;
|
|
659
|
+
}
|
|
660
|
+
return fences;
|
|
661
|
+
}
|
|
662
|
+
function paragraphEnd(text, from, joins) {
|
|
663
|
+
let i = from;
|
|
664
|
+
while(i < text.length){
|
|
665
|
+
if (text[i] === '\n' && !joins.has(i)) return i;
|
|
666
|
+
if (text[i] === '\r' && text[i + 1] !== '\n') return i;
|
|
667
|
+
i += 1;
|
|
668
|
+
}
|
|
669
|
+
return i;
|
|
345
670
|
}
|
|
346
671
|
export function linkInventory(body) {
|
|
347
|
-
const {
|
|
672
|
+
const { raw, definitions, blocks, joins } = scanBlocks(body);
|
|
673
|
+
const skipped = [
|
|
674
|
+
...raw,
|
|
675
|
+
...blocks.map((block)=>[
|
|
676
|
+
block.start,
|
|
677
|
+
block.end
|
|
678
|
+
])
|
|
679
|
+
];
|
|
348
680
|
const uses = [];
|
|
681
|
+
const openers = [];
|
|
682
|
+
let paragraph = {
|
|
683
|
+
start: 0,
|
|
684
|
+
end: -1,
|
|
685
|
+
text: ''
|
|
686
|
+
};
|
|
349
687
|
let i = 0;
|
|
350
688
|
while(i < body.length){
|
|
351
|
-
const
|
|
352
|
-
if (
|
|
353
|
-
|
|
689
|
+
const skipEnd = spanEndAt(skipped, i);
|
|
690
|
+
if (skipEnd !== undefined) {
|
|
691
|
+
openers.length = 0;
|
|
692
|
+
i = skipEnd;
|
|
354
693
|
continue;
|
|
355
694
|
}
|
|
356
|
-
|
|
357
|
-
|
|
695
|
+
const ch = body[i];
|
|
696
|
+
if (escapes(body, i)) {
|
|
697
|
+
i += 2;
|
|
358
698
|
continue;
|
|
359
699
|
}
|
|
360
|
-
if (
|
|
361
|
-
i =
|
|
700
|
+
if (ch === '`') {
|
|
701
|
+
i = skipInlineCode(body, i, joins);
|
|
702
|
+
continue;
|
|
703
|
+
}
|
|
704
|
+
if (ch === '<') {
|
|
705
|
+
if (i >= paragraph.end) {
|
|
706
|
+
const end = paragraphEnd(body, i, joins);
|
|
707
|
+
paragraph = {
|
|
708
|
+
start: i,
|
|
709
|
+
end,
|
|
710
|
+
text: body.slice(i, end)
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
INLINE_RAW.lastIndex = i - paragraph.start;
|
|
714
|
+
const raw = INLINE_RAW.exec(paragraph.text);
|
|
715
|
+
if (raw !== null) {
|
|
716
|
+
i += raw[0].length;
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
if (ch === '\n' && !joins.has(i) || ch === '\r' && body[i + 1] !== '\n') openers.length = 0;
|
|
721
|
+
if (ch === '[' || ch === '!' && body[i + 1] === '[') {
|
|
722
|
+
const previous = openers.at(-1);
|
|
723
|
+
if (previous !== undefined) previous.bracketAfter = true;
|
|
724
|
+
const image = ch === '!';
|
|
725
|
+
openers.push({
|
|
726
|
+
at: image ? i + 1 : i,
|
|
727
|
+
start: i,
|
|
728
|
+
image,
|
|
729
|
+
active: true,
|
|
730
|
+
bracketAfter: false
|
|
731
|
+
});
|
|
732
|
+
i += image ? 2 : 1;
|
|
362
733
|
continue;
|
|
363
734
|
}
|
|
364
|
-
if (
|
|
365
|
-
|
|
735
|
+
if (ch === ']') {
|
|
736
|
+
const opener = openers.pop();
|
|
737
|
+
const use = opener?.active ? closeLink(body, opener, i, definitions, joins) : null;
|
|
738
|
+
if (opener === undefined || use === null) {
|
|
739
|
+
i += 1;
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
uses.push(use);
|
|
743
|
+
if (!opener.image) {
|
|
744
|
+
for (const earlier of openers)if (!earlier.image) earlier.active = false;
|
|
745
|
+
}
|
|
746
|
+
i = use.end;
|
|
366
747
|
continue;
|
|
367
748
|
}
|
|
368
749
|
i += 1;
|