@astryxdesign/core 0.4.7-canary.e93acd6 → 0.4.7-canary.ecaf779

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.
@@ -6,5 +6,5 @@
6
6
  export { Markdown } from './Markdown';
7
7
  export type { MarkdownProps, MarkdownSource, MarkdownComponents, MarkdownInlinePlugin, } from './Markdown';
8
8
  export { parseMarkdown, parseMarkdownIncremental, createIncrementalState, parseInline, } from './parser';
9
- export type { BlockNode, InlineNode, SourceRange, ListItemNode, TableCellNode, TableAlignment, IncrementalState as IncrementalParseState, } from './parser';
9
+ export type { BlockNode, InlineNode, ListItemNode, TableCellNode, TableAlignment, IncrementalState as IncrementalParseState, } from './parser';
10
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Markdown/index.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AAEH,OAAO,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAC;AACpC,YAAY,EACV,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,aAAa,EACb,wBAAwB,EACxB,sBAAsB,EACtB,WAAW,GACZ,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,SAAS,EACT,UAAU,EACV,WAAW,EACX,YAAY,EACZ,aAAa,EACb,cAAc,EACd,gBAAgB,IAAI,qBAAqB,GAC1C,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Markdown/index.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AAEH,OAAO,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAC;AACpC,YAAY,EACV,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,aAAa,EACb,wBAAwB,EACxB,sBAAsB,EACtB,WAAW,GACZ,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,SAAS,EACT,UAAU,EACV,YAAY,EACZ,aAAa,EACb,cAAc,EACd,gBAAgB,IAAI,qBAAqB,GAC1C,MAAM,UAAU,CAAC"}
@@ -35,14 +35,7 @@ export type InlineNode = {
35
35
  } | {
36
36
  type: 'break';
37
37
  };
38
- export type BlockNode = BlockNodeKind & {
39
- /**
40
- * Where this block came from in the source, when parsed with the
41
- * `sourceRanges` option. Top-level blocks only.
42
- */
43
- range?: SourceRange;
44
- };
45
- type BlockNodeKind = {
38
+ export type BlockNode = {
46
39
  type: 'heading';
47
40
  level: 1 | 2 | 3 | 4 | 5 | 6;
48
41
  children: InlineNode[];
@@ -76,11 +69,6 @@ type BlockNodeKind = {
76
69
  src: string;
77
70
  alt: string;
78
71
  };
79
- /**
80
- * Character offsets `[start, end)` of a block in the source string handed to
81
- * `parseMarkdown`. `end` excludes the block's trailing blank lines.
82
- */
83
- export type SourceRange = readonly [start: number, end: number];
84
72
  export type ListItemNode = {
85
73
  checked?: boolean;
86
74
  children: BlockNode[];
@@ -112,16 +100,6 @@ export type ParseOptions = {
112
100
  * suffix is not rejected (Astryx accepts any plausible TLD shape).
113
101
  */
114
102
  autolink?: 'gfm';
115
- /**
116
- * When true, every top-level block carries a `range` — the offsets it
117
- * occupies in the string passed in. Lets a consumer that still holds the
118
- * source slice the original markdown for a block instead of reconstructing
119
- * it from the parsed node (or from the rendered DOM). Off by default: the
120
- * field is absent unless asked for, so nothing that compares nodes changes.
121
- *
122
- * Blocks nested inside a list item or a blockquote do not carry one.
123
- */
124
- sourceRanges?: boolean;
125
103
  };
126
104
  export declare function parseInline(text: string, sourceIds?: ReadonlySet<string>): InlineNode[];
127
105
  export declare function parseInline(text: string, options: ParseOptions): InlineNode[];
@@ -139,12 +117,6 @@ export interface IncrementalState {
139
117
  * with newly-arriving content.
140
118
  */
141
119
  autolink?: 'gfm';
142
- /**
143
- * The `sourceRanges` option the cached `settledBlocks` were parsed with.
144
- * Flipping it invalidates them the same way `autolink` does: they either
145
- * lack the ranges the caller now asks for, or carry ones it did not.
146
- */
147
- sourceRanges?: boolean;
148
120
  /**
149
121
  * Signature of the link reference definitions the cached `settledBlocks`
150
122
  * were parsed with. Definitions are document-global and typically arrive
@@ -171,5 +143,4 @@ export declare function slugify(value: string): string;
171
143
  * document shares a single numbering sequence.
172
144
  */
173
145
  export declare function uniqueSlug(baseSlug: string, counts: Map<string, number>): string;
174
- export {};
175
146
  //# sourceMappingURL=parser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/Markdown/parser.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/Markdown/parser.ts"],"names":[],"mappings":"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"}
@@ -13,11 +13,6 @@
13
13
  // Types
14
14
  // ---------------------------------------------------------------------------
15
15
 
16
- /**
17
- * Character offsets `[start, end)` of a block in the source string handed to
18
- * `parseMarkdown`. `end` excludes the block's trailing blank lines.
19
- */
20
-
21
16
  // ---------------------------------------------------------------------------
22
17
  // Parse options
23
18
  // ---------------------------------------------------------------------------
@@ -52,8 +47,7 @@ function resolveOptions(arg) {
52
47
  const opts = arg;
53
48
  return {
54
49
  sourceIds: opts.sourceIds,
55
- autolink: opts.autolink,
56
- sourceRanges: opts.sourceRanges
50
+ autolink: opts.autolink
57
51
  };
58
52
  }
59
53
 
@@ -174,17 +168,10 @@ function extractLinkDefinitions(input) {
174
168
  cleaned: input
175
169
  };
176
170
  }
177
- const lineMap = [];
178
- for (let index = 0; index < lines.length; index++) {
179
- if (keep[index]) {
180
- lineMap.push(index);
181
- }
182
- }
183
- const cleaned = lineMap.map(index => lines[index]).join('\n');
171
+ const cleaned = lines.filter((_, index) => keep[index]).join('\n');
184
172
  return {
185
173
  defs,
186
- cleaned,
187
- lineMap
174
+ cleaned
188
175
  };
189
176
  }
190
177
 
@@ -978,19 +965,6 @@ function isTableSeparator(line) {
978
965
  return nonEmpty.length > 0 && nonEmpty.every(cell => /^:?-+:?$/.test(cell));
979
966
  }
980
967
 
981
- /**
982
- * The same options for content parsed out of an enclosing block. Ranges are a
983
- * top-level contract: a list item's or a blockquote's children are parsed from
984
- * text the caller reassembled (markers and `>` prefixes stripped), so an
985
- * offset into it would not address the document.
986
- */
987
- function nested(opts) {
988
- return opts.sourceRanges ? {
989
- ...opts,
990
- sourceRanges: false
991
- } : opts;
992
- }
993
-
994
968
  /**
995
969
  * Returns true when a line could start a new block — used to stop paragraph
996
970
  * continuation. Every regex here uses bounded or single-class quantifiers
@@ -1121,7 +1095,7 @@ function parseList(lines, startIndex, ordered, opts) {
1121
1095
  }
1122
1096
  items.push({
1123
1097
  checked,
1124
- children: parseMarkdownImpl(itemText, nested(opts))
1098
+ children: parseMarkdownImpl(itemText, opts)
1125
1099
  });
1126
1100
 
1127
1101
  // CommonMark loose list: blank line(s) between items of the same style
@@ -1166,8 +1140,7 @@ function parseMarkdownImpl(input, baseOpts) {
1166
1140
  // parse.
1167
1141
  const {
1168
1142
  defs,
1169
- cleaned,
1170
- lineMap
1143
+ cleaned
1171
1144
  } = extractLinkDefinitions(input);
1172
1145
  const inherited = baseOpts.linkDefs;
1173
1146
  let linkDefs;
@@ -1184,22 +1157,8 @@ function parseMarkdownImpl(input, baseOpts) {
1184
1157
  } : baseOpts;
1185
1158
  const lines = cleaned.split('\n');
1186
1159
  const blocks = [];
1187
- // The line each block started on, parallel to `blocks`. Only collected when
1188
- // ranges were asked for; a block's end is resolved after the loop, since the
1189
- // branch that produced it has already moved `index` past whatever it read.
1190
- const blockStartLines = opts.sourceRanges ? [] : null;
1191
- // Set only by a block that consumes blank lines as content, where the
1192
- // positional end derivation would trim them away.
1193
- const blockEndLines = opts.sourceRanges ? [] : null;
1194
- let blockStartLine = 0;
1195
- const pushBlock = (node, endLine) => {
1196
- blocks.push(node);
1197
- blockStartLines?.push(blockStartLine);
1198
- blockEndLines?.push(endLine);
1199
- };
1200
1160
  let index = 0;
1201
1161
  while (index < lines.length) {
1202
- blockStartLine = index;
1203
1162
  const line = lines[index];
1204
1163
  if (line.trim() === '') {
1205
1164
  index++;
@@ -1218,21 +1177,18 @@ function parseMarkdownImpl(input, baseOpts) {
1218
1177
  index++;
1219
1178
  }
1220
1179
  index++; // skip closing fence
1221
- // A fence owns its blank lines, and an unterminated one (mid-stream)
1222
- // can end on them, so it states its own end rather than letting the
1223
- // positional derivation trim them off.
1224
- pushBlock({
1180
+ blocks.push({
1225
1181
  type: 'codeblock',
1226
1182
  language,
1227
1183
  content: codeLines.join('\n')
1228
- }, Math.min(index, lines.length) - 1);
1184
+ });
1229
1185
  continue;
1230
1186
  }
1231
1187
 
1232
1188
  // --- Heading ---
1233
1189
  const headingMatch = line.match(/^(#{1,6}) +(.*)/);
1234
1190
  if (headingMatch) {
1235
- pushBlock({
1191
+ blocks.push({
1236
1192
  type: 'heading',
1237
1193
  level: headingMatch[1].length,
1238
1194
  children: parseInlineEntry(headingMatch[2], opts)
@@ -1243,7 +1199,7 @@ function parseMarkdownImpl(input, baseOpts) {
1243
1199
 
1244
1200
  // --- HR (must precede list check to handle `- - -`, `* * *`, `_ _ _`) ---
1245
1201
  if (isHorizontalRule(line)) {
1246
- pushBlock({
1202
+ blocks.push({
1247
1203
  type: 'hr'
1248
1204
  });
1249
1205
  index++;
@@ -1253,7 +1209,7 @@ function parseMarkdownImpl(input, baseOpts) {
1253
1209
  // --- Standalone image ---
1254
1210
  const imageMatch = line.match(/^!\[([^\]]*)\]\(([^)]+)\)/);
1255
1211
  if (imageMatch && line.trim() === imageMatch[0]) {
1256
- pushBlock({
1212
+ blocks.push({
1257
1213
  type: 'image',
1258
1214
  alt: imageMatch[1],
1259
1215
  src: imageMatch[2]
@@ -1265,7 +1221,7 @@ function parseMarkdownImpl(input, baseOpts) {
1265
1221
  // --- Table (with or without leading pipe) ---
1266
1222
  if (index + 1 < lines.length && line.includes('|') && isTableSeparator(lines[index + 1])) {
1267
1223
  const tableResult = parseTable(lines, index, opts);
1268
- pushBlock(tableResult.node);
1224
+ blocks.push(tableResult.node);
1269
1225
  index = tableResult.nextIndex;
1270
1226
  continue;
1271
1227
  }
@@ -1277,9 +1233,9 @@ function parseMarkdownImpl(input, baseOpts) {
1277
1233
  quoteLines.push(lines[index].replace(/^> ?/, ''));
1278
1234
  index++;
1279
1235
  }
1280
- pushBlock({
1236
+ blocks.push({
1281
1237
  type: 'blockquote',
1282
- children: parseMarkdownImpl(quoteLines.join('\n'), nested(opts))
1238
+ children: parseMarkdownImpl(quoteLines.join('\n'), opts)
1283
1239
  });
1284
1240
  continue;
1285
1241
  }
@@ -1287,7 +1243,7 @@ function parseMarkdownImpl(input, baseOpts) {
1287
1243
  // --- Unordered list ---
1288
1244
  if (/^ {0,9}[-*+] /.test(line)) {
1289
1245
  const listResult = parseList(lines, index, false, opts);
1290
- pushBlock(listResult.node);
1246
+ blocks.push(listResult.node);
1291
1247
  index = listResult.nextIndex;
1292
1248
  continue;
1293
1249
  }
@@ -1295,7 +1251,7 @@ function parseMarkdownImpl(input, baseOpts) {
1295
1251
  // --- Ordered list ---
1296
1252
  if (/^ {0,9}\d+[.)] /.test(line)) {
1297
1253
  const listResult = parseList(lines, index, true, opts);
1298
- pushBlock(listResult.node);
1254
+ blocks.push(listResult.node);
1299
1255
  index = listResult.nextIndex;
1300
1256
  continue;
1301
1257
  }
@@ -1307,60 +1263,14 @@ function parseMarkdownImpl(input, baseOpts) {
1307
1263
  paraLines.push(lines[index]);
1308
1264
  index++;
1309
1265
  }
1310
- pushBlock({
1266
+ blocks.push({
1311
1267
  type: 'paragraph',
1312
1268
  children: parseInlineEntry(paraLines.join('\n'), opts)
1313
1269
  });
1314
1270
  }
1315
- if (blockStartLines != null) {
1316
- stampSourceRanges(blocks, blockStartLines, blockEndLines ?? [], lines, lineMap, input, opts);
1317
- }
1318
1271
  return blocks;
1319
1272
  }
1320
1273
 
1321
- /**
1322
- * Give each block the offsets it occupies in the original input.
1323
- *
1324
- * Blocks are contiguous and in source order, so a block runs from its own
1325
- * first line to the line before the next block starts, minus the blank lines
1326
- * between them. Offsets are computed against the *input*, not the text the
1327
- * block loop saw: link reference definitions are stripped before parsing, and
1328
- * `lineMap` says which input line each surviving line came from.
1329
- */
1330
- function stampSourceRanges(blocks, blockStartLines, blockEndLines, lines, lineMap, input, opts) {
1331
- const base = opts.baseOffset ?? 0;
1332
- // Offset of the first character of every line of the input.
1333
- const inputLineStarts = [0];
1334
- for (let i = 0; i < input.length; i++) {
1335
- if (input[i] === '\n') {
1336
- inputLineStarts.push(i + 1);
1337
- }
1338
- }
1339
- // Stripping removes whole lines and never edits one, so a parsed line's
1340
- // length is its input line's length.
1341
- const lineStart = line => base + inputLineStarts[lineMap != null ? lineMap[line] : line];
1342
- for (let i = 0; i < blocks.length; i++) {
1343
- const startLine = blockStartLines[i];
1344
- let endLine = blockEndLines[i];
1345
- if (endLine == null) {
1346
- const nextStart = i + 1 < blocks.length ? blockStartLines[i + 1] : lines.length;
1347
- endLine = nextStart - 1;
1348
- while (endLine > startLine && lines[endLine].trim() === '') {
1349
- endLine--;
1350
- }
1351
- }
1352
- // Exactly the block's own lines, verbatim — a CRLF document's trailing
1353
- // `\r` included, since the parser reads it as part of the line too and a
1354
- // range that dropped it would slice to something that re-parses
1355
- // differently.
1356
- const end = lineStart(endLine) + lines[endLine].length;
1357
- blocks[i] = {
1358
- ...blocks[i],
1359
- range: [lineStart(startLine), end]
1360
- };
1361
- }
1362
- }
1363
-
1364
1274
  // ---------------------------------------------------------------------------
1365
1275
  // Incremental parsing
1366
1276
  // ---------------------------------------------------------------------------
@@ -1622,18 +1532,6 @@ function trimUnsettledStructural(text) {
1622
1532
  return lines.join('\n');
1623
1533
  }
1624
1534
 
1625
- /**
1626
- * The same options, for parsing a slice that starts at `offset` of the
1627
- * document — so the slice's blocks report ranges into the whole document
1628
- * rather than into the slice.
1629
- */
1630
- function atOffset(opts, offset) {
1631
- return opts.sourceRanges ? {
1632
- ...opts,
1633
- baseOffset: offset
1634
- } : opts;
1635
- }
1636
-
1637
1535
  /**
1638
1536
  * Concatenate freshly-parsed delta blocks with previously-settled blocks,
1639
1537
  * merging adjacent same-style lists into a single loose list. The boundary
@@ -1654,12 +1552,7 @@ function mergeSettledBlocks(prev, delta) {
1654
1552
  start: prevLast.start,
1655
1553
  delimiter: prevLast.delimiter,
1656
1554
  loose: true,
1657
- items: [...prevLast.items, ...deltaFirst.items],
1658
- // One list now, so one range: from where the first half started to
1659
- // where the second half ended.
1660
- ...(prevLast.range != null && deltaFirst.range != null ? {
1661
- range: [prevLast.range[0], deltaFirst.range[1]]
1662
- } : null)
1555
+ items: [...prevLast.items, ...deltaFirst.items]
1663
1556
  };
1664
1557
  return [...prev.slice(0, -1), merged, ...delta.slice(1)];
1665
1558
  }
@@ -1667,16 +1560,15 @@ function mergeSettledBlocks(prev, delta) {
1667
1560
  }
1668
1561
  export function parseMarkdownIncremental(input, state, arg) {
1669
1562
  const opts = resolveOptions(arg);
1670
- // Invalidate cache when the autolink or sourceRanges option flips — cached
1671
- // settled blocks were parsed with the previous setting and would otherwise
1672
- // be reused unchanged.
1673
- if (state.autolink !== opts.autolink || Boolean(state.sourceRanges) !== Boolean(opts.sourceRanges)) {
1563
+ // Invalidate cache when the autolink option flips — cached settled blocks
1564
+ // were parsed with the previous setting and would otherwise be reused
1565
+ // unchanged.
1566
+ if (state.autolink !== opts.autolink) {
1674
1567
  state.prevInput = '';
1675
1568
  state.settledText = '';
1676
1569
  state.settledBlocks = [];
1677
1570
  state.settledUpTo = 0;
1678
1571
  state.autolink = opts.autolink;
1679
- state.sourceRanges = opts.sourceRanges;
1680
1572
  }
1681
1573
  if (input === '') {
1682
1574
  state.prevInput = '';
@@ -1723,20 +1615,13 @@ export function parseMarkdownIncremental(input, state, arg) {
1723
1615
  } else if (state.settledText.length > 0 && settledText.startsWith(state.settledText)) {
1724
1616
  // Settled portion grew — parse only the new delta
1725
1617
  const delta = settledText.slice(state.settledText.length);
1726
- const deltaBlocks = parseMarkdownImpl(delta, atOffset(parseOpts, state.settledText.length));
1618
+ const deltaBlocks = parseMarkdownImpl(delta, parseOpts);
1727
1619
  settledBlocks = mergeSettledBlocks(state.settledBlocks, deltaBlocks);
1728
1620
  } else {
1729
1621
  // Content before the boundary changed — full re-parse of settled portion
1730
1622
  settledBlocks = parseMarkdownImpl(settledText, parseOpts);
1731
1623
  }
1732
-
1733
- // The unsettled tail is trimmed before parsing, so its offset in the
1734
- // document is where that trimmed text actually starts — not the boundary,
1735
- // which is a line index. If it somehow can't be located, parse it without
1736
- // ranges rather than report wrong ones. Only worth searching for when
1737
- // ranges were asked for: this runs on every streamed chunk.
1738
- const unsettledStart = unsettledText && opts.sourceRanges ? input.indexOf(unsettledText, settledText.length) : -1;
1739
- const unsettledBlocks = unsettledText ? parseMarkdownImpl(unsettledText, unsettledStart >= 0 ? atOffset(parseOpts, unsettledStart) : nested(parseOpts)) : [];
1624
+ const unsettledBlocks = unsettledText ? parseMarkdownImpl(unsettledText, parseOpts) : [];
1740
1625
  state.settledText = settledText;
1741
1626
  state.settledBlocks = settledBlocks;
1742
1627
  state.settledUpTo = boundary;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/core",
3
- "version": "0.4.7-canary.e93acd6",
3
+ "version": "0.4.7-canary.ecaf779",
4
4
  "displayName": "Astryx Core",
5
5
  "description": "The component library. Accessible, themeable React components with built-in spacing, dark mode, and StyleX styling.",
6
6
  "author": "Meta Open Source",
@@ -661,7 +661,7 @@
661
661
  "react-dom": ">=19.0.0"
662
662
  },
663
663
  "devDependencies": {
664
- "@astryxdesign/cli": "0.4.7-canary.e93acd6",
664
+ "@astryxdesign/cli": "0.4.7-canary.ecaf779",
665
665
  "@babel/cli": "^8.0.4",
666
666
  "@babel/core": "^7.29.7",
667
667
  "@babel/preset-react": "^8.0.1",
@@ -25,7 +25,6 @@ export {
25
25
  export type {
26
26
  BlockNode,
27
27
  InlineNode,
28
- SourceRange,
29
28
  ListItemNode,
30
29
  TableCellNode,
31
30
  TableAlignment,
@@ -1,13 +1,8 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
3
  import {describe, it, expect} from 'vitest';
4
- import {
5
- createIncrementalState,
6
- parseInline,
7
- parseMarkdown,
8
- parseMarkdownIncremental,
9
- } from './parser';
10
- import type {BlockNode, InlineNode} from './parser';
4
+ import {parseMarkdown, parseInline} from './parser';
5
+ import type {InlineNode} from './parser';
11
6
 
12
7
  describe('parseInline', () => {
13
8
  it('parses plain text', () => {
@@ -1214,129 +1209,3 @@ describe('link reference definitions', () => {
1214
1209
  expect(blocks[0].type).toBe('paragraph');
1215
1210
  });
1216
1211
  });
1217
-
1218
- describe('sourceRanges', () => {
1219
- const slice = (source: string, block: {range?: readonly [number, number]}) =>
1220
- block.range == null ? null : source.slice(block.range[0], block.range[1]);
1221
-
1222
- it('is off by default', () => {
1223
- const [block] = parseMarkdown('# Title');
1224
- expect(block.range).toBeUndefined();
1225
- });
1226
-
1227
- it('gives every top-level block the source it came from', () => {
1228
- const source = [
1229
- '# Title',
1230
- '',
1231
- 'A paragraph that',
1232
- 'wraps onto two lines.',
1233
- '',
1234
- '- one',
1235
- '- two',
1236
- '',
1237
- '```js',
1238
- 'const x = 1;',
1239
- '```',
1240
- '',
1241
- '| a | b |',
1242
- '| --- | --- |',
1243
- '| 1 | 2 |',
1244
- '',
1245
- '> quoted',
1246
- '',
1247
- '---',
1248
- ].join('\n');
1249
- const blocks = parseMarkdown(source, {sourceRanges: true});
1250
- expect(blocks.map(b => slice(source, b))).toEqual([
1251
- '# Title',
1252
- 'A paragraph that\nwraps onto two lines.',
1253
- '- one\n- two',
1254
- '```js\nconst x = 1;\n```',
1255
- '| a | b |\n| --- | --- |\n| 1 | 2 |',
1256
- '> quoted',
1257
- '---',
1258
- ]);
1259
- });
1260
-
1261
- it('reports offsets into the input, not into the link-definition-stripped text', () => {
1262
- // The definition lines are removed before the block loop runs, so a naive
1263
- // offset would drift by their length for everything after them.
1264
- const source = [
1265
- '[ref]: https://example.com',
1266
- '',
1267
- 'See [the docs][ref].',
1268
- '',
1269
- 'Another paragraph.',
1270
- ].join('\n');
1271
- const blocks = parseMarkdown(source, {sourceRanges: true});
1272
- expect(blocks.map(b => slice(source, b))).toEqual([
1273
- 'See [the docs][ref].',
1274
- 'Another paragraph.',
1275
- ]);
1276
- });
1277
-
1278
- it('reports absolute offsets when the document is parsed incrementally', () => {
1279
- const source = ['# Title', '', 'One.', '', 'Two.', '', 'Three.'].join('\n');
1280
- const state = createIncrementalState();
1281
- let blocks: BlockNode[] = [];
1282
- for (let end = 1; end <= source.length; end++) {
1283
- blocks = parseMarkdownIncremental(source.slice(0, end), state, {
1284
- sourceRanges: true,
1285
- });
1286
- }
1287
- expect(blocks.map(b => slice(source, b))).toEqual([
1288
- '# Title',
1289
- 'One.',
1290
- 'Two.',
1291
- 'Three.',
1292
- ]);
1293
- });
1294
-
1295
- it('keeps the blank lines an unterminated fence owns', () => {
1296
- // Mid-stream the closing fence has not arrived, and the blank lines are
1297
- // part of the code, not spacing between blocks.
1298
- const source = '```\ncode\n\n';
1299
- const [block] = parseMarkdown(source, {sourceRanges: true});
1300
- // The whole thing: the fence consumed those lines as code.
1301
- expect(slice(source, block)).toBe(source);
1302
- });
1303
-
1304
- it('slices to something that re-parses to the same block', () => {
1305
- // The property a consumer actually needs, and the one that says the
1306
- // offsets are right: what the range points at is the block.
1307
- const check = (source: string) => {
1308
- for (const block of parseMarkdown(source, {sourceRanges: true})) {
1309
- const {range: _range, ...node} = block;
1310
- expect(parseMarkdown(slice(source, block)!)).toEqual([node]);
1311
- }
1312
- };
1313
- check('# Title\n\nA paragraph.\n\n- one\n- two\n\n> quoted');
1314
- // CRLF: the parser keeps the `\r` in its own content, so the range does
1315
- // too rather than slicing to text that parses differently.
1316
- check('# Title\r\n\r\nA paragraph.\r\n');
1317
- });
1318
-
1319
- it('re-parses when the caller flips the option on an existing state', () => {
1320
- const source = 'One.\n\nTwo.\n\nThree.';
1321
- const state = createIncrementalState();
1322
- const without = parseMarkdownIncremental(source, state);
1323
- expect(without.every(b => b.range == null)).toBe(true);
1324
- const with_ = parseMarkdownIncremental(source, state, {
1325
- sourceRanges: true,
1326
- });
1327
- expect(with_.map(b => slice(source, b))).toEqual(['One.', 'Two.', 'Three.']);
1328
- const back = parseMarkdownIncremental(source, state);
1329
- expect(back.every(b => b.range == null)).toBe(true);
1330
- });
1331
-
1332
- it('spans both halves of a list the incremental parser merged', () => {
1333
- const source = '1. one\n\n1. two';
1334
- const state = createIncrementalState();
1335
- parseMarkdownIncremental('1. one\n\n', state, {sourceRanges: true});
1336
- const blocks = parseMarkdownIncremental(source, state, {
1337
- sourceRanges: true,
1338
- });
1339
- expect(blocks).toHaveLength(1);
1340
- expect(slice(source, blocks[0])).toBe(source);
1341
- });
1342
- });
@@ -24,15 +24,7 @@ export type InlineNode =
24
24
  | {type: 'citation'; sourceId: string}
25
25
  | {type: 'break'};
26
26
 
27
- export type BlockNode = BlockNodeKind & {
28
- /**
29
- * Where this block came from in the source, when parsed with the
30
- * `sourceRanges` option. Top-level blocks only.
31
- */
32
- range?: SourceRange;
33
- };
34
-
35
- type BlockNodeKind =
27
+ export type BlockNode =
36
28
  | {type: 'heading'; level: 1 | 2 | 3 | 4 | 5 | 6; children: InlineNode[]}
37
29
  | {type: 'paragraph'; children: InlineNode[]}
38
30
  | {type: 'codeblock'; language: string; content: string}
@@ -55,12 +47,6 @@ type BlockNodeKind =
55
47
  | {type: 'hr'}
56
48
  | {type: 'image'; src: string; alt: string};
57
49
 
58
- /**
59
- * Character offsets `[start, end)` of a block in the source string handed to
60
- * `parseMarkdown`. `end` excludes the block's trailing blank lines.
61
- */
62
- export type SourceRange = readonly [start: number, end: number];
63
-
64
50
  export type ListItemNode = {checked?: boolean; children: BlockNode[]};
65
51
  export type TableCellNode = {children: InlineNode[]};
66
52
  export type TableAlignment = 'left' | 'center' | 'right' | null;
@@ -92,28 +78,11 @@ export type ParseOptions = {
92
78
  * suffix is not rejected (Astryx accepts any plausible TLD shape).
93
79
  */
94
80
  autolink?: 'gfm';
95
- /**
96
- * When true, every top-level block carries a `range` — the offsets it
97
- * occupies in the string passed in. Lets a consumer that still holds the
98
- * source slice the original markdown for a block instead of reconstructing
99
- * it from the parsed node (or from the rendered DOM). Off by default: the
100
- * field is absent unless asked for, so nothing that compares nodes changes.
101
- *
102
- * Blocks nested inside a list item or a blockquote do not carry one.
103
- */
104
- sourceRanges?: boolean;
105
81
  };
106
82
 
107
83
  type ResolvedOptions = {
108
84
  readonly sourceIds: ReadonlySet<string> | undefined;
109
85
  readonly autolink: 'gfm' | undefined;
110
- readonly sourceRanges?: boolean;
111
- /**
112
- * Offset of this parse's input within the document the ranges are reported
113
- * against. Internal only — the incremental parser parses slices and needs
114
- * their blocks' ranges to come out absolute.
115
- */
116
- readonly baseOffset?: number;
117
86
  /**
118
87
  * Link reference definitions (`[label]: url`) collected from the whole
119
88
  * document, keyed by normalized label. Internal only — populated by the
@@ -140,11 +109,7 @@ function resolveOptions(
140
109
  return {sourceIds: arg as ReadonlySet<string>, autolink: undefined};
141
110
  }
142
111
  const opts = arg as ParseOptions;
143
- return {
144
- sourceIds: opts.sourceIds,
145
- autolink: opts.autolink,
146
- sourceRanges: opts.sourceRanges,
147
- };
112
+ return {sourceIds: opts.sourceIds, autolink: opts.autolink};
148
113
  }
149
114
 
150
115
  // ---------------------------------------------------------------------------
@@ -209,11 +174,6 @@ function matchLinkDefinition(
209
174
  function extractLinkDefinitions(input: string): {
210
175
  defs: ReadonlyMap<string, string>;
211
176
  cleaned: string;
212
- /**
213
- * For each line of `cleaned`, the line of `input` it came from. Undefined
214
- * when nothing was stripped and the two are the same text.
215
- */
216
- lineMap?: number[];
217
177
  } {
218
178
  const lines = input.split('\n');
219
179
  const defs = new Map<string, string>();
@@ -275,14 +235,8 @@ function extractLinkDefinitions(input: string): {
275
235
  if (defs.size === 0) {
276
236
  return {defs, cleaned: input};
277
237
  }
278
- const lineMap: number[] = [];
279
- for (let index = 0; index < lines.length; index++) {
280
- if (keep[index]) {
281
- lineMap.push(index);
282
- }
283
- }
284
- const cleaned = lineMap.map(index => lines[index]).join('\n');
285
- return {defs, cleaned, lineMap};
238
+ const cleaned = lines.filter((_, index) => keep[index]).join('\n');
239
+ return {defs, cleaned};
286
240
  }
287
241
 
288
242
  /** Order-independent signature of a link-definition set, for cache checks. */
@@ -1088,16 +1042,6 @@ function isTableSeparator(line: string): boolean {
1088
1042
  return nonEmpty.length > 0 && nonEmpty.every(cell => /^:?-+:?$/.test(cell));
1089
1043
  }
1090
1044
 
1091
- /**
1092
- * The same options for content parsed out of an enclosing block. Ranges are a
1093
- * top-level contract: a list item's or a blockquote's children are parsed from
1094
- * text the caller reassembled (markers and `>` prefixes stripped), so an
1095
- * offset into it would not address the document.
1096
- */
1097
- function nested(opts: ResolvedOptions): ResolvedOptions {
1098
- return opts.sourceRanges ? {...opts, sourceRanges: false} : opts;
1099
- }
1100
-
1101
1045
  /**
1102
1046
  * Returns true when a line could start a new block — used to stop paragraph
1103
1047
  * continuation. Every regex here uses bounded or single-class quantifiers
@@ -1269,7 +1213,7 @@ function parseList(
1269
1213
  itemText += '\n' + deindented.join('\n');
1270
1214
  }
1271
1215
 
1272
- items.push({checked, children: parseMarkdownImpl(itemText, nested(opts))});
1216
+ items.push({checked, children: parseMarkdownImpl(itemText, opts)});
1273
1217
 
1274
1218
  // CommonMark loose list: blank line(s) between items of the same style
1275
1219
  // and indent still form one list. Skip the blanks and continue if the
@@ -1330,7 +1274,7 @@ function parseMarkdownImpl(
1330
1274
  // definitions win on conflict, matching CommonMark's first-definition-wins
1331
1275
  // in document order; locally-nested definitions still resolve within this
1332
1276
  // parse.
1333
- const {defs, cleaned, lineMap} = extractLinkDefinitions(input);
1277
+ const {defs, cleaned} = extractLinkDefinitions(input);
1334
1278
  const inherited = baseOpts.linkDefs;
1335
1279
  let linkDefs: ReadonlyMap<string, string> | undefined;
1336
1280
  if (defs.size === 0) {
@@ -1344,25 +1288,9 @@ function parseMarkdownImpl(
1344
1288
  linkDefs != null ? {...baseOpts, linkDefs} : baseOpts;
1345
1289
  const lines = cleaned.split('\n');
1346
1290
  const blocks: BlockNode[] = [];
1347
- // The line each block started on, parallel to `blocks`. Only collected when
1348
- // ranges were asked for; a block's end is resolved after the loop, since the
1349
- // branch that produced it has already moved `index` past whatever it read.
1350
- const blockStartLines: number[] | null = opts.sourceRanges ? [] : null;
1351
- // Set only by a block that consumes blank lines as content, where the
1352
- // positional end derivation would trim them away.
1353
- const blockEndLines: (number | undefined)[] | null = opts.sourceRanges
1354
- ? []
1355
- : null;
1356
- let blockStartLine = 0;
1357
- const pushBlock = (node: BlockNode, endLine?: number) => {
1358
- blocks.push(node);
1359
- blockStartLines?.push(blockStartLine);
1360
- blockEndLines?.push(endLine);
1361
- };
1362
1291
  let index = 0;
1363
1292
 
1364
1293
  while (index < lines.length) {
1365
- blockStartLine = index;
1366
1294
  const line = lines[index];
1367
1295
  if (line.trim() === '') {
1368
1296
  index++;
@@ -1381,20 +1309,14 @@ function parseMarkdownImpl(
1381
1309
  index++;
1382
1310
  }
1383
1311
  index++; // skip closing fence
1384
- // A fence owns its blank lines, and an unterminated one (mid-stream)
1385
- // can end on them, so it states its own end rather than letting the
1386
- // positional derivation trim them off.
1387
- pushBlock(
1388
- {type: 'codeblock', language, content: codeLines.join('\n')},
1389
- Math.min(index, lines.length) - 1,
1390
- );
1312
+ blocks.push({type: 'codeblock', language, content: codeLines.join('\n')});
1391
1313
  continue;
1392
1314
  }
1393
1315
 
1394
1316
  // --- Heading ---
1395
1317
  const headingMatch = line.match(/^(#{1,6}) +(.*)/);
1396
1318
  if (headingMatch) {
1397
- pushBlock({
1319
+ blocks.push({
1398
1320
  type: 'heading',
1399
1321
  level: headingMatch[1].length as 1 | 2 | 3 | 4 | 5 | 6,
1400
1322
  children: parseInlineEntry(headingMatch[2], opts),
@@ -1405,7 +1327,7 @@ function parseMarkdownImpl(
1405
1327
 
1406
1328
  // --- HR (must precede list check to handle `- - -`, `* * *`, `_ _ _`) ---
1407
1329
  if (isHorizontalRule(line)) {
1408
- pushBlock({type: 'hr'});
1330
+ blocks.push({type: 'hr'});
1409
1331
  index++;
1410
1332
  continue;
1411
1333
  }
@@ -1413,7 +1335,7 @@ function parseMarkdownImpl(
1413
1335
  // --- Standalone image ---
1414
1336
  const imageMatch = line.match(/^!\[([^\]]*)\]\(([^)]+)\)/);
1415
1337
  if (imageMatch && line.trim() === imageMatch[0]) {
1416
- pushBlock({type: 'image', alt: imageMatch[1], src: imageMatch[2]});
1338
+ blocks.push({type: 'image', alt: imageMatch[1], src: imageMatch[2]});
1417
1339
  index++;
1418
1340
  continue;
1419
1341
  }
@@ -1425,7 +1347,7 @@ function parseMarkdownImpl(
1425
1347
  isTableSeparator(lines[index + 1])
1426
1348
  ) {
1427
1349
  const tableResult = parseTable(lines, index, opts);
1428
- pushBlock(tableResult.node);
1350
+ blocks.push(tableResult.node);
1429
1351
  index = tableResult.nextIndex;
1430
1352
  continue;
1431
1353
  }
@@ -1440,9 +1362,9 @@ function parseMarkdownImpl(
1440
1362
  quoteLines.push(lines[index].replace(/^> ?/, ''));
1441
1363
  index++;
1442
1364
  }
1443
- pushBlock({
1365
+ blocks.push({
1444
1366
  type: 'blockquote',
1445
- children: parseMarkdownImpl(quoteLines.join('\n'), nested(opts)),
1367
+ children: parseMarkdownImpl(quoteLines.join('\n'), opts),
1446
1368
  });
1447
1369
  continue;
1448
1370
  }
@@ -1450,7 +1372,7 @@ function parseMarkdownImpl(
1450
1372
  // --- Unordered list ---
1451
1373
  if (/^ {0,9}[-*+] /.test(line)) {
1452
1374
  const listResult = parseList(lines, index, false, opts);
1453
- pushBlock(listResult.node);
1375
+ blocks.push(listResult.node);
1454
1376
  index = listResult.nextIndex;
1455
1377
  continue;
1456
1378
  }
@@ -1458,7 +1380,7 @@ function parseMarkdownImpl(
1458
1380
  // --- Ordered list ---
1459
1381
  if (/^ {0,9}\d+[.)] /.test(line)) {
1460
1382
  const listResult = parseList(lines, index, true, opts);
1461
- pushBlock(listResult.node);
1383
+ blocks.push(listResult.node);
1462
1384
  index = listResult.nextIndex;
1463
1385
  continue;
1464
1386
  }
@@ -1474,76 +1396,14 @@ function parseMarkdownImpl(
1474
1396
  paraLines.push(lines[index]);
1475
1397
  index++;
1476
1398
  }
1477
- pushBlock({
1399
+ blocks.push({
1478
1400
  type: 'paragraph',
1479
1401
  children: parseInlineEntry(paraLines.join('\n'), opts),
1480
1402
  });
1481
1403
  }
1482
- if (blockStartLines != null) {
1483
- stampSourceRanges(
1484
- blocks,
1485
- blockStartLines,
1486
- blockEndLines ?? [],
1487
- lines,
1488
- lineMap,
1489
- input,
1490
- opts,
1491
- );
1492
- }
1493
1404
  return blocks;
1494
1405
  }
1495
1406
 
1496
- /**
1497
- * Give each block the offsets it occupies in the original input.
1498
- *
1499
- * Blocks are contiguous and in source order, so a block runs from its own
1500
- * first line to the line before the next block starts, minus the blank lines
1501
- * between them. Offsets are computed against the *input*, not the text the
1502
- * block loop saw: link reference definitions are stripped before parsing, and
1503
- * `lineMap` says which input line each surviving line came from.
1504
- */
1505
- function stampSourceRanges(
1506
- blocks: BlockNode[],
1507
- blockStartLines: number[],
1508
- blockEndLines: (number | undefined)[],
1509
- lines: string[],
1510
- lineMap: number[] | undefined,
1511
- input: string,
1512
- opts: ResolvedOptions,
1513
- ): void {
1514
- const base = opts.baseOffset ?? 0;
1515
- // Offset of the first character of every line of the input.
1516
- const inputLineStarts = [0];
1517
- for (let i = 0; i < input.length; i++) {
1518
- if (input[i] === '\n') {
1519
- inputLineStarts.push(i + 1);
1520
- }
1521
- }
1522
- // Stripping removes whole lines and never edits one, so a parsed line's
1523
- // length is its input line's length.
1524
- const lineStart = (line: number): number =>
1525
- base + inputLineStarts[lineMap != null ? lineMap[line] : line];
1526
-
1527
- for (let i = 0; i < blocks.length; i++) {
1528
- const startLine = blockStartLines[i];
1529
- let endLine = blockEndLines[i];
1530
- if (endLine == null) {
1531
- const nextStart =
1532
- i + 1 < blocks.length ? blockStartLines[i + 1] : lines.length;
1533
- endLine = nextStart - 1;
1534
- while (endLine > startLine && lines[endLine].trim() === '') {
1535
- endLine--;
1536
- }
1537
- }
1538
- // Exactly the block's own lines, verbatim — a CRLF document's trailing
1539
- // `\r` included, since the parser reads it as part of the line too and a
1540
- // range that dropped it would slice to something that re-parses
1541
- // differently.
1542
- const end = lineStart(endLine) + lines[endLine].length;
1543
- blocks[i] = {...blocks[i], range: [lineStart(startLine), end]};
1544
- }
1545
- }
1546
-
1547
1407
  // ---------------------------------------------------------------------------
1548
1408
  // Incremental parsing
1549
1409
  // ---------------------------------------------------------------------------
@@ -1560,12 +1420,6 @@ export interface IncrementalState {
1560
1420
  * with newly-arriving content.
1561
1421
  */
1562
1422
  autolink?: 'gfm';
1563
- /**
1564
- * The `sourceRanges` option the cached `settledBlocks` were parsed with.
1565
- * Flipping it invalidates them the same way `autolink` does: they either
1566
- * lack the ranges the caller now asks for, or carry ones it did not.
1567
- */
1568
- sourceRanges?: boolean;
1569
1423
  /**
1570
1424
  * Signature of the link reference definitions the cached `settledBlocks`
1571
1425
  * were parsed with. Definitions are document-global and typically arrive
@@ -1835,15 +1689,6 @@ function trimUnsettledStructural(text: string): string {
1835
1689
  return lines.join('\n');
1836
1690
  }
1837
1691
 
1838
- /**
1839
- * The same options, for parsing a slice that starts at `offset` of the
1840
- * document — so the slice's blocks report ranges into the whole document
1841
- * rather than into the slice.
1842
- */
1843
- function atOffset(opts: ResolvedOptions, offset: number): ResolvedOptions {
1844
- return opts.sourceRanges ? {...opts, baseOffset: offset} : opts;
1845
- }
1846
-
1847
1692
  /**
1848
1693
  * Concatenate freshly-parsed delta blocks with previously-settled blocks,
1849
1694
  * merging adjacent same-style lists into a single loose list. The boundary
@@ -1873,11 +1718,6 @@ function mergeSettledBlocks(
1873
1718
  delimiter: prevLast.delimiter,
1874
1719
  loose: true,
1875
1720
  items: [...prevLast.items, ...deltaFirst.items],
1876
- // One list now, so one range: from where the first half started to
1877
- // where the second half ended.
1878
- ...(prevLast.range != null && deltaFirst.range != null
1879
- ? {range: [prevLast.range[0], deltaFirst.range[1]] as SourceRange}
1880
- : null),
1881
1721
  };
1882
1722
  return [...prev.slice(0, -1), merged, ...delta.slice(1)];
1883
1723
  }
@@ -1900,19 +1740,15 @@ export function parseMarkdownIncremental(
1900
1740
  arg?: ReadonlySet<string> | ParseOptions,
1901
1741
  ): BlockNode[] {
1902
1742
  const opts = resolveOptions(arg);
1903
- // Invalidate cache when the autolink or sourceRanges option flips — cached
1904
- // settled blocks were parsed with the previous setting and would otherwise
1905
- // be reused unchanged.
1906
- if (
1907
- state.autolink !== opts.autolink ||
1908
- Boolean(state.sourceRanges) !== Boolean(opts.sourceRanges)
1909
- ) {
1743
+ // Invalidate cache when the autolink option flips — cached settled blocks
1744
+ // were parsed with the previous setting and would otherwise be reused
1745
+ // unchanged.
1746
+ if (state.autolink !== opts.autolink) {
1910
1747
  state.prevInput = '';
1911
1748
  state.settledText = '';
1912
1749
  state.settledBlocks = [];
1913
1750
  state.settledUpTo = 0;
1914
1751
  state.autolink = opts.autolink;
1915
- state.sourceRanges = opts.sourceRanges;
1916
1752
  }
1917
1753
  if (input === '') {
1918
1754
  state.prevInput = '';
@@ -1963,32 +1799,15 @@ export function parseMarkdownIncremental(
1963
1799
  ) {
1964
1800
  // Settled portion grew — parse only the new delta
1965
1801
  const delta = settledText.slice(state.settledText.length);
1966
- const deltaBlocks = parseMarkdownImpl(
1967
- delta,
1968
- atOffset(parseOpts, state.settledText.length),
1969
- );
1802
+ const deltaBlocks = parseMarkdownImpl(delta, parseOpts);
1970
1803
  settledBlocks = mergeSettledBlocks(state.settledBlocks, deltaBlocks);
1971
1804
  } else {
1972
1805
  // Content before the boundary changed — full re-parse of settled portion
1973
1806
  settledBlocks = parseMarkdownImpl(settledText, parseOpts);
1974
1807
  }
1975
1808
 
1976
- // The unsettled tail is trimmed before parsing, so its offset in the
1977
- // document is where that trimmed text actually starts — not the boundary,
1978
- // which is a line index. If it somehow can't be located, parse it without
1979
- // ranges rather than report wrong ones. Only worth searching for when
1980
- // ranges were asked for: this runs on every streamed chunk.
1981
- const unsettledStart =
1982
- unsettledText && opts.sourceRanges
1983
- ? input.indexOf(unsettledText, settledText.length)
1984
- : -1;
1985
1809
  const unsettledBlocks = unsettledText
1986
- ? parseMarkdownImpl(
1987
- unsettledText,
1988
- unsettledStart >= 0
1989
- ? atOffset(parseOpts, unsettledStart)
1990
- : nested(parseOpts),
1991
- )
1810
+ ? parseMarkdownImpl(unsettledText, parseOpts)
1992
1811
  : [];
1993
1812
 
1994
1813
  state.settledText = settledText;