@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.
- package/dist/client-codegen.d.ts.map +1 -1
- package/dist/client-codegen.js +143 -7
- package/dist/ir-generator.d.ts.map +1 -1
- package/dist/ir-generator.js +135 -1
- package/dist/ir.d.ts +16 -1
- package/dist/ir.d.ts.map +1 -1
- package/dist/ir.js +15 -0
- package/dist/parser.d.ts +11 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +28 -5
- package/dist/server-jsgen.d.ts.map +1 -1
- package/dist/server-jsgen.js +9 -4
- package/dist/tokens.d.ts +26 -3
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +75 -31
- package/package.json +2 -2
|
@@ -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,
|
|
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"}
|
package/dist/client-codegen.js
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/ir-generator.js
CHANGED
|
@@ -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
|
};
|
package/dist/parser.d.ts.map
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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;
|
|
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"}
|
package/dist/server-jsgen.js
CHANGED
|
@@ -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
|
-
|
|
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} =
|
|
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(
|
|
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
|
package/dist/tokens.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
|
55
|
-
if (
|
|
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 <
|
|
58
|
-
const
|
|
59
|
-
if (
|
|
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 ?
|
|
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 ?
|
|
66
|
-
const next =
|
|
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 =
|
|
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(
|
|
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
|
|
372
|
-
if (
|
|
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 <
|
|
376
|
-
if (
|
|
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 =
|
|
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 =
|
|
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
|
|
405
|
-
if (
|
|
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 <
|
|
409
|
-
if (
|
|
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 (!
|
|
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 <
|
|
420
|
-
const lbl =
|
|
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 <
|
|
435
|
-
const lbl =
|
|
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,
|
|
489
|
+
const pairs = readSpecifierPairs(code, t[i + 1].end, t[k].end);
|
|
446
490
|
if (pairs === null)
|
|
447
491
|
continue;
|
|
448
|
-
const stmtEnd =
|
|
492
|
+
const stmtEnd = t[k].end;
|
|
449
493
|
for (const [local, exported] of pairs)
|
|
450
|
-
out.push({ local, exported, start:
|
|
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.
|
|
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.
|
|
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",
|