@vesk/compiler 0.2.92 → 0.2.94
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/acorn-ts-plugin/index.js +1 -0
- package/dist/client-codegen.d.ts.map +1 -1
- package/dist/client-codegen.js +136 -29
- package/dist/errors.d.ts +28 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +133 -25
- package/dist/ir-generator.d.ts.map +1 -1
- package/dist/ir-generator.js +65 -1
- package/dist/module-imports.d.ts +9 -2
- package/dist/module-imports.d.ts.map +1 -1
- package/dist/module-imports.js +191 -44
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +306 -15
- package/dist/precompile.d.ts.map +1 -1
- package/dist/precompile.js +40 -2
- package/dist/server-render.d.ts.map +1 -1
- package/dist/server-render.js +36 -35
- package/package.json +2 -2
|
@@ -774,6 +774,7 @@ function generateJsxParser(acorn, acornTypeScript, Parser, jsxOptions) {
|
|
|
774
774
|
return node;
|
|
775
775
|
case tok.jsxTagStart:
|
|
776
776
|
case tt.string:
|
|
777
|
+
case tt.backQuote:
|
|
777
778
|
return this.parseExprAtom();
|
|
778
779
|
default:
|
|
779
780
|
this.raise(this.start, "JSX value should be either an expression or a quoted JSX text");
|
|
@@ -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,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;AAoFD,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;
|
|
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;AAoFD,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;AA0uED,iBAAS,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAe3C;AAuKD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO9C;AAwED,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
|
@@ -539,6 +539,15 @@ class Ctx {
|
|
|
539
539
|
// item element already claimed by key) instead of walking for a new element.
|
|
540
540
|
rootClaim = false;
|
|
541
541
|
walker = '__hydrate';
|
|
542
|
+
// Registry a hydrate-mode component call must resolve through. The hydrate
|
|
543
|
+
// bundle registers its functions under `__hydrators`, while non-hydrate
|
|
544
|
+
// nested renders (children of self-claiming components, client-only blocks,
|
|
545
|
+
// fresh map items) must keep calling the plain `__components` map. Emitting
|
|
546
|
+
// the hydrate registry name directly avoids the old global
|
|
547
|
+
// `__components` → `__hydrators` text rewrite, which also rewrote those
|
|
548
|
+
// nested fresh-render calls and handed them the 3-argument hydrator with no
|
|
549
|
+
// walker (`Cannot read properties of undefined (reading 'root')`).
|
|
550
|
+
hydrateRegistry = '__hydrators';
|
|
542
551
|
asyncComps = new Set();
|
|
543
552
|
isAsyncScope = false;
|
|
544
553
|
// While emitting a loop body, names the boolean flag a `break` sets (see
|
|
@@ -676,6 +685,7 @@ function emitNode(ctx, node, tracked, effectsVar, parentVar, compPrefix = '__com
|
|
|
676
685
|
ctx.push(` const __slot = createLayoutSlot(${ctx.walker}, ${parentVar});`);
|
|
677
686
|
ctx.push(` const __child = props.children(__slot.walker);`);
|
|
678
687
|
ctx.push(` if (__child && typeof __child.then === 'function') __pendingChild = __slot.track(__child);`);
|
|
688
|
+
ctx.push(` else if (__child && __child.nodeType === 11) ${parentVar}.appendChild(__child);`);
|
|
679
689
|
ctx.push(` } else {`);
|
|
680
690
|
ctx.push(` ${parentVar}.appendChild(props.children);`);
|
|
681
691
|
ctx.push(` }`);
|
|
@@ -1260,6 +1270,65 @@ function emitFragment(ctx, nodes, tracked, effectsVar) {
|
|
|
1260
1270
|
ctx.push(`return $f; })();`);
|
|
1261
1271
|
return frag;
|
|
1262
1272
|
}
|
|
1273
|
+
/**
|
|
1274
|
+
* True when any node in the subtree is a layout `{props.children}` slot.
|
|
1275
|
+
*
|
|
1276
|
+
* A slot must be hydrated on the walker scoped to the element that will
|
|
1277
|
+
* actually contain it. Building the children fragment eagerly on the caller's
|
|
1278
|
+
* walker is only correct when the callee appends that fragment to its own
|
|
1279
|
+
* mount point. A component that forwards children through a scoped container
|
|
1280
|
+
* (e.g. a shell that renders `{props.children}` inside `<main>`) invokes the
|
|
1281
|
+
* child hydrator with a sub-walker; passing an eagerly-built fragment skips
|
|
1282
|
+
* that scope and lets the page claim the shell's own SSR root.
|
|
1283
|
+
*/
|
|
1284
|
+
function containsSlot(nodes) {
|
|
1285
|
+
for (const node of nodes) {
|
|
1286
|
+
if (node instanceof SlotNode)
|
|
1287
|
+
return true;
|
|
1288
|
+
if (node instanceof StaticNode) {
|
|
1289
|
+
if (containsSlot(node.children))
|
|
1290
|
+
return true;
|
|
1291
|
+
}
|
|
1292
|
+
else if (node instanceof ComponentCall) {
|
|
1293
|
+
if (containsSlot(node.children))
|
|
1294
|
+
return true;
|
|
1295
|
+
}
|
|
1296
|
+
else if (node instanceof OpaqueDynamicRegion) {
|
|
1297
|
+
if (containsSlot(node.consequentNodes) || containsSlot(node.alternateNodes))
|
|
1298
|
+
return true;
|
|
1299
|
+
}
|
|
1300
|
+
else if (node instanceof MapRegion) {
|
|
1301
|
+
if (containsSlot(node.bodyTemplate) || containsSlot(node.alternateNodes))
|
|
1302
|
+
return true;
|
|
1303
|
+
}
|
|
1304
|
+
else if (node instanceof WhileLoop) {
|
|
1305
|
+
if (containsSlot(node.bodyTemplate))
|
|
1306
|
+
return true;
|
|
1307
|
+
}
|
|
1308
|
+
else if (node instanceof ForLoop) {
|
|
1309
|
+
if (containsSlot(node.bodyTemplate))
|
|
1310
|
+
return true;
|
|
1311
|
+
}
|
|
1312
|
+
else if (node instanceof SwitchBlock) {
|
|
1313
|
+
for (const c of node.cases)
|
|
1314
|
+
if (containsSlot(c.body))
|
|
1315
|
+
return true;
|
|
1316
|
+
}
|
|
1317
|
+
else if (node instanceof TryCatch) {
|
|
1318
|
+
if (containsSlot(node.bodyTemplate) || containsSlot(node.catchBody))
|
|
1319
|
+
return true;
|
|
1320
|
+
}
|
|
1321
|
+
else if (node instanceof ServerBlock || node instanceof ClientBlock || node instanceof HeadBlock) {
|
|
1322
|
+
if (containsSlot(node.children))
|
|
1323
|
+
return true;
|
|
1324
|
+
}
|
|
1325
|
+
else if (node instanceof PropSlot) {
|
|
1326
|
+
if (containsSlot(node.body))
|
|
1327
|
+
return true;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
return false;
|
|
1331
|
+
}
|
|
1263
1332
|
function emitComponentCall(ctx, node, tracked, effectsVar, parentVar, compPrefix = '__components', skipK = 0, regionBudget) {
|
|
1264
1333
|
const propsEntries = node.props.map((p) => {
|
|
1265
1334
|
if (typeof p.value === 'string')
|
|
@@ -1322,7 +1391,7 @@ function emitComponentCall(ctx, node, tracked, effectsVar, parentVar, compPrefix
|
|
|
1322
1391
|
const access = calleeExpr
|
|
1323
1392
|
?? (ctx.importedNames.has(node.componentName)
|
|
1324
1393
|
? node.componentName
|
|
1325
|
-
:
|
|
1394
|
+
: `${ctx.hydrateRegistry}[${JSON.stringify(node.componentName)}]`);
|
|
1326
1395
|
// Registry lookups can resolve to `undefined` when a JSX tag names a
|
|
1327
1396
|
// component that was never declared, imported, or registered (e.g.
|
|
1328
1397
|
// `<Slot/>` in a framework where `{props.children}` is the channel).
|
|
@@ -1397,20 +1466,56 @@ function emitComponentCall(ctx, node, tracked, effectsVar, parentVar, compPrefix
|
|
|
1397
1466
|
// fresh and let the runtime component adopt the existing root.
|
|
1398
1467
|
if (wipeMount || ctx.selfClaimNames.has(node.componentName))
|
|
1399
1468
|
ctx.hydrate = false;
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1469
|
+
// A slot nested under a compiled component must be built by that
|
|
1470
|
+
// component's scoped walker, not the caller's. Pass children as a lazy
|
|
1471
|
+
// hydrator so the callee can invoke it with the walker for the element
|
|
1472
|
+
// that actually contains the slot (e.g. `<main>` in a shell). Eagerly
|
|
1473
|
+
// building the fragment here hydrates the page against the shell's own
|
|
1474
|
+
// SSR root, which detaches the shell on hydration.
|
|
1475
|
+
const lazySlotChildren = ctx.hydrate && !plainTarget && !ctx.selfClaimNames.has(node.componentName) && containsSlot(regChildren);
|
|
1476
|
+
if (lazySlotChildren) {
|
|
1477
|
+
const fnVar = ctx.n();
|
|
1478
|
+
const savedLines = ctx.lines;
|
|
1479
|
+
const savedEffects = ctx.effects;
|
|
1480
|
+
const savedWalker = ctx.walker;
|
|
1481
|
+
ctx.lines = [];
|
|
1482
|
+
ctx.effects = [];
|
|
1483
|
+
ctx.walker = '__childWalker';
|
|
1484
|
+
ctx.push(`const $f = document.createDocumentFragment();`);
|
|
1485
|
+
for (const child of regChildren) {
|
|
1486
|
+
const childVar = emitNode(ctx, child, tracked, effectsVar, '$f');
|
|
1487
|
+
if (childVar)
|
|
1488
|
+
ctx.push(`$f.appendChild(${childVar});`);
|
|
1489
|
+
}
|
|
1490
|
+
for (const eff of ctx.effects)
|
|
1491
|
+
ctx.push(effectsVar ? `${effectsVar}.push(${effectBlockToHandlerExpr(eff)});` : eff);
|
|
1492
|
+
ctx.push(`return $f;`);
|
|
1493
|
+
const body = ctx.lines.join('\n');
|
|
1494
|
+
ctx.lines = savedLines;
|
|
1495
|
+
ctx.effects = savedEffects;
|
|
1496
|
+
ctx.walker = savedWalker;
|
|
1497
|
+
const asyncKw = ctx.isAsyncScope ? 'async ' : '';
|
|
1498
|
+
ctx.push(`const ${fnVar} = ${asyncKw}(__childWalker) => {`);
|
|
1499
|
+
ctx.push(indent(body));
|
|
1500
|
+
ctx.push(`};`);
|
|
1501
|
+
propsEntries.push(`children: ${fnVar}`);
|
|
1502
|
+
}
|
|
1503
|
+
else {
|
|
1504
|
+
ctx.push(`const ${frag} = (() => { const $f = document.createDocumentFragment();`);
|
|
1505
|
+
const savedEffects = ctx.effects;
|
|
1506
|
+
ctx.effects = [];
|
|
1507
|
+
for (const child of regChildren) {
|
|
1508
|
+
const childVar = emitNode(ctx, child, tracked, effectsVar, '$f');
|
|
1509
|
+
if (childVar)
|
|
1510
|
+
ctx.push(`$f.appendChild(${childVar});`);
|
|
1511
|
+
}
|
|
1512
|
+
for (const eff of ctx.effects)
|
|
1513
|
+
ctx.push(effectsVar ? `${effectsVar}.push(${effectBlockToHandlerExpr(eff)});` : eff);
|
|
1514
|
+
ctx.effects = savedEffects;
|
|
1515
|
+
ctx.push(`return $f; })();`);
|
|
1516
|
+
propsEntries.push(`children: ${frag}`);
|
|
1407
1517
|
}
|
|
1408
|
-
for (const eff of ctx.effects)
|
|
1409
|
-
ctx.push(effectsVar ? `${effectsVar}.push(${effectBlockToHandlerExpr(eff)});` : eff);
|
|
1410
|
-
ctx.effects = savedEffects;
|
|
1411
1518
|
ctx.hydrate = savedHydrate;
|
|
1412
|
-
ctx.push(`return $f; })();`);
|
|
1413
|
-
propsEntries.push(`children: ${frag}`);
|
|
1414
1519
|
}
|
|
1415
1520
|
// Deposit the residue for the child's first top-level claim (it runs on
|
|
1416
1521
|
// this same walker with its own zero `topPendingSkip`). A region budget is
|
|
@@ -1658,7 +1763,7 @@ function emitTryCatch(ctx, node, tracked, effectsVar, parentVar, skipK = 0, regi
|
|
|
1658
1763
|
${effArr}.length = 0;
|
|
1659
1764
|
__cleanup(${anchor}, ${endAnchor});
|
|
1660
1765
|
try { ${asyncKw}${tryRenderComp}(); } catch(${catchParam}) { ${hasCatch ? `${asyncKw}${catchRenderComp}(${catchParam});` : 'throw ${catchParam};'} }
|
|
1661
|
-
});
|
|
1766
|
+
}, true);
|
|
1662
1767
|
}`);
|
|
1663
1768
|
return null;
|
|
1664
1769
|
}
|
|
@@ -1757,7 +1862,7 @@ function emitOpaque(ctx, node, tracked, effTarget, parentVar, skipK = 0, regionB
|
|
|
1757
1862
|
${effInner}
|
|
1758
1863
|
__iv = __new;
|
|
1759
1864
|
}
|
|
1760
|
-
});`;
|
|
1865
|
+
}, true);`;
|
|
1761
1866
|
if (effTarget) {
|
|
1762
1867
|
ctx.push(`${effTarget}.push((() => {
|
|
1763
1868
|
let __iv = ${branchInit};
|
|
@@ -1858,7 +1963,7 @@ function emitOpaque(ctx, node, tracked, effTarget, parentVar, skipK = 0, regionB
|
|
|
1858
1963
|
${renderBranch}
|
|
1859
1964
|
__iv = __nv;
|
|
1860
1965
|
}
|
|
1861
|
-
});`;
|
|
1966
|
+
}, true);`;
|
|
1862
1967
|
// A static condition renders once; an effect referencing it would only ever
|
|
1863
1968
|
// reference statement/loop-local bindings that are out of scope at the flush
|
|
1864
1969
|
// site (e.g. `const pages = …; if (pages.length > 0)` inside a for-of body).
|
|
@@ -1964,7 +2069,7 @@ function emitWhileLoop(ctx, node, tracked, parentVar, skipK = 0, regionBudget) {
|
|
|
1964
2069
|
__busy = false;
|
|
1965
2070
|
__iv = (${condExpr});
|
|
1966
2071
|
}
|
|
1967
|
-
});
|
|
2072
|
+
}, true);
|
|
1968
2073
|
}`);
|
|
1969
2074
|
return null;
|
|
1970
2075
|
}
|
|
@@ -2078,7 +2183,7 @@ function emitForLoop(ctx, node, tracked, parentVar, skipK = 0, regionBudget) {
|
|
|
2078
2183
|
__busy = false;
|
|
2079
2184
|
__iv = (${srcExpr});
|
|
2080
2185
|
}
|
|
2081
|
-
});
|
|
2186
|
+
}, true);
|
|
2082
2187
|
}`);
|
|
2083
2188
|
}
|
|
2084
2189
|
else {
|
|
@@ -2147,7 +2252,7 @@ function emitForLoop(ctx, node, tracked, parentVar, skipK = 0, regionBudget) {
|
|
|
2147
2252
|
__cleanup(${anchor}, ${endAnchor});
|
|
2148
2253
|
${ctx.isAsyncScope ? 'await ' : ''}${renderLoop}();
|
|
2149
2254
|
__busy = false;
|
|
2150
|
-
});
|
|
2255
|
+
}, true);
|
|
2151
2256
|
}`);
|
|
2152
2257
|
}
|
|
2153
2258
|
}
|
|
@@ -2224,7 +2329,7 @@ function emitSwitchBlock(ctx, node, tracked, parentVar, skipK = 0, regionBudget)
|
|
|
2224
2329
|
__busy = false;
|
|
2225
2330
|
__iv = (${discExpr});
|
|
2226
2331
|
}
|
|
2227
|
-
});
|
|
2332
|
+
}, true);
|
|
2228
2333
|
}`);
|
|
2229
2334
|
return null;
|
|
2230
2335
|
}
|
|
@@ -2447,7 +2552,7 @@ function emitMap(ctx, node, tracked, effTarget, parentVar, skipK = 0, regionBudg
|
|
|
2447
2552
|
${effectsVar}.length = 0;
|
|
2448
2553
|
__cleanup(${anchor}, ${endAnchor});
|
|
2449
2554
|
if (__new) { ${initList}(); } else { ${aw}${emptyRenderName}(); }
|
|
2450
|
-
})`);
|
|
2555
|
+
}, true)`);
|
|
2451
2556
|
}
|
|
2452
2557
|
else {
|
|
2453
2558
|
ctx.push(`${initList}();`);
|
|
@@ -2457,7 +2562,7 @@ function emitMap(ctx, node, tracked, effTarget, parentVar, skipK = 0, regionBudg
|
|
|
2457
2562
|
const __nv = ${arrExpr};
|
|
2458
2563
|
if (__first) { __first = false; __nv; ${breakReset}${reconciler}(${arrExpr}); return; }
|
|
2459
2564
|
${breakVar ? `if (${breakVar}) ${breakVar} = false;` : ''}${reconciler}(${arrExpr});
|
|
2460
|
-
})`);
|
|
2565
|
+
}, true)`);
|
|
2461
2566
|
}
|
|
2462
2567
|
}
|
|
2463
2568
|
else {
|
|
@@ -2515,7 +2620,7 @@ ${renderAllItems(' ', null)}
|
|
|
2515
2620
|
if (__new) {
|
|
2516
2621
|
${renderAllItems(' ', null)}
|
|
2517
2622
|
} else { ${aw}${emptyRenderName}(${collectVar ? collectVar : ''}); }
|
|
2518
|
-
})`);
|
|
2623
|
+
}, true)`);
|
|
2519
2624
|
}
|
|
2520
2625
|
else {
|
|
2521
2626
|
const collectVar = hyd ? ctx.n() : null;
|
|
@@ -2533,7 +2638,7 @@ ${renderAllItems(' ', null)}
|
|
|
2533
2638
|
${effectsVar}.length = 0;
|
|
2534
2639
|
__cleanup(${anchor}, ${endAnchor});
|
|
2535
2640
|
${renderAllItems(' ', null)}
|
|
2536
|
-
})`);
|
|
2641
|
+
}, true)`);
|
|
2537
2642
|
}
|
|
2538
2643
|
}
|
|
2539
2644
|
return null;
|
|
@@ -2746,7 +2851,8 @@ function buildParamInit(paramNames, propsAlias = null) {
|
|
|
2746
2851
|
}
|
|
2747
2852
|
function buildComponentMap(irRoot, hydrate = false, alloc, markerless = true) {
|
|
2748
2853
|
const mapLines = [];
|
|
2749
|
-
|
|
2854
|
+
const registry = hydrate ? '__hydrators' : '__components';
|
|
2855
|
+
mapLines.push(`const ${registry} = {};`);
|
|
2750
2856
|
const asyncComps = computeAsyncComponents(irRoot.components);
|
|
2751
2857
|
// Wipe-style router components (Link/NavLink) replace their SSR children on
|
|
2752
2858
|
// hydration, so their children fragments must be rebuilt in full. Track the
|
|
@@ -2788,11 +2894,11 @@ function buildComponentMap(irRoot, hydrate = false, alloc, markerless = true) {
|
|
|
2788
2894
|
? `__hydrate.claimOnly(undefined, (typeof __hydrate.takeSkipK === 'function' ? __hydrate.takeSkipK() : 0))`
|
|
2789
2895
|
: `__hydrate.claimOnly()`;
|
|
2790
2896
|
const stub = `(props, __registry, __hydrate) => { return ${claimExpr}; }`;
|
|
2791
|
-
mapLines.push(
|
|
2897
|
+
mapLines.push(`${registry}[${JSON.stringify(comp.name)}] = ${stub};`);
|
|
2792
2898
|
continue;
|
|
2793
2899
|
}
|
|
2794
2900
|
const code = generateComponent(comp, directNames, hydrate, asyncComps, linkNames, selfClaimNames, alloc, markerless);
|
|
2795
|
-
mapLines.push(
|
|
2901
|
+
mapLines.push(`${registry}[${JSON.stringify(comp.name)}] = ${code};`);
|
|
2796
2902
|
}
|
|
2797
2903
|
mapLines.push(`function __cleanup(start, end) {`);
|
|
2798
2904
|
mapLines.push(`\tlet n = start.nextSibling;`);
|
|
@@ -3117,13 +3223,14 @@ function emitClientFromIR(ir, options) {
|
|
|
3117
3223
|
const importLines = ir.imports.length > 0 ? ir.imports.join('\n') + '\n' : '';
|
|
3118
3224
|
const topCode = (options.includeTopLevel === false ? [] : transformTopLevelForActions(ir.topLevelCode, 'client')).join('\n') + '\n';
|
|
3119
3225
|
const exportLines = [];
|
|
3226
|
+
const registry = options.hydrate ? '__hydrators' : '__components';
|
|
3120
3227
|
for (const comp of ir.components) {
|
|
3121
3228
|
if (comp.exported) {
|
|
3122
3229
|
if (comp.defaultExport) {
|
|
3123
|
-
exportLines.push(`export default
|
|
3230
|
+
exportLines.push(`export default ${registry}[${JSON.stringify(comp.name)}];`);
|
|
3124
3231
|
}
|
|
3125
3232
|
else {
|
|
3126
|
-
exportLines.push(`export const ${comp.name} =
|
|
3233
|
+
exportLines.push(`export const ${comp.name} = ${registry}[${JSON.stringify(comp.name)}];`);
|
|
3127
3234
|
}
|
|
3128
3235
|
}
|
|
3129
3236
|
}
|
package/dist/errors.d.ts
CHANGED
|
@@ -104,6 +104,34 @@ export declare class VeskError extends Error {
|
|
|
104
104
|
static attrJsxElement(context?: VeskErrorOptions & {
|
|
105
105
|
attr?: string;
|
|
106
106
|
}): VeskError;
|
|
107
|
+
/**
|
|
108
|
+
* A `{ ... }` block statement wrapping regular JS in statement mode.
|
|
109
|
+
* Vesk statement mode doesn't need braces around statements —
|
|
110
|
+
* `if (cond) { <jsx/> }` is written without the outer `{ }`.
|
|
111
|
+
*/
|
|
112
|
+
static blockStatementInStatementMode(context?: VeskErrorOptions): VeskError;
|
|
113
|
+
/**
|
|
114
|
+
* An opening JSX tag was never closed, e.g. `<div><span>hello</div>`.
|
|
115
|
+
* Acorn reports this as "Expected corresponding JSX closing tag for <span>"
|
|
116
|
+
* or, when wrapped in a component body, as a generic "Unexpected token".
|
|
117
|
+
*/
|
|
118
|
+
static jsxUnclosedTag(tagName: string, context?: VeskErrorOptions & {
|
|
119
|
+
closingTag?: string;
|
|
120
|
+
suggestedFix?: string;
|
|
121
|
+
}): VeskError;
|
|
122
|
+
/**
|
|
123
|
+
* A JSX closing tag doesn't match the most recent opening tag,
|
|
124
|
+
* e.g. `<div></span>`.
|
|
125
|
+
*/
|
|
126
|
+
static jsxMismatchedTag(openingTag: string, closingTag: string, context?: VeskErrorOptions): VeskError;
|
|
127
|
+
/**
|
|
128
|
+
* JSX content was never terminated before end of file, e.g. `<div>` with no `>`.
|
|
129
|
+
*/
|
|
130
|
+
static jsxUnterminated(context?: VeskErrorOptions): VeskError;
|
|
131
|
+
/**
|
|
132
|
+
* An empty JSX expression container `{}`, which is not allowed.
|
|
133
|
+
*/
|
|
134
|
+
static jsxEmptyExpression(context?: VeskErrorOptions): VeskError;
|
|
107
135
|
toString(): string;
|
|
108
136
|
toJSON(): Record<string, unknown>;
|
|
109
137
|
}
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.js
CHANGED
|
@@ -389,38 +389,146 @@ export class VeskError extends Error {
|
|
|
389
389
|
tip: 'JSX elements are content. They render inside an element or travel as a component prop — never as an attribute value on an HTML tag.',
|
|
390
390
|
});
|
|
391
391
|
}
|
|
392
|
+
/**
|
|
393
|
+
* A `{ ... }` block statement wrapping regular JS in statement mode.
|
|
394
|
+
* Vesk statement mode doesn't need braces around statements —
|
|
395
|
+
* `if (cond) { <jsx/> }` is written without the outer `{ }`.
|
|
396
|
+
*/
|
|
397
|
+
static blockStatementInStatementMode(context = {}) {
|
|
398
|
+
return new VeskError('Unnecessary `{ ... }` block around statements in statement mode. Vesk statement mode does not use braces around `if`/`for`/`while`/etc. — write the statement directly.', {
|
|
399
|
+
...context,
|
|
400
|
+
code: context.code || 'V0424',
|
|
401
|
+
suggestions: [
|
|
402
|
+
'Remove the outer `{ }` and write the statement directly:',
|
|
403
|
+
' `{ if (cond) { <div/> } }` → `if (cond) { <div/> }`',
|
|
404
|
+
'Braces are only needed around JSX expression containers `{ expr }` inside JSX, not around statements.',
|
|
405
|
+
],
|
|
406
|
+
nextSteps: [
|
|
407
|
+
'Delete the opening `{` and closing `}` that wrap the statement.',
|
|
408
|
+
'If you meant to group multiple statements, they can appear directly one after another in statement mode.',
|
|
409
|
+
],
|
|
410
|
+
tip: 'Vesk statement mode accepts bare JSX, if/for/while/switch/try without wrapping braces — no `{ }` needed around statements.',
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* An opening JSX tag was never closed, e.g. `<div><span>hello</div>`.
|
|
415
|
+
* Acorn reports this as "Expected corresponding JSX closing tag for <span>"
|
|
416
|
+
* or, when wrapped in a component body, as a generic "Unexpected token".
|
|
417
|
+
*/
|
|
418
|
+
static jsxUnclosedTag(tagName, context = {}) {
|
|
419
|
+
const closing = context.closingTag ?? `</${tagName}>`;
|
|
420
|
+
return new VeskError(`Unclosed JSX tag \`<${tagName}>\` — expected \`${closing}\` before the closing tag.`, {
|
|
421
|
+
...context,
|
|
422
|
+
code: context.code || 'V0420',
|
|
423
|
+
suggestions: [
|
|
424
|
+
`Add \`${closing}\` to close the \`<${tagName}>\` tag.`,
|
|
425
|
+
`Every opening JSX tag must have a matching closing tag: \`<${tagName}></${tagName}>\`.`,
|
|
426
|
+
],
|
|
427
|
+
nextSteps: [
|
|
428
|
+
context.suggestedFix ?? `Insert \`${closing}\` right before the tag that closes the parent element.`,
|
|
429
|
+
`Check that all JSX elements in this component are properly closed and nested.`,
|
|
430
|
+
],
|
|
431
|
+
tip: `The \`<${tagName}>\` tag on the marked line was opened but never closed.`,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* A JSX closing tag doesn't match the most recent opening tag,
|
|
436
|
+
* e.g. `<div></span>`.
|
|
437
|
+
*/
|
|
438
|
+
static jsxMismatchedTag(openingTag, closingTag, context = {}) {
|
|
439
|
+
return new VeskError(`Expected closing tag \`</${openingTag}>\` to match \`<${openingTag}>\`, but found \`</${closingTag}>\`.`, {
|
|
440
|
+
...context,
|
|
441
|
+
code: context.code || 'V0421',
|
|
442
|
+
suggestions: [
|
|
443
|
+
`Change \`</${closingTag}>\` to \`</${openingTag}>\` to close the \`<${openingTag}>\` tag.`,
|
|
444
|
+
`Or change the opening tag to \`<${closingTag}>\` if that was the intended element.`,
|
|
445
|
+
],
|
|
446
|
+
nextSteps: [
|
|
447
|
+
`Fix the mismatched tag on the marked line so opening and closing tags agree.`,
|
|
448
|
+
],
|
|
449
|
+
tip: `JSX closing tags must match their opening tag: \`<${openingTag}></${openingTag}>\`, not \`<${openingTag}></${closingTag}>\`.`,
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* JSX content was never terminated before end of file, e.g. `<div>` with no `>`.
|
|
454
|
+
*/
|
|
455
|
+
static jsxUnterminated(context = {}) {
|
|
456
|
+
return new VeskError('Unterminated JSX contents — the JSX block was never closed before the end of the file.', {
|
|
457
|
+
...context,
|
|
458
|
+
code: context.code || 'V0422',
|
|
459
|
+
suggestions: [
|
|
460
|
+
'Close the JSX element with `>` or `/>` before the end of the file.',
|
|
461
|
+
'Make sure every `<` that opens a JSX tag has a matching `>` (or `/>`).',
|
|
462
|
+
],
|
|
463
|
+
nextSteps: [
|
|
464
|
+
'Add the missing `>` or `/>` to close the JSX tag.',
|
|
465
|
+
'Check that no string or comment is swallowing the closing bracket.',
|
|
466
|
+
],
|
|
467
|
+
tip: 'The JSX block on the marked line was opened but never terminated.',
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* An empty JSX expression container `{}`, which is not allowed.
|
|
472
|
+
*/
|
|
473
|
+
static jsxEmptyExpression(context = {}) {
|
|
474
|
+
return new VeskError('Empty JSX expression container `{}` — JSX expressions must contain a value.', {
|
|
475
|
+
...context,
|
|
476
|
+
code: context.code || 'V0423',
|
|
477
|
+
suggestions: [
|
|
478
|
+
'Fill the braces with an expression: `{ count }`, `{ item.name }`, or `{ true }`.',
|
|
479
|
+
'If you meant literal braces, escape them: `{ \'{\' }` and `{ \'}\' }`.',
|
|
480
|
+
],
|
|
481
|
+
nextSteps: [
|
|
482
|
+
'Replace `{}` with a real expression, or remove the empty braces.',
|
|
483
|
+
],
|
|
484
|
+
tip: '`{}` is not valid JSX — put a value inside the braces.',
|
|
485
|
+
});
|
|
486
|
+
}
|
|
392
487
|
toString() {
|
|
393
|
-
|
|
488
|
+
const lines = [];
|
|
489
|
+
// Header: `error[V####]: message` — rustc-style error code first.
|
|
490
|
+
lines.push(this.code ? `error[${this.code}]: ${this.message}` : `error: ${this.message}`);
|
|
491
|
+
// Location: `--> file:line:col`
|
|
492
|
+
let loc = ' --> ';
|
|
493
|
+
let hasLoc = false;
|
|
394
494
|
if (this.file) {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
out += `:${this.line}`;
|
|
398
|
-
if (this.column)
|
|
399
|
-
out += `:${this.column}`;
|
|
400
|
-
}
|
|
401
|
-
if (this.line && this.column)
|
|
402
|
-
out += ` (line ${this.line}, column ${this.column})`;
|
|
403
|
-
else if (this.line)
|
|
404
|
-
out += ` (line ${this.line})`;
|
|
495
|
+
loc += this.file;
|
|
496
|
+
hasLoc = true;
|
|
405
497
|
}
|
|
406
|
-
|
|
407
|
-
|
|
498
|
+
if (this.line) {
|
|
499
|
+
loc += `:${this.line}`;
|
|
500
|
+
hasLoc = true;
|
|
501
|
+
}
|
|
502
|
+
if (this.column) {
|
|
503
|
+
loc += `:${this.column}`;
|
|
504
|
+
hasLoc = true;
|
|
408
505
|
}
|
|
409
|
-
if (
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
506
|
+
if (hasLoc)
|
|
507
|
+
lines.push(loc);
|
|
508
|
+
// Code frame: source context with a `^` caret under the offending column.
|
|
509
|
+
if (this.frame) {
|
|
510
|
+
lines.push(' |');
|
|
511
|
+
for (const frameLine of this.frame.split('\n')) {
|
|
512
|
+
lines.push(` ${frameLine}`);
|
|
513
|
+
}
|
|
415
514
|
}
|
|
416
|
-
if (this.
|
|
417
|
-
|
|
418
|
-
for (const s of this.nextSteps.slice(0, 4))
|
|
419
|
-
out += `\n • ${s}`;
|
|
515
|
+
else if (this.line) {
|
|
516
|
+
lines.push(' |');
|
|
420
517
|
}
|
|
518
|
+
// Help lines, rustc-style `= help:` — tip first, then next steps / suggestions.
|
|
519
|
+
const help = [];
|
|
421
520
|
if (this.tip)
|
|
422
|
-
|
|
423
|
-
|
|
521
|
+
help.push(this.tip);
|
|
522
|
+
for (const s of this.nextSteps)
|
|
523
|
+
help.push(s);
|
|
524
|
+
for (const s of this.suggestions)
|
|
525
|
+
help.push(s);
|
|
526
|
+
if (help.length) {
|
|
527
|
+
lines.push(' |');
|
|
528
|
+
for (const h of help)
|
|
529
|
+
lines.push(`= help: ${h}`);
|
|
530
|
+
}
|
|
531
|
+
return lines.join('\n');
|
|
424
532
|
}
|
|
425
533
|
toJSON() {
|
|
426
534
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ir-generator.d.ts","sourceRoot":"","sources":["../src/ir-generator.ts"],"names":[],"mappings":"AACA,OAAO,EACL,MAAM,EAuBP,MAAM,uBAAuB,CAAC;AAkW/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;
|
|
1
|
+
{"version":3,"file":"ir-generator.d.ts","sourceRoot":"","sources":["../src/ir-generator.ts"],"names":[],"mappings":"AACA,OAAO,EACL,MAAM,EAuBP,MAAM,uBAAuB,CAAC;AAkW/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;AA0yDD,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAsV9E"}
|
package/dist/ir-generator.js
CHANGED
|
@@ -974,6 +974,12 @@ function processAttribute(source, attr) {
|
|
|
974
974
|
return { name, value: '' };
|
|
975
975
|
if (attr.value.type === 'Literal')
|
|
976
976
|
return { name, value: String(attr.value.value) };
|
|
977
|
+
if (attr.value.type === 'TemplateLiteral') {
|
|
978
|
+
// A bare backtick template literal in a JSX attribute, e.g.
|
|
979
|
+
// `<Link href=`/admin/learners/${id}` />`. It is a dynamic value, so it
|
|
980
|
+
// becomes an attribute binding rather than a static string.
|
|
981
|
+
return { name, value: toExpression(source, attr.value) };
|
|
982
|
+
}
|
|
977
983
|
if (attr.value.type === 'JSXExpressionContainer') {
|
|
978
984
|
const expr = attr.value.expression;
|
|
979
985
|
if (expr.type === 'Literal')
|
|
@@ -1596,6 +1602,11 @@ function extractProps(source, element) {
|
|
|
1596
1602
|
}
|
|
1597
1603
|
value = toExpression(source, expr);
|
|
1598
1604
|
}
|
|
1605
|
+
else if (attr.value.type === 'TemplateLiteral') {
|
|
1606
|
+
// A bare backtick template literal prop value, e.g.
|
|
1607
|
+
// `<Comp href=`/admin/learners/${id}` />`.
|
|
1608
|
+
value = toExpression(source, attr.value);
|
|
1609
|
+
}
|
|
1599
1610
|
else {
|
|
1600
1611
|
value = new Expression(JSON.stringify(attr.value.value));
|
|
1601
1612
|
}
|
|
@@ -1765,6 +1776,8 @@ function isStatementMode(bodyStmts) {
|
|
|
1765
1776
|
if (bodyStmts.some((s) => s.type === 'JSXElement' || s.type === 'JSXExpressionContainer' || s.type === 'JSXFragment'))
|
|
1766
1777
|
return true;
|
|
1767
1778
|
for (const stmt of bodyStmts) {
|
|
1779
|
+
if (stmt.type === 'BlockStatement' && hasJSXInSubtree(stmt))
|
|
1780
|
+
return true;
|
|
1768
1781
|
if (stmt.type === 'IfStatement' && !isGuardClause(stmt) && hasJSXInSubtree(stmt))
|
|
1769
1782
|
return true;
|
|
1770
1783
|
if (stmt.type === 'ForOfStatement' && hasJSXInSubtree(stmt))
|
|
@@ -1928,6 +1941,57 @@ function processStatementModeBody(source, bodyStmts, filename) {
|
|
|
1928
1941
|
if (refName)
|
|
1929
1942
|
nodes.push(new ComponentRef(refName));
|
|
1930
1943
|
}
|
|
1944
|
+
else if (stmt.type === 'VariableDeclaration') {
|
|
1945
|
+
// Plain variable declarations (`const items = [1, 2, 3]`, `let x = 1`)
|
|
1946
|
+
// that are not track() declarations are regular JavaScript. Emit them
|
|
1947
|
+
// verbatim as runtime statements so they execute in statement mode.
|
|
1948
|
+
const raw = getSource(source, stmt);
|
|
1949
|
+
if (raw)
|
|
1950
|
+
nodes.push(new RuntimeStatement(raw, stmt, source));
|
|
1951
|
+
}
|
|
1952
|
+
else if (stmt.type === 'BlockStatement') {
|
|
1953
|
+
// Braces around statements are unnecessary in Vesk statement mode —
|
|
1954
|
+
// `if (cond) { <jsx/> }` is written without the outer `{ }`. If the
|
|
1955
|
+
// block contains only JSX children, unwrap them; otherwise give a
|
|
1956
|
+
// clear error (braces around `if`/`for`/`const`/etc. are not needed).
|
|
1957
|
+
const innerStmts = stmt.body || [];
|
|
1958
|
+
const hasNonJSX = innerStmts.some((s) => s.type !== 'JSXElement' && s.type !== 'JSXExpressionContainer' && s.type !== 'JSXFragment');
|
|
1959
|
+
if (hasNonJSX) {
|
|
1960
|
+
const start = stmt.start ?? 0;
|
|
1961
|
+
let line = 1;
|
|
1962
|
+
let column = 1;
|
|
1963
|
+
for (let i = 0; i < start && i < source.length; i++) {
|
|
1964
|
+
if (source.charCodeAt(i) === 10) {
|
|
1965
|
+
line++;
|
|
1966
|
+
column = 1;
|
|
1967
|
+
}
|
|
1968
|
+
else {
|
|
1969
|
+
column++;
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
throw VeskError.blockStatementInStatementMode({
|
|
1973
|
+
file: filename || '',
|
|
1974
|
+
line,
|
|
1975
|
+
column,
|
|
1976
|
+
code: codeFrame(source, line, column),
|
|
1977
|
+
});
|
|
1978
|
+
}
|
|
1979
|
+
// Pure JSX block: unwrap the children directly.
|
|
1980
|
+
for (const child of innerStmts) {
|
|
1981
|
+
if (child.type === 'JSXElement') {
|
|
1982
|
+
nodes.push(...processJSXElement(source, child));
|
|
1983
|
+
}
|
|
1984
|
+
else if (child.type === 'JSXExpressionContainer') {
|
|
1985
|
+
if (child.expression.type === 'JSXEmptyExpression')
|
|
1986
|
+
continue;
|
|
1987
|
+
nodes.push(...exprToIR(source, child.expression));
|
|
1988
|
+
}
|
|
1989
|
+
else if (child.type === 'JSXFragment') {
|
|
1990
|
+
for (const c of child.children)
|
|
1991
|
+
nodes.push(...processJSXChildren(source, [c]));
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1931
1995
|
else if (stmt.type === 'IfStatement') {
|
|
1932
1996
|
// Guard-clause early return (`if (c) return X` with no else): everything
|
|
1933
1997
|
// after this statement becomes the alternate branch, mirroring
|
|
@@ -2388,7 +2452,7 @@ export function generateIR(ast, source, filename) {
|
|
|
2388
2452
|
for (const spec of node.specifiers) {
|
|
2389
2453
|
if (spec.importKind === 'type')
|
|
2390
2454
|
continue;
|
|
2391
|
-
if (spec.type === 'ImportSpecifier') {
|
|
2455
|
+
if (spec.type === 'ImportSpecifier' || spec.type === 'ImportDefaultSpecifier' || spec.type === 'ImportNamespaceSpecifier') {
|
|
2392
2456
|
importedNames.add(spec.local.name);
|
|
2393
2457
|
}
|
|
2394
2458
|
}
|