@vesk/compiler 0.2.59 → 0.2.61

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.
@@ -1 +1 @@
1
- {"version":3,"file":"client-codegen.d.ts","sourceRoot":"","sources":["../src/client-codegen.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAuB,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAGL,cAAc,EAQd,gBAAgB,EASjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAkGxD,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC;CAC1B;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI1D;AAiDD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,gBAAgB,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,CAgJlI;AAID,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAU5E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,EAAE,CAuB9F;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,CAY5F;AAoBD,qDAAqD;AACrD,MAAM,MAAM,SAAS,GAAG;IAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,SAAS,CAQtE;AAmiED,iBAAS,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAe3C;AAmJD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO9C;AAuED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,aAAa,CAAC,EAAE,SAAS,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,MAAM,CAOpQ;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,GAAG,IAAI,EAC7B,UAAU,CAAC,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAC;IAAC,cAAc,EAAE,MAAM,EAAE,CAAA;CAAE,CAyC5J;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAG/E;AAED,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,UAAU,EAAE,CAAC"}
1
+ {"version":3,"file":"client-codegen.d.ts","sourceRoot":"","sources":["../src/client-codegen.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAuB,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAGL,cAAc,EAQd,gBAAgB,EAUjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAkGxD,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC;CAC1B;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI1D;AAiDD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,gBAAgB,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,CAgJlI;AAID,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAI5E;AAyCD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,EAAE,CAuB9F;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,CAY5F;AAoBD,qDAAqD;AACrD,MAAM,MAAM,SAAS,GAAG;IAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,SAAS,CAQtE;AAumED,iBAAS,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAe3C;AAmJD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO9C;AAuED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,aAAa,CAAC,EAAE,SAAS,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,MAAM,CAOpQ;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,GAAG,IAAI,EAC7B,UAAU,CAAC,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAC;IAAC,cAAc,EAAE,MAAM,EAAE,CAAA;CAAE,CAyC5J;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAG/E;AAED,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,UAAU,EAAE,CAAC"}
@@ -2,7 +2,7 @@ import { walk } from 'zimmerframe';
2
2
  import { print } from 'esrap';
3
3
  import ts from 'esrap/languages/ts';
4
4
  import tsx from 'esrap/languages/tsx';
5
- import { StaticNode, TextNode, DynamicBinding, OpaqueDynamicRegion, MapRegion, WhileLoop, SwitchBlock, TryCatch, ForLoop, TrackDecl, RuntimeStatement, ComponentRef, ComponentCall, ServerBlock, ClientBlock, HeadBlock, SlotNode, PropSlot, PropSlotRender, } from '@vesk/compiler/src/ir';
5
+ import { StaticNode, TextNode, DynamicBinding, OpaqueDynamicRegion, MapRegion, WhileLoop, SwitchBlock, TryCatch, ForLoop, TrackDecl, RuntimeStatement, BreakSignal, ComponentRef, ComponentCall, ServerBlock, ClientBlock, HeadBlock, SlotNode, PropSlot, PropSlotRender, } from '@vesk/compiler/src/ir';
6
6
  import { parse } from '@vesk/compiler/src/parser';
7
7
  import { generateIR } from '@vesk/compiler/src/ir-generator';
8
8
  import { transformTopLevelForActions } from '@vesk/compiler/src/actions';
@@ -296,6 +296,20 @@ export function transformTracked(irNode, tracked) {
296
296
  }
297
297
  export function collectTrackedNames(body) {
298
298
  const names = new Map();
299
+ collectTrackedNamesInto(body, names);
300
+ return names;
301
+ }
302
+ /**
303
+ * A `track()` declaration is not always a direct child of the component body.
304
+ * A guard-clause early return (`if (!props.x) return <empty/>;`) lowers to an
305
+ * opaque region whose alternate branch carries the rest of the body — so the
306
+ * track decls (and every assignment that must become `set(cell, …)`) live
307
+ * inside `OpaqueDynamicRegion.alternateNodes`, not at the top level. Other
308
+ * constructs (loops, switch cases, `{#server}` blocks, slot children) nest
309
+ * bodies too. Miss one and the cell is emitted but never `get`/`set`-wrapped,
310
+ * which both breaks reactivity and leaves `x = …` assigning to a `const`.
311
+ */
312
+ function collectTrackedNamesInto(body, names) {
299
313
  for (const node of body) {
300
314
  if (node instanceof TrackDecl) {
301
315
  const cellName = node.rawName || node.name;
@@ -303,8 +317,35 @@ export function collectTrackedNames(body) {
303
317
  if (node.rawName)
304
318
  names.set(node.rawName, { cellName, kind: 'cell' });
305
319
  }
320
+ else if (node instanceof StaticNode || node instanceof ServerBlock || node instanceof ClientBlock || node instanceof HeadBlock) {
321
+ collectTrackedNamesInto(node.children, names);
322
+ }
323
+ else if (node instanceof ComponentCall) {
324
+ collectTrackedNamesInto(node.children, names);
325
+ }
326
+ else if (node instanceof PropSlot) {
327
+ collectTrackedNamesInto(node.body, names);
328
+ }
329
+ else if (node instanceof MapRegion) {
330
+ collectTrackedNamesInto(node.bodyTemplate, names);
331
+ collectTrackedNamesInto(node.alternateNodes, names);
332
+ }
333
+ else if (node instanceof OpaqueDynamicRegion) {
334
+ collectTrackedNamesInto(node.consequentNodes, names);
335
+ collectTrackedNamesInto(node.alternateNodes, names);
336
+ }
337
+ else if (node instanceof WhileLoop || node instanceof ForLoop) {
338
+ collectTrackedNamesInto(node.bodyTemplate, names);
339
+ }
340
+ else if (node instanceof TryCatch) {
341
+ collectTrackedNamesInto(node.bodyTemplate, names);
342
+ collectTrackedNamesInto(node.catchBody, names);
343
+ }
344
+ else if (node instanceof SwitchBlock) {
345
+ for (const c of node.cases)
346
+ collectTrackedNamesInto(c.body, names);
347
+ }
306
348
  }
307
- return names;
308
349
  }
309
350
  /**
310
351
  * Collect the tracked bindings a prop expression READS, so a string-returning
@@ -428,6 +469,9 @@ class Ctx {
428
469
  walker = '__hydrate';
429
470
  asyncComps = new Set();
430
471
  isAsyncScope = false;
472
+ // While emitting a loop body, names the boolean flag a `break` sets (see
473
+ // BreakSignal). Null outside a break-capable loop body.
474
+ breakFlag = null;
431
475
  constructor(alloc) {
432
476
  this.alloc = alloc ?? { c: 0 };
433
477
  }
@@ -516,6 +560,11 @@ function emitNode(ctx, node, tracked, effectsVar, parentVar, compPrefix = '__com
516
560
  ctx.push(semicolonizeStatement(transformTracked(node, tracked)));
517
561
  return null;
518
562
  }
563
+ if (node instanceof BreakSignal) {
564
+ if (ctx.breakFlag)
565
+ ctx.push(`${ctx.breakFlag} = true;`);
566
+ return null;
567
+ }
519
568
  if (node instanceof SlotNode) {
520
569
  if (!parentVar) {
521
570
  // A TOP-LEVEL `{props.children}` — the whole body of a layout is one
@@ -528,8 +577,16 @@ function emitNode(ctx, node, tracked, effectsVar, parentVar, compPrefix = '__com
528
577
  // The children ARE this layout's output, so they take the component's
529
578
  // return value rather than being appended to a parent that does not exist.
530
579
  if (ctx.hydrate) {
580
+ const promptExpr = ctx.markerless && (regionBudget || skipK > 0)
581
+ ? markerlessSkipExpr(regionBudget, skipK)
582
+ : null;
531
583
  ctx.push(`if (props.children !== undefined && props.children !== null) {`);
532
584
  ctx.push(` if (typeof props.children === 'function') {`);
585
+ if (promptExpr) {
586
+ ctx.push(` ${ctx.walker}.injectSkipK(${promptExpr});`);
587
+ if (regionBudget && ctx.markerless)
588
+ ctx.push(` ${regionBudget} = 0;`);
589
+ }
533
590
  ctx.push(` __pendingChild = props.children(__hydrate);`);
534
591
  ctx.push(` } else if (__hydrate && __hydrate.root) {`);
535
592
  ctx.push(` __hydrate.root.appendChild(toDomNode(props.children));`);
@@ -1741,6 +1798,8 @@ function emitWhileLoop(ctx, node, tracked, parentVar, skipK = 0, regionBudget) {
1741
1798
  ctx.push(`const ${endAnchor} = document.createComment('while-end');`);
1742
1799
  emitHydrateFenceAnchoring(ctx, anchor, endAnchor, parent === '$root', budget || undefined);
1743
1800
  const renderLoop = ctx.n();
1801
+ const hasBreak = bodyHasBreak(node.bodyTemplate);
1802
+ const breakVar = hasBreak ? ctx.n() : null;
1744
1803
  ctx.push(`const ${renderLoop} = ${ctx.isAsyncScope ? 'async () => {' : '() => {'}`);
1745
1804
  if (!hyd)
1746
1805
  ctx.push(indent(`const __p = ${anchor}.parentNode;`));
@@ -1748,8 +1807,14 @@ function emitWhileLoop(ctx, node, tracked, parentVar, skipK = 0, regionBudget) {
1748
1807
  ctx.push(indent(`const __b = document.createDocumentFragment();`));
1749
1808
  if (hyd)
1750
1809
  ctx.push(indent(`const __cl = [];`));
1810
+ if (breakVar)
1811
+ ctx.push(indent(`let ${breakVar} = false;`));
1812
+ const savedBreakFlag = ctx.breakFlag;
1813
+ ctx.breakFlag = breakVar;
1751
1814
  if (node.isDoWhile) {
1752
1815
  ctx.push(indent(`do {`));
1816
+ if (breakVar)
1817
+ ctx.push(indent(indent(`if (${breakVar}) break;`)));
1753
1818
  for (const n of node.bodyTemplate) {
1754
1819
  const v = emitNode(ctx, n, tracked, effectsVar, hyd ? parentVar : '__b', '__components', 0, budget || undefined);
1755
1820
  if (v) {
@@ -1763,6 +1828,8 @@ function emitWhileLoop(ctx, node, tracked, parentVar, skipK = 0, regionBudget) {
1763
1828
  }
1764
1829
  else {
1765
1830
  ctx.push(indent(`while (${condExpr}) {`));
1831
+ if (breakVar)
1832
+ ctx.push(indent(indent(`if (${breakVar}) break;`)));
1766
1833
  for (const n of node.bodyTemplate) {
1767
1834
  const v = emitNode(ctx, n, tracked, effectsVar, hyd ? parentVar : '__b', '__components', 0, budget || undefined);
1768
1835
  if (v) {
@@ -1774,6 +1841,7 @@ function emitWhileLoop(ctx, node, tracked, parentVar, skipK = 0, regionBudget) {
1774
1841
  }
1775
1842
  ctx.push(indent(`}`));
1776
1843
  }
1844
+ ctx.breakFlag = savedBreakFlag;
1777
1845
  if (!hyd)
1778
1846
  ctx.push(indent(`__p.insertBefore(__b, ${endAnchor});`));
1779
1847
  if (hyd)
@@ -1817,6 +1885,8 @@ function emitForLoop(ctx, node, tracked, parentVar, skipK = 0, regionBudget) {
1817
1885
  ctx.push(`const ${endAnchor} = document.createComment('for-end');`);
1818
1886
  emitHydrateFenceAnchoring(ctx, anchor, endAnchor, parent === '$root', budget || undefined);
1819
1887
  const renderLoop = ctx.n();
1888
+ const forHasBreak = bodyHasBreak(node.bodyTemplate);
1889
+ const forBreakVar = forHasBreak ? ctx.n() : null;
1820
1890
  ctx.push(`const ${renderLoop} = ${ctx.isAsyncScope ? 'async () => {' : '() => {'}`);
1821
1891
  if (!hyd)
1822
1892
  ctx.push(indent(`const __p = ${anchor}.parentNode;`));
@@ -1824,9 +1894,15 @@ function emitForLoop(ctx, node, tracked, parentVar, skipK = 0, regionBudget) {
1824
1894
  ctx.push(indent(`const __b = document.createDocumentFragment();`));
1825
1895
  if (hyd)
1826
1896
  ctx.push(indent(`const __cl = [];`));
1897
+ if (forBreakVar)
1898
+ ctx.push(indent(`let ${forBreakVar} = false;`));
1899
+ const savedForBreakFlag = ctx.breakFlag;
1900
+ ctx.breakFlag = forBreakVar;
1827
1901
  if (node.kind === 'for-in') {
1828
1902
  const srcExpr = transformTracked(node.condition, tracked);
1829
1903
  ctx.push(indent(`for (${node.init} of (Array.isArray(${srcExpr}) ? ${srcExpr} : (${srcExpr} == null ? [] : Object.keys(${srcExpr})))) {`));
1904
+ if (forBreakVar)
1905
+ ctx.push(indent(indent(`if (${forBreakVar}) break;`)));
1830
1906
  for (const n of node.bodyTemplate) {
1831
1907
  const v = emitNode(ctx, n, tracked, effectsVar, hyd ? parentVar : '__b', '__components', 0, budget || undefined);
1832
1908
  if (v) {
@@ -1856,6 +1932,8 @@ function emitForLoop(ctx, node, tracked, parentVar, skipK = 0, regionBudget) {
1856
1932
  updatePart = updatePart.slice(0, -1).trim();
1857
1933
  const head = initPart ? `${initPart}; ${condExpr}` : `; ${condExpr}`;
1858
1934
  ctx.push(indent(`for (${head}; ${updatePart}) {`));
1935
+ if (forBreakVar)
1936
+ ctx.push(indent(indent(`if (${forBreakVar}) break;`)));
1859
1937
  for (const n of node.bodyTemplate) {
1860
1938
  const v = emitNode(ctx, n, tracked, effectsVar, hyd ? parentVar : '__b', '__components', 0, budget || undefined);
1861
1939
  if (v) {
@@ -1867,6 +1945,7 @@ function emitForLoop(ctx, node, tracked, parentVar, skipK = 0, regionBudget) {
1867
1945
  }
1868
1946
  ctx.push(indent(`}`));
1869
1947
  }
1948
+ ctx.breakFlag = savedForBreakFlag;
1870
1949
  if (!hyd)
1871
1950
  ctx.push(indent(`__p.insertBefore(__b, ${endAnchor});`));
1872
1951
  if (hyd)
@@ -2052,6 +2131,47 @@ function emitSwitchBlock(ctx, node, tracked, parentVar, skipK = 0, regionBudget)
2052
2131
  }`);
2053
2132
  return null;
2054
2133
  }
2134
+ /**
2135
+ * True when a loop body contains a BreakSignal (or `continue`/`break`) that
2136
+ * targets the loop itself. Descent stops at nested loops, whose breaks bind to
2137
+ * themselves and are handled by their own emitter.
2138
+ */
2139
+ function bodyHasBreak(nodes) {
2140
+ for (const n of nodes) {
2141
+ if (n instanceof BreakSignal)
2142
+ return true;
2143
+ if (n instanceof OpaqueDynamicRegion) {
2144
+ if (bodyHasBreak([...n.consequentNodes, ...n.alternateNodes]))
2145
+ return true;
2146
+ }
2147
+ else if (n instanceof SwitchBlock) {
2148
+ for (const c of n.cases)
2149
+ if (bodyHasBreak(c.body))
2150
+ return true;
2151
+ }
2152
+ else if (n instanceof TryCatch) {
2153
+ if (bodyHasBreak([...n.bodyTemplate, ...n.catchBody]))
2154
+ return true;
2155
+ }
2156
+ else if (n instanceof StaticNode) {
2157
+ if (bodyHasBreak(n.children))
2158
+ return true;
2159
+ }
2160
+ else if (n instanceof ComponentCall) {
2161
+ if (bodyHasBreak(n.children))
2162
+ return true;
2163
+ }
2164
+ else if (n instanceof ServerBlock || n instanceof ClientBlock || n instanceof HeadBlock) {
2165
+ if (bodyHasBreak(n.children))
2166
+ return true;
2167
+ }
2168
+ else if (n instanceof PropSlot) {
2169
+ if (bodyHasBreak(n.body))
2170
+ return true;
2171
+ }
2172
+ }
2173
+ return false;
2174
+ }
2055
2175
  function emitMap(ctx, node, tracked, effTarget, parentVar, skipK = 0, regionBudget) {
2056
2176
  const arrExpr = transformTracked(node.expression, tracked);
2057
2177
  const itemVar = node.itemVariable;
@@ -2101,9 +2221,15 @@ function emitMap(ctx, node, tracked, effTarget, parentVar, skipK = 0, regionBudg
2101
2221
  ctx.push(`let ${effectsVar} = [];`);
2102
2222
  ctx.push(`const ${endAnchor} = document.createComment('map-end');`);
2103
2223
  const renderItem = ctx.n();
2224
+ const hasBreak = bodyHasBreak(node.bodyTemplate);
2225
+ const breakVar = hasBreak ? ctx.n() : null;
2226
+ if (breakVar)
2227
+ ctx.push(`let ${breakVar} = false;`);
2104
2228
  const indexParam = node.indexVariable ? ', __i' : '';
2105
2229
  ctx.push(`const ${renderItem} = ${ctx.isAsyncScope ? 'async ' : ''}(${itemVar}${indexParam}, __e, __r${hyd ? ', __cl' : ''}${hydKeyed ? ', __root' : ''}) => {`);
2106
2230
  ctx.push(indent(`__r = __r || ${endAnchor};`));
2231
+ if (breakVar)
2232
+ ctx.push(indent(`if (${breakVar}) return;`));
2107
2233
  if (node.indexVariable)
2108
2234
  ctx.push(indent(`const ${node.indexVariable} = __i;`));
2109
2235
  if (hyd) {
@@ -2120,6 +2246,8 @@ function emitMap(ctx, node, tracked, effTarget, parentVar, skipK = 0, regionBudg
2120
2246
  }
2121
2247
  if (hydKeyed)
2122
2248
  ctx.rootClaim = true;
2249
+ const savedMapBreak = ctx.breakFlag;
2250
+ ctx.breakFlag = breakVar;
2123
2251
  for (const n of node.bodyTemplate) {
2124
2252
  const v = emitNode(ctx, n, tracked, '__e', hyd ? undefined : '__it', '__components', 0, budget || undefined);
2125
2253
  if (v) {
@@ -2137,6 +2265,7 @@ function emitMap(ctx, node, tracked, effTarget, parentVar, skipK = 0, regionBudg
2137
2265
  }
2138
2266
  }
2139
2267
  }
2268
+ ctx.breakFlag = savedMapBreak;
2140
2269
  if (hydKeyed)
2141
2270
  ctx.rootClaim = false;
2142
2271
  if (!hyd)
@@ -2191,16 +2320,17 @@ function emitMap(ctx, node, tracked, effTarget, parentVar, skipK = 0, regionBudg
2191
2320
  const keyFnSrc = `(${itemVar}${node.indexVariable ? `, ${node.indexVariable}` : ''}) => ${keyExpr}`;
2192
2321
  const reconciler = ctx.n();
2193
2322
  const initList = ctx.n();
2323
+ const breakReset = breakVar ? `${breakVar} = false; ` : '';
2194
2324
  ctx.push(`let ${reconciler} = () => {};`);
2195
2325
  if (hydKeyed) {
2196
2326
  // claim-by-key hydration: `reconcileHydrated` attaches the region anchors
2197
2327
  // inside `parent` (pre-attach, `anchor.parentNode` is null) and peeks all
2198
2328
  // keys before claim-by-key consumes the walker, so server-rendered items
2199
2329
  // are adopted and wired in place instead of being rebuilt.
2200
- ctx.push(`const ${initList} = () => { ${reconciler} = reconcileHydrated(${anchor}, ${endAnchor}, ${arrExpr}, ${keyFnSrc}, (${itemVar}, __i, __e, __root) => ${renderItem}(${itemVar}${indexParam ? ', __i, __e' : ', __e'}, null, __root), __hydrate, ${parent}, ${skipK}); };`);
2330
+ ctx.push(`const ${initList} = () => { ${breakReset}${reconciler} = reconcileHydrated(${anchor}, ${endAnchor}, ${arrExpr}, ${keyFnSrc}, (${itemVar}, __i, __e, __root) => ${renderItem}(${itemVar}${indexParam ? ', __i, __e' : ', __e'}, null, __root), __hydrate, ${parent}, ${skipK}); };`);
2201
2331
  }
2202
2332
  else {
2203
- ctx.push(`const ${initList} = () => { ${reconciler} = reconcile(${anchor}, ${endAnchor}, ${arrExpr}, ${keyFnSrc}, (${itemVar}, __i, __e) => ${renderItem}(${itemVar}${indexParam ? ', __i, __e' : ', __e'})); };`);
2333
+ ctx.push(`const ${initList} = () => { ${breakReset}${reconciler} = reconcile(${anchor}, ${endAnchor}, ${arrExpr}, ${keyFnSrc}, (${itemVar}, __i, __e) => ${renderItem}(${itemVar}${indexParam ? ', __i, __e' : ', __e'})); };`);
2204
2334
  }
2205
2335
  if (emptyRenderName) {
2206
2336
  const isEmptyVar = ctx.n();
@@ -2212,7 +2342,7 @@ function emitMap(ctx, node, tracked, effTarget, parentVar, skipK = 0, regionBudg
2212
2342
  const __new = ${hasItems}();
2213
2343
  if (__first) { __first = false; __new; return; }
2214
2344
  if (__new !== ${isEmptyVar}) {
2215
- if (__new) ${reconciler}(${arrExpr});
2345
+ if (__new) {${breakVar ? ` ${breakVar} = false;` : ''} ${reconciler}(${arrExpr}); }
2216
2346
  return;
2217
2347
  }
2218
2348
  ${isEmptyVar} = !__new;
@@ -2228,24 +2358,30 @@ function emitMap(ctx, node, tracked, effTarget, parentVar, skipK = 0, regionBudg
2228
2358
  return null;
2229
2359
  pushEffect(`let __first = true;`, `effect(() => {
2230
2360
  const __nv = ${arrExpr};
2231
- if (__first) { __first = false; __nv; ${reconciler}(${arrExpr}); return; }
2232
- ${reconciler}(${arrExpr});
2361
+ if (__first) { __first = false; __nv; ${breakReset}${reconciler}(${arrExpr}); return; }
2362
+ ${breakVar ? `if (${breakVar}) ${breakVar} = false;` : ''}${reconciler}(${arrExpr});
2233
2363
  })`);
2234
2364
  }
2235
2365
  }
2236
2366
  else {
2237
2367
  const renderAllItems = (ind, collect) => {
2238
2368
  const lines = [];
2369
+ if (breakVar)
2370
+ lines.push(`${ind}${breakVar} = false;`);
2239
2371
  if (node.indexVariable) {
2240
2372
  lines.push(`${ind}let __i = 0;`);
2241
2373
  lines.push(`${ind}for (const ${itemVar} of ${arrExpr}) {`);
2242
2374
  lines.push(`${ind}\t${aw}${renderItem}(${itemVar}, __i, ${effectsVar}${collect ? `, null, ${collect}` : ''});`);
2243
2375
  lines.push(`${ind}\t__i++;`);
2376
+ if (breakVar)
2377
+ lines.push(`${ind}\tif (${breakVar}) break;`);
2244
2378
  lines.push(`${ind}}`);
2245
2379
  }
2246
2380
  else {
2247
2381
  lines.push(`${ind}for (const ${itemVar} of ${arrExpr}) {`);
2248
2382
  lines.push(`${ind}\t${aw}${renderItem}(${itemVar}, ${effectsVar}${collect ? `, null, ${collect}` : ''});`);
2383
+ if (breakVar)
2384
+ lines.push(`${ind}\tif (${breakVar}) break;`);
2249
2385
  lines.push(`${ind}}`);
2250
2386
  }
2251
2387
  return lines.join('\n');
@@ -1 +1 @@
1
- {"version":3,"file":"ir-generator.d.ts","sourceRoot":"","sources":["../src/ir-generator.ts"],"names":[],"mappings":"AACA,OAAO,EACL,MAAM,EAsBP,MAAM,uBAAuB,CAAC;AA2T/B,wBAAgB,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,GAAG,CAAC;IAAC,cAAc,CAAC,EAAE,GAAG,CAAC;IAAC,UAAU,CAAC,EAAE,GAAG,EAAE,CAAA;CAAE,CAAC,GAAG,SAAS,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkCnL;AAgmDD,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAqV9E"}
1
+ {"version":3,"file":"ir-generator.d.ts","sourceRoot":"","sources":["../src/ir-generator.ts"],"names":[],"mappings":"AACA,OAAO,EACL,MAAM,EAuBP,MAAM,uBAAuB,CAAC;AA2T/B,wBAAgB,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,GAAG,CAAC;IAAC,cAAc,CAAC,EAAE,GAAG,CAAC;IAAC,UAAU,CAAC,EAAE,GAAG,EAAE,CAAA;CAAE,CAAC,GAAG,SAAS,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkCnL;AAytDD,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAqV9E"}
@@ -1,4 +1,4 @@
1
- import { IRRoot, ComponentIR, StaticNode, TextNode, DynamicBinding, OpaqueDynamicRegion, MapRegion, WhileLoop, SwitchBlock, TryCatch, RuntimeStatement, ForLoop, TrackDecl, ComponentRef, ComponentCall, ServerBlock, ClientBlock, HeadBlock, Expression, SlotNode, PropSlot, PropSlotRender, } from '@vesk/compiler/src/ir';
1
+ import { IRRoot, ComponentIR, StaticNode, TextNode, DynamicBinding, OpaqueDynamicRegion, MapRegion, WhileLoop, SwitchBlock, TryCatch, RuntimeStatement, ForLoop, TrackDecl, ComponentRef, ComponentCall, ServerBlock, ClientBlock, HeadBlock, BreakSignal, Expression, SlotNode, PropSlot, PropSlotRender, } from '@vesk/compiler/src/ir';
2
2
  import { VeskError, codeFrame } from '@vesk/compiler/src/errors';
3
3
  import { createBaseParser } from '@vesk/compiler/src/parser';
4
4
  import { skipWhitespace, findBalancedEnd, splitTopLevel, startsWithIdentifier, stripDeclKeyword, isWhitespaceChar, collapseNewlineWhitespace } from '@vesk/compiler/src/scan';
@@ -1410,6 +1410,13 @@ function processJSXCallbackBody(source, body) {
1410
1410
  nodes.push(...processJSXChildren(source, [c]));
1411
1411
  return nodes;
1412
1412
  }
1413
+ // A block-bodied callback — `.map((x) => { const y = …; return <jsx/>; })` —
1414
+ // is a statement-mode body: local declarations become runtime statements and
1415
+ // the terminal `return <jsx>` renders the item. Previously the block was
1416
+ // printed verbatim as an expression (`const y = …; return <li>`) and esbuild
1417
+ // rejected it with `Unexpected keyword 'const'`.
1418
+ if (body.type === 'BlockStatement')
1419
+ return processStatementModeBody(source, body.body);
1413
1420
  if (body.type === 'ParenthesizedExpression')
1414
1421
  return exprToIR(source, body.expression);
1415
1422
  return exprToIR(source, body);
@@ -1627,6 +1634,54 @@ function getReturnStatement(node) {
1627
1634
  }
1628
1635
  return null;
1629
1636
  }
1637
+ /**
1638
+ * True when `node`'s subtree contains a `continue`/`break` that targets the
1639
+ * loop `node` belongs to. Descent stops at nested loops (their control
1640
+ * statements bind to them, not to the enclosing loop) and at function
1641
+ * boundaries.
1642
+ */
1643
+ function containsLoopControl(node) {
1644
+ if (!node)
1645
+ return false;
1646
+ if (node.type === 'ContinueStatement' || node.type === 'BreakStatement')
1647
+ return true;
1648
+ if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' ||
1649
+ node.type === 'ArrowFunctionExpression')
1650
+ return false;
1651
+ if (node.type === 'ForStatement' || node.type === 'ForInStatement' ||
1652
+ node.type === 'ForOfStatement' || node.type === 'WhileStatement' ||
1653
+ node.type === 'DoWhileStatement')
1654
+ return false;
1655
+ if (node.type === 'BlockStatement')
1656
+ return node.body.some(containsLoopControl);
1657
+ if (node.type === 'IfStatement')
1658
+ return containsLoopControl(node.consequent) || containsLoopControl(node.alternate);
1659
+ if (node.type === 'SwitchStatement')
1660
+ return node.cases.some((c) => (c.consequent || []).some(containsLoopControl));
1661
+ if (node.type === 'TryStatement') {
1662
+ return containsLoopControl(node.block) || containsLoopControl(node.handler) || containsLoopControl(node.finalizer);
1663
+ }
1664
+ if (node.type === 'CatchClause')
1665
+ return containsLoopControl(node.body);
1666
+ if (node.type === 'LabeledStatement')
1667
+ return containsLoopControl(node.body);
1668
+ return false;
1669
+ }
1670
+ /**
1671
+ * Recognizes an `if (c) continue;` / `if (c) break;` guard (no `else`) and
1672
+ * returns the control node plus any statements that precede it inside the
1673
+ * branch block (`if (c) { foo(); continue; }`).
1674
+ */
1675
+ function getLoopControlGuard(consequent, kind) {
1676
+ const stmts = consequent.type === 'BlockStatement' ? consequent.body : [consequent];
1677
+ if (stmts.length === 0)
1678
+ return null;
1679
+ const want = kind === 'continue' ? 'ContinueStatement' : 'BreakStatement';
1680
+ const last = stmts[stmts.length - 1];
1681
+ if (!last || last.type !== want)
1682
+ return null;
1683
+ return { pre: stmts.slice(0, -1), node: last };
1684
+ }
1630
1685
  function isStatementMode(bodyStmts) {
1631
1686
  if (bodyStmts.some((s) => s.type === 'JSXElement' || s.type === 'JSXExpressionContainer' || s.type === 'JSXFragment'))
1632
1687
  return true;
@@ -1680,6 +1735,15 @@ function processIfStatement(source, stmt) {
1680
1735
  return [new OpaqueDynamicRegion(condExpr, consequent, alternate)];
1681
1736
  }
1682
1737
  function processForStatement(source, stmt, alternate = []) {
1738
+ // A loop with no JSX is pure computation: its value is the side effects it
1739
+ // performs on surrounding bindings. Leaving it as a reactive DOM region would
1740
+ // emit anchors and per-item render closures for no output — and a
1741
+ // `continue`/`break` inside it lands inside those closures as invalid JS
1742
+ // (`Unsyntactic continue`). Emit the loop verbatim instead so its control
1743
+ // flow runs in its original scope.
1744
+ if (stmt.body && !hasJSXInSubtree(stmt.body) && containsLoopControl(stmt.body)) {
1745
+ return [new RuntimeStatement(getSource(source, stmt), stmt, source)];
1746
+ }
1683
1747
  if (stmt.type === 'ForOfStatement') {
1684
1748
  const left = stmt.left;
1685
1749
  let itemVar = 'item';
@@ -1715,6 +1779,9 @@ function processForStatement(source, stmt, alternate = []) {
1715
1779
  return [];
1716
1780
  }
1717
1781
  function processWhileStatement(source, stmt) {
1782
+ if (stmt.body && !hasJSXInSubtree(stmt.body) && containsLoopControl(stmt.body)) {
1783
+ return [new RuntimeStatement(getSource(source, stmt), stmt, source)];
1784
+ }
1718
1785
  const condition = toExpression(source, stmt.test);
1719
1786
  const bodyTemplate = processBlockBody(source, stmt.body);
1720
1787
  const isDoWhile = stmt.type === 'DoWhileStatement';
@@ -1797,6 +1864,25 @@ function processStatementModeBody(source, bodyStmts, filename) {
1797
1864
  nodes.push(new OpaqueDynamicRegion(toExpression(source, stmt.test), consequent, alternate));
1798
1865
  break;
1799
1866
  }
1867
+ // `if (c) continue;` / `if (c) break;` — a loop-body guard. The loop body
1868
+ // is re-emitted as a per-item closure (map) or inline loop, so the control
1869
+ // statement cannot cross the branch closure. Rewrite the remainder of the
1870
+ // iteration into the guard's ELSE branch: `if (c) { pre } else { rest }`.
1871
+ // `continue` renders nothing in the consequent; `break` also sets the
1872
+ // region's break flag (a BreakSignal) so later iterations stop.
1873
+ if (!stmt.alternate) {
1874
+ const guardContinue = getLoopControlGuard(stmt.consequent, 'continue');
1875
+ const guardBreak = guardContinue ? null : getLoopControlGuard(stmt.consequent, 'break');
1876
+ const guard = guardContinue || guardBreak;
1877
+ if (guard) {
1878
+ const consequent = guard.pre.length ? processStatementModeBody(source, guard.pre, filename) : [];
1879
+ if (guardBreak)
1880
+ consequent.push(new BreakSignal());
1881
+ const alternate = processStatementModeBody(source, bodyStmts.slice(i + 1), filename);
1882
+ nodes.push(new OpaqueDynamicRegion(toExpression(source, stmt.test), consequent, alternate));
1883
+ break;
1884
+ }
1885
+ }
1800
1886
  nodes.push(...processIfStatement(source, stmt));
1801
1887
  }
1802
1888
  else if (stmt.type === 'ForOfStatement') {
@@ -1847,6 +1933,17 @@ function processStatementModeBody(source, bodyStmts, filename) {
1847
1933
  const { line, column } = offsetToLineCol(source, stmt.start ?? 0);
1848
1934
  throw VeskError.classDecl({ file: filename || '', line, column, code: codeFrame(source, line, column) });
1849
1935
  }
1936
+ else if (stmt.type === 'ContinueStatement') {
1937
+ // An unconditional `continue` at the top level of a loop body: everything
1938
+ // after it is dead code. Drop the remainder of the body.
1939
+ break;
1940
+ }
1941
+ else if (stmt.type === 'BreakStatement') {
1942
+ // An unconditional `break` at the top level of a loop body: stop the loop
1943
+ // and drop the remainder.
1944
+ nodes.push(new BreakSignal());
1945
+ break;
1946
+ }
1850
1947
  else {
1851
1948
  const raw = getSource(source, stmt);
1852
1949
  if (raw)
@@ -2038,6 +2135,13 @@ function warnStaleConstBindings(comp, file) {
2038
2135
  // pushed authors to inline helpers that were already correct.
2039
2136
  if (d.init.type === 'ArrowFunctionExpression' || d.init.type === 'FunctionExpression')
2040
2137
  continue;
2138
+ // `const x = derived(() => trackedValue)` is the DOCUMENTED fix this very
2139
+ // warning recommends, and it is live by construction. The recursion below
2140
+ // would still reach the tracked read inside the closure and flag it, so
2141
+ // a reactive factory call is excluded for the same reason a bare function
2142
+ // initializer is.
2143
+ if (isReactiveFactoryCall(d.init))
2144
+ continue;
2041
2145
  // A bare identifier initializer is an ALIAS, not a read: `const copy =
2042
2146
  // selCell` re-binds the cell and stays live. Only a read snapshots.
2043
2147
  if (d.init.type === 'Identifier' && reactive.has(d.init.name))
@@ -2094,6 +2198,21 @@ function warnStaleConstBindings(comp, file) {
2094
2198
  });
2095
2199
  }
2096
2200
  /** Every identifier name appearing anywhere in an expression (AST walk). */
2201
+ function isReactiveFactoryCall(node) {
2202
+ if (!node || node.type !== 'CallExpression')
2203
+ return false;
2204
+ const callee = node.callee;
2205
+ if (!callee)
2206
+ return false;
2207
+ // `derived(...)` is auto-imported, so the callee is a bare identifier; also
2208
+ // accept a namespace/dotted call (`$state.derived(...)`) defensively.
2209
+ if (callee.type === 'Identifier')
2210
+ return callee.name === 'derived';
2211
+ if (callee.type === 'MemberExpression' && !callee.computed) {
2212
+ return callee.property?.type === 'Identifier' && callee.property.name === 'derived';
2213
+ }
2214
+ return false;
2215
+ }
2097
2216
  function collectIdentifiers(node, into) {
2098
2217
  if (!node || typeof node !== 'object')
2099
2218
  return;
@@ -2104,6 +2223,21 @@ function collectIdentifiers(node, into) {
2104
2223
  }
2105
2224
  if (typeof node.type !== 'string')
2106
2225
  return;
2226
+ // A NON-computed member property (`learner.classId`) and a non-computed
2227
+ // object/class key (`{ classId: 1 }`) are names, not references. Collecting
2228
+ // them made `const klass = base.find(c => c.id === learner.classId)` look
2229
+ // like it read the tracked `classId` binding, when it only reads the loop
2230
+ // item's field — a false positive on correct per-iteration code.
2231
+ if (node.type === 'MemberExpression' && !node.computed) {
2232
+ collectIdentifiers(node.object, into);
2233
+ if (node.property?.type === 'MemberExpression')
2234
+ collectIdentifiers(node.property, into);
2235
+ return;
2236
+ }
2237
+ if ((node.type === 'Property' || node.type === 'PropertyDefinition' || node.type === 'MethodDefinition') && !node.computed) {
2238
+ collectIdentifiers(node.value, into);
2239
+ return;
2240
+ }
2107
2241
  if (node.type === 'Identifier' && typeof node.name === 'string')
2108
2242
  into.add(node.name);
2109
2243
  for (const key of Object.keys(node)) {
package/dist/ir.d.ts CHANGED
@@ -203,6 +203,21 @@ export declare class RuntimeStatement {
203
203
  source: string | null;
204
204
  constructor(raw: string, ast?: ESTreeNode | null, source?: string | null);
205
205
  }
206
+ /**
207
+ * A `break` targeting the enclosing reactive loop region.
208
+ *
209
+ * A statement-mode loop body is emitted as a per-item render closure (map) or
210
+ * an inline real loop (for/while). A `break` inside a nested `if` region is
211
+ * emitted inside a branch closure and cannot `break` the outer loop. The IR
212
+ * generator rewrites `if (c) break; rest` into `if (c) { <BreakSignal> } else
213
+ * { rest }`; codegen lowers this node to "set the region's break flag and stop
214
+ * emitting the current item" (map) or "set the flag; the loop checks it at the
215
+ * top of each pass" (inline loops). The server emits a native `break;` since
216
+ * its regions are inline.
217
+ */
218
+ export declare class BreakSignal {
219
+ constructor();
220
+ }
206
221
  export declare class ForLoop {
207
222
  init: string;
208
223
  condition: Expression;
@@ -223,5 +238,5 @@ export declare class HeadBlock {
223
238
  children: IRNode[];
224
239
  constructor(children: IRNode[]);
225
240
  }
226
- export type IRNode = StaticNode | TextNode | DynamicBinding | OpaqueDynamicRegion | MapRegion | WhileLoop | SwitchBlock | TryCatch | ForLoop | TrackDecl | RuntimeStatement | ComponentRef | ComponentCall | ServerBlock | ClientBlock | HeadBlock | SlotNode | PropSlot | PropSlotRender;
241
+ export type IRNode = StaticNode | TextNode | DynamicBinding | OpaqueDynamicRegion | MapRegion | WhileLoop | SwitchBlock | TryCatch | ForLoop | TrackDecl | RuntimeStatement | BreakSignal | ComponentRef | ComponentCall | ServerBlock | ClientBlock | HeadBlock | SlotNode | PropSlot | PropSlotRender;
227
242
  //# sourceMappingURL=ir.d.ts.map
package/dist/ir.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ir.d.ts","sourceRoot":"","sources":["../src/ir.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ir.d.ts","sourceRoot":"","sources":["../src/ir.ts"],"names":[],"mappings":"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"}
package/dist/ir.js CHANGED
@@ -254,6 +254,21 @@ export class RuntimeStatement {
254
254
  this.source = source;
255
255
  }
256
256
  }
257
+ /**
258
+ * A `break` targeting the enclosing reactive loop region.
259
+ *
260
+ * A statement-mode loop body is emitted as a per-item render closure (map) or
261
+ * an inline real loop (for/while). A `break` inside a nested `if` region is
262
+ * emitted inside a branch closure and cannot `break` the outer loop. The IR
263
+ * generator rewrites `if (c) break; rest` into `if (c) { <BreakSignal> } else
264
+ * { rest }`; codegen lowers this node to "set the region's break flag and stop
265
+ * emitting the current item" (map) or "set the flag; the loop checks it at the
266
+ * top of each pass" (inline loops). The server emits a native `break;` since
267
+ * its regions are inline.
268
+ */
269
+ export class BreakSignal {
270
+ constructor() { }
271
+ }
257
272
  export class ForLoop {
258
273
  init;
259
274
  condition;
package/dist/parser.d.ts CHANGED
@@ -28,8 +28,18 @@ export interface VeskAnnotation {
28
28
  * Scans the source and blanks `; key <expr>` / `; index <ident>` clauses
29
29
  * found in `for (...)` headers, returning the rewritten code (same length
30
30
  * as the input, so every source offset is preserved) plus annotations.
31
+ *
32
+ * `delimiters` — the real string/template delimiters from
33
+ * `collectStringDelimiters` — is optional. Without it the scan assumes every
34
+ * `"`, `'` and `` ` `` opens a literal, which is wrong for JSX: an apostrophe
35
+ * in text (`<th>Joe Christopher's Standing</th>`) or in an attribute value
36
+ * opens a "string" that runs to the next quote anywhere in the file, so every
37
+ * `for` loop in between is invisible and its `; key …` clause reaches acorn
38
+ * un-blanked as `Unexpected token` at the header. Callers that can afford the
39
+ * tokenization pass it in; when it is unavailable the scan falls back to the
40
+ * plain lexical skip so mid-edit source never crashes it.
31
41
  */
32
- export declare function preprocessForClauses(source: string): {
42
+ export declare function preprocessForClauses(source: string, delimiters?: Set<number> | null): {
33
43
  code: string;
34
44
  annotations: VeskAnnotation[];
35
45
  };
@@ -1 +1 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAOtC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,YAAY,CAAC;IACnB,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA0BD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,cAAc,EAAE,CAAA;CAAE,CA4LpG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAwFlE;AA2DD,wBAAgB,gBAAgB,IAAI,OAAO,KAAK,CAAC,MAAM,CAEtD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAM/D;AAED,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAuEzE"}
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAOtC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,YAAY,CAAC;IACnB,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA0BD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAC9B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,cAAc,EAAE,CAAA;CAAE,CAmMjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAwFlE;AA2DD,wBAAgB,gBAAgB,IAAI,OAAO,KAAK,CAAC,MAAM,CAEtD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAM/D;AAED,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CA6EzE"}
package/dist/parser.js CHANGED
@@ -3,7 +3,7 @@ import { tsPlugin } from './acorn-ts-plugin/index.js';
3
3
  import { VeskParserPlugin } from '@vesk/compiler/src/vesk-plugin';
4
4
  import { blankComments, containsForOfIn } from '@vesk/compiler/src/scan';
5
5
  import { VeskError, codeFrame } from '@vesk/compiler/src/errors';
6
- import { findSpecifierExports, hasTopLevelValueDeclaration } from '@vesk/compiler/src/tokens';
6
+ import { findSpecifierExports, hasTopLevelValueDeclaration, collectStringDelimiters, tokenizeCode } from '@vesk/compiler/src/tokens';
7
7
  function isIdentChar(code) {
8
8
  return ((code >= 97 && code <= 122) || (code >= 65 && code <= 90) || (code >= 48 && code <= 57) || code === 95 || code === 36);
9
9
  }
@@ -28,8 +28,18 @@ function offsetToLineCol(source, offset) {
28
28
  * Scans the source and blanks `; key <expr>` / `; index <ident>` clauses
29
29
  * found in `for (...)` headers, returning the rewritten code (same length
30
30
  * as the input, so every source offset is preserved) plus annotations.
31
+ *
32
+ * `delimiters` — the real string/template delimiters from
33
+ * `collectStringDelimiters` — is optional. Without it the scan assumes every
34
+ * `"`, `'` and `` ` `` opens a literal, which is wrong for JSX: an apostrophe
35
+ * in text (`<th>Joe Christopher's Standing</th>`) or in an attribute value
36
+ * opens a "string" that runs to the next quote anywhere in the file, so every
37
+ * `for` loop in between is invisible and its `; key …` clause reaches acorn
38
+ * un-blanked as `Unexpected token` at the header. Callers that can afford the
39
+ * tokenization pass it in; when it is unavailable the scan falls back to the
40
+ * plain lexical skip so mid-edit source never crashes it.
31
41
  */
32
- export function preprocessForClauses(source) {
42
+ export function preprocessForClauses(source, delimiters) {
33
43
  const annotations = [];
34
44
  const chars = source.split('');
35
45
  const isString = (ch) => ch === '"' || ch === "'" || ch === '`';
@@ -38,6 +48,13 @@ export function preprocessForClauses(source) {
38
48
  const ch = source[i];
39
49
  // Skip strings and comments (nested backtick templates keep depth).
40
50
  if (isString(ch)) {
51
+ // A quote the tokenizer says is not a delimiter is literal JSX text (or
52
+ // a quote inside a literal); step over the single character instead of
53
+ // swallowing the rest of the file.
54
+ if (delimiters && !delimiters.has(i)) {
55
+ i++;
56
+ continue;
57
+ }
41
58
  const quote = ch;
42
59
  let j = i + 1;
43
60
  let tplDepth = 0;
@@ -503,15 +520,21 @@ export function parseGeneratedJs(source) {
503
520
  }
504
521
  export function parse(source, options = {}) {
505
522
  const ParserClass = createBaseParser();
506
- const { code, annotations } = preprocessForClauses(blankGenericArrowTypeParams(blankComments(source)));
523
+ // Tokenize once for both delimiter detection and specifier-export filtering
524
+ // to avoid duplicate lexing/parsing overhead.
525
+ const sourceTokens = tokenizeCode(source);
526
+ const delimiters = sourceTokens === null
527
+ ? null
528
+ : collectStringDelimiters(source, sourceTokens);
529
+ const { code, annotations } = preprocessForClauses(blankGenericArrowTypeParams(blankComments(source)), delimiters);
507
530
  // Acorn validates that every `export { X }` names a real top-level binding.
508
531
  // A `.vsk` component is a registry entry, not a binding, so acorn rejects
509
532
  // `export { MyComponent }`. Remove only those specifier lists — one whose
510
533
  // local IS a real declaration keeps native module semantics — and carry the
511
534
  // pairs on the AST; the IR generator turns them into registry aliases.
512
535
  const vskSource = !options.filename || String(options.filename).endsWith('.vsk');
513
- const specifierExports = vskSource
514
- ? findSpecifierExports(code).filter((e) => !hasTopLevelValueDeclaration(code, e.local))
536
+ const specifierExports = vskSource && sourceTokens !== null
537
+ ? findSpecifierExports(code, sourceTokens).filter((e) => !hasTopLevelValueDeclaration(code, e.local, sourceTokens))
515
538
  : [];
516
539
  let parseable = code;
517
540
  if (specifierExports.length > 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"server-jsgen.d.ts","sourceRoot":"","sources":["../src/server-jsgen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAOzE,OAAO,EAAkG,KAAK,WAAW,EAAE,MAAM,mCAAmC,CAAC;AA6DrK,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,GAAE,OAAe,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,CA4EjJ;AAobD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAwD1F;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAuFD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAkCzE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAYhG"}
1
+ {"version":3,"file":"server-jsgen.d.ts","sourceRoot":"","sources":["../src/server-jsgen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAQzE,OAAO,EAAkG,KAAK,WAAW,EAAE,MAAM,mCAAmC,CAAC;AA6DrK,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,GAAE,OAAe,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,CA+EjJ;AAqbD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAwD1F;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAuFD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAkCzE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAYhG"}
@@ -1,4 +1,4 @@
1
- import { Expression, StaticNode, TextNode, DynamicBinding, OpaqueDynamicRegion, MapRegion, WhileLoop, SwitchBlock, TryCatch, ForLoop, TrackDecl, RuntimeStatement, ComponentRef, ComponentCall, ServerBlock, ClientBlock, HeadBlock, SlotNode, PropSlot, PropSlotRender, } from '@vesk/compiler/src/ir';
1
+ import { Expression, StaticNode, TextNode, DynamicBinding, OpaqueDynamicRegion, MapRegion, WhileLoop, SwitchBlock, TryCatch, ForLoop, TrackDecl, RuntimeStatement, ComponentRef, ComponentCall, BreakSignal, ServerBlock, ClientBlock, HeadBlock, SlotNode, PropSlot, PropSlotRender, } from '@vesk/compiler/src/ir';
2
2
  import { isStaticIR, collectTrackedNames, transformTracked, transformTrackedInit, semicolonizeStatement } from '@vesk/compiler/src/client-codegen';
3
3
  import { unwrapTrackCall, stripTrackGeneric, hasTopLevelComma, skipWhitespace, findBalancedEnd, startsWithIdentifier, callsIdentifier } from '@vesk/compiler/src/scan';
4
4
  import { escapeHtml, indent, exprJS, extractTopLevelNames, extractRuntimeNames, buildParamInit, VOID_ELEMENTS, __vskHydrate, __vskMarkerless, __vskImportedNames, setVskImportedNames, setVskForceClaim, takeVskForceClaim, nextVskId, } from '@vesk/compiler/src/server-utils';
@@ -125,6 +125,10 @@ export function irNodeToJS(node, importedNames, isAsync = false, tracked) {
125
125
  }
126
126
  return semicolonizeStatement(transformTracked(node, tracked || new Map()));
127
127
  }
128
+ // SSR regions are emitted inline inside a real loop, so a break signal is a
129
+ // native `break;` — it exits the loop the item body was emitted into.
130
+ if (node instanceof BreakSignal)
131
+ return `break;`;
128
132
  if (node instanceof SlotNode) {
129
133
  // Markerless mode drops the slot boundary comments: SSR output is plain
130
134
  // HTML and slot claims scope structurally (createLayoutSlot subject to the
@@ -343,15 +347,16 @@ function mapRegionToJS(node, isAsync = false, tracked) {
343
347
  }
344
348
  const loopArr = arrVar || arr;
345
349
  if (node.indexVariable) {
346
- lines.push('let __i = 0;');
350
+ const counter = `__i${nextVskId()}`;
351
+ lines.push(`let ${counter} = 0;`);
347
352
  lines.push(`for (const ${item} of ${loopArr}) {`);
348
- lines.push(indent(`const ${node.indexVariable} = __i;`));
353
+ lines.push(indent(`const ${node.indexVariable} = ${counter};`));
349
354
  for (const n of bodyForItem) {
350
355
  const code = irNodeToJS(n, null, isAsync, tracked);
351
356
  if (code)
352
357
  lines.push(indent(code));
353
358
  }
354
- lines.push(indent('__i++;'));
359
+ lines.push(indent(`${counter}++;`));
355
360
  lines.push(`}`);
356
361
  }
357
362
  else {
package/dist/tokens.d.ts CHANGED
@@ -20,13 +20,36 @@ export interface CodeToken {
20
20
  * their span so callers can read the actual character via `code[start]`.
21
21
  */
22
22
  export declare function tokenizeCode(code: string): CodeToken[] | null;
23
+ /**
24
+ * Returns the set of offsets at which a `"`, `'` or `` ` `` in `code` really
25
+ * opens a quoted literal, or `null` when the code cannot be tokenized.
26
+ *
27
+ * A character-level scanner cannot tell an apostrophe in JSX text
28
+ * (`<th>Joe Christopher's Standing</th>`) or inside an attribute value
29
+ * (`class="Bob's"`) from the opening quote of a JavaScript string, and the
30
+ * mistake is unbounded: the scanner skips to the next quote it can find — or to
31
+ * end of file — and everything in between is invisible to it. That is how the
32
+ * `; key …` clause of a `for` loop could go un-blanked and surface as
33
+ * `Unexpected token` at a header hundreds of lines away from the real cause.
34
+ *
35
+ * The tokenizer has no such ambiguity, so this hands the decision to it. A
36
+ * quote is a delimiter when a quoted-literal token starts there (ordinary JS
37
+ * strings and template backticks) or when no token covers it at all — which is
38
+ * how a JSX attribute value arrives, since acorn's JSX reader consumes the
39
+ * attribute's opening quote before tokenizing its content.
40
+ *
41
+ * Callers must fall back to their own lexical skip when this returns `null`;
42
+ * tokenizing fails on input that is mid-edit or otherwise unparsable, and the
43
+ * character scanners have to keep working there.
44
+ */
45
+ export declare function collectStringDelimiters(code: string, tokens?: CodeToken[] | null): Set<number> | null;
23
46
  /**
24
47
  * Returns the set of identifiers in `code` that are called — an identifier
25
48
  * token immediately followed by `(` or by a `<...>` generic clause and then
26
49
  * `(`. Member accesses (`obj.fn(`, `obj?.fn(`) are excluded so methods are
27
50
  * not mistaken for imported runtime functions.
28
51
  */
29
- export declare function collectCalledIdentifiers(code: string): Set<string>;
52
+ export declare function collectCalledIdentifiers(code: string, tokens?: CodeToken[] | null): Set<string>;
30
53
  /**
31
54
  * True when `code` contains the identifier `name` (as a plain identifier or
32
55
  * a JSX name), outside strings and comments.
@@ -53,7 +76,7 @@ export declare function importModuleTarget(importText: string): string | null;
53
76
  * with no binding — apart from an ordinary JS/TS declaration, whose
54
77
  * `export { name }` must keep native module semantics.
55
78
  */
56
- export declare function hasTopLevelValueDeclaration(code: string, name: string): boolean;
79
+ export declare function hasTopLevelValueDeclaration(code: string, name: string, tokens?: CodeToken[] | null): boolean;
57
80
  /** One entry of a same-file `export { A, B as C }` specifier list. */
58
81
  export interface VskSpecifierExport {
59
82
  local: string;
@@ -72,7 +95,7 @@ export interface VskSpecifierExport {
72
95
  * `export const A = 1`) and re-exports (`export … from '…'`) are left alone:
73
96
  * those parse fine and are handled in the IR generator.
74
97
  */
75
- export declare function findSpecifierExports(code: string): VskSpecifierExport[];
98
+ export declare function findSpecifierExports(code: string, tokens?: CodeToken[] | null): VskSpecifierExport[];
76
99
  /** `text` must be `source.slice(base, …)`; `node` is AST parsed from `source`. */
77
100
  export declare function stripTrackDeclMarkers(text: string, base: number, node: any): string;
78
101
  //# sourceMappingURL=tokens.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,IAAI,CA0B7D;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CA2BlE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAStE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAqBxE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAuC/D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOpE;AAwID;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAkB/E;AAED,sEAAsE;AACtE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAkCvE;AA6DD,kFAAkF;AAClF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,MAAM,CAcnF"}
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,IAAI,CA0B7D;AAKD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAerG;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,CA2B/F;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAStE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAqBxE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAuC/D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOpE;AAwID;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,GAAG,OAAO,CAkB5G;AAED,sEAAsE;AACtE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,GAAG,kBAAkB,EAAE,CAkCpG;AA6DD,kFAAkF;AAClF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,MAAM,CAcnF"}
package/dist/tokens.js CHANGED
@@ -44,26 +44,70 @@ export function tokenizeCode(code) {
44
44
  }
45
45
  });
46
46
  }
47
+ /** Token labels whose source text is a quoted literal (string or template). */
48
+ const QUOTED_LABELS = new Set(['string', 'template', '`', 'backQuote']);
49
+ /**
50
+ * Returns the set of offsets at which a `"`, `'` or `` ` `` in `code` really
51
+ * opens a quoted literal, or `null` when the code cannot be tokenized.
52
+ *
53
+ * A character-level scanner cannot tell an apostrophe in JSX text
54
+ * (`<th>Joe Christopher's Standing</th>`) or inside an attribute value
55
+ * (`class="Bob's"`) from the opening quote of a JavaScript string, and the
56
+ * mistake is unbounded: the scanner skips to the next quote it can find — or to
57
+ * end of file — and everything in between is invisible to it. That is how the
58
+ * `; key …` clause of a `for` loop could go un-blanked and surface as
59
+ * `Unexpected token` at a header hundreds of lines away from the real cause.
60
+ *
61
+ * The tokenizer has no such ambiguity, so this hands the decision to it. A
62
+ * quote is a delimiter when a quoted-literal token starts there (ordinary JS
63
+ * strings and template backticks) or when no token covers it at all — which is
64
+ * how a JSX attribute value arrives, since acorn's JSX reader consumes the
65
+ * attribute's opening quote before tokenizing its content.
66
+ *
67
+ * Callers must fall back to their own lexical skip when this returns `null`;
68
+ * tokenizing fails on input that is mid-edit or otherwise unparsable, and the
69
+ * character scanners have to keep working there.
70
+ */
71
+ export function collectStringDelimiters(code, tokens) {
72
+ const t = tokens == null ? tokenizeCode(code) : tokens;
73
+ if (t === null)
74
+ return null;
75
+ const starts = new Set();
76
+ // `t` is ordered by offset, so one pointer answers "is this offset
77
+ // inside a token?" for the whole source without materialising a bitmap.
78
+ let ti = 0;
79
+ for (let i = 0; i < code.length; i++) {
80
+ const c = code.charCodeAt(i);
81
+ if (c !== 34 && c !== 39 && c !== 96)
82
+ continue;
83
+ while (ti < t.length && t[ti].end <= i)
84
+ ti++;
85
+ const covered = ti < t.length && t[ti].start <= i;
86
+ if (!covered || QUOTED_LABELS.has(t[ti].label))
87
+ starts.add(i);
88
+ }
89
+ return starts;
90
+ }
47
91
  /**
48
92
  * Returns the set of identifiers in `code` that are called — an identifier
49
93
  * token immediately followed by `(` or by a `<...>` generic clause and then
50
94
  * `(`. Member accesses (`obj.fn(`, `obj?.fn(`) are excluded so methods are
51
95
  * not mistaken for imported runtime functions.
52
96
  */
53
- export function collectCalledIdentifiers(code) {
54
- const tokens = tokenizeCode(code);
55
- if (tokens !== null) {
97
+ export function collectCalledIdentifiers(code, tokens) {
98
+ const t = tokens == null ? tokenizeCode(code) : tokens;
99
+ if (t !== null) {
56
100
  const result = new Set();
57
- for (let i = 0; i < tokens.length; i++) {
58
- const t = tokens[i];
59
- if (t.label !== 'name')
101
+ for (let i = 0; i < t.length; i++) {
102
+ const tok = t[i];
103
+ if (tok.label !== 'name')
60
104
  continue;
61
- const prev = i > 0 ? tokens[i - 1] : null;
105
+ const prev = i > 0 ? t[i - 1] : null;
62
106
  if (prev && (prev.label === '.' || prev.label === '?.')) {
63
107
  // Member calls (`useFetch.text(`) surface the receiver so an
64
108
  // auto-importable function used as a receiver is still imported.
65
- const obj = i >= 2 ? tokens[i - 2] : null;
66
- const next = tokens[i + 1];
109
+ const obj = i >= 2 ? t[i - 2] : null;
110
+ const next = t[i + 1];
67
111
  if (obj && obj.label === 'name' && next) {
68
112
  const nextCh = code[next.start];
69
113
  if (nextCh === '(' || nextCh === '<')
@@ -71,12 +115,12 @@ export function collectCalledIdentifiers(code) {
71
115
  }
72
116
  continue;
73
117
  }
74
- const next = tokens[i + 1];
118
+ const next = t[i + 1];
75
119
  if (!next)
76
120
  continue;
77
121
  const nextCh = code[next.start];
78
122
  if (nextCh === '(' || nextCh === '<')
79
- result.add(t.value);
123
+ result.add(tok.value);
80
124
  }
81
125
  return result;
82
126
  }
@@ -367,22 +411,22 @@ function manualExtractImportNames(importText) {
367
411
  * with no binding — apart from an ordinary JS/TS declaration, whose
368
412
  * `export { name }` must keep native module semantics.
369
413
  */
370
- export function hasTopLevelValueDeclaration(code, name) {
371
- const tokens = tokenizeCode(code);
372
- if (tokens === null)
414
+ export function hasTopLevelValueDeclaration(code, name, tokens) {
415
+ const t = tokens == null ? tokenizeCode(code) : tokens;
416
+ if (t === null)
373
417
  return false;
374
418
  const declWords = ['const', 'let', 'var', 'function', 'class'];
375
- for (let i = 1; i < tokens.length; i++) {
376
- if (tokens[i].label !== 'name' || tokens[i].value !== name)
419
+ for (let i = 1; i < t.length; i++) {
420
+ if (t[i].label !== 'name' || t[i].value !== name)
377
421
  continue;
378
- const prev = tokens[i - 1];
422
+ const prev = t[i - 1];
379
423
  // Declaration keywords carry their own token label (`const`, `var`,
380
424
  // `function`, `class`); `let` arrives as a plain name. Match on value.
381
425
  if (!prev || !declWords.includes(prev.value))
382
426
  continue;
383
427
  // `export const X` / `export function X` are preceded by `export`.
384
428
  if (i >= 2) {
385
- const pp = tokens[i - 2];
429
+ const pp = t[i - 2];
386
430
  if (pp && pp.label === 'name' && pp.value === 'export')
387
431
  continue;
388
432
  }
@@ -400,24 +444,24 @@ export function hasTopLevelValueDeclaration(code, name) {
400
444
  * `export const A = 1`) and re-exports (`export … from '…'`) are left alone:
401
445
  * those parse fine and are handled in the IR generator.
402
446
  */
403
- export function findSpecifierExports(code) {
404
- const tokens = tokenizeCode(code);
405
- if (tokens === null)
447
+ export function findSpecifierExports(code, tokens) {
448
+ const t = tokens == null ? tokenizeCode(code) : tokens;
449
+ if (t === null)
406
450
  return [];
407
451
  const out = [];
408
- for (let i = 0; i < tokens.length; i++) {
409
- if (tokens[i].label !== 'export')
452
+ for (let i = 0; i < t.length; i++) {
453
+ if (t[i].label !== 'export')
410
454
  continue;
411
455
  // A specifier list starts with `{`. `export default`, `export const` and
412
456
  // `export *` take another route and are left for the IR generator.
413
- if (!tokens[i + 1] || tokens[i + 1].label !== '{')
457
+ if (!t[i + 1] || t[i + 1].label !== '{')
414
458
  continue;
415
459
  // Find this statement's `}` and note any module source, which makes it a
416
460
  // re-export (`export { A } from './x.vsk'`) rather than a specifier list.
417
461
  let k = i + 2;
418
462
  let depth = 1;
419
- for (; k < tokens.length; k++) {
420
- const lbl = tokens[k].label;
463
+ for (; k < t.length; k++) {
464
+ const lbl = t[k].label;
421
465
  if (lbl === 'string') {
422
466
  continue;
423
467
  }
@@ -431,8 +475,8 @@ export function findSpecifierExports(code) {
431
475
  }
432
476
  // A re-export puts `from '…'` AFTER the closing brace, so check past it.
433
477
  let isReexport = false;
434
- for (let j = k + 1; j < tokens.length; j++) {
435
- const lbl = tokens[j].label;
478
+ for (let j = k + 1; j < t.length; j++) {
479
+ const lbl = t[j].label;
436
480
  if (lbl === 'string') {
437
481
  isReexport = true;
438
482
  break;
@@ -442,12 +486,12 @@ export function findSpecifierExports(code) {
442
486
  }
443
487
  if (depth !== 0 || isReexport)
444
488
  continue;
445
- const pairs = readSpecifierPairs(code, tokens[i + 1].end, tokens[k].end);
489
+ const pairs = readSpecifierPairs(code, t[i + 1].end, t[k].end);
446
490
  if (pairs === null)
447
491
  continue;
448
- const stmtEnd = tokens[k].end;
492
+ const stmtEnd = t[k].end;
449
493
  for (const [local, exported] of pairs)
450
- out.push({ local, exported, start: tokens[i].start, end: stmtEnd });
494
+ out.push({ local, exported, start: t[i].start, end: stmtEnd });
451
495
  i = k;
452
496
  }
453
497
  return out;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vesk/compiler",
3
- "version": "0.2.59",
3
+ "version": "0.2.61",
4
4
  "type": "module",
5
5
  "description": "Vesk compiler — lexer, parser, semantic analysis, IR, codegen",
6
6
  "main": "./dist/index.js",
@@ -20,7 +20,7 @@
20
20
  },
21
21
  "dependencies": {
22
22
  "@sveltejs/acorn-typescript": "^1.0.0",
23
- "@vesk/types": "^0.2.59",
23
+ "@vesk/types": "^0.2.61",
24
24
  "acorn": "^8.15.0",
25
25
  "esrap": "^2.3.0",
26
26
  "is-reference": "^3.0.0",