@vesk/compiler 0.2.91 → 0.2.93
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 +17 -6
- 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.map +1 -1
- package/dist/module-imports.js +33 -11
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +306 -15
- 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;AAiqED,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
|
|
@@ -1322,7 +1331,7 @@ function emitComponentCall(ctx, node, tracked, effectsVar, parentVar, compPrefix
|
|
|
1322
1331
|
const access = calleeExpr
|
|
1323
1332
|
?? (ctx.importedNames.has(node.componentName)
|
|
1324
1333
|
? node.componentName
|
|
1325
|
-
:
|
|
1334
|
+
: `${ctx.hydrateRegistry}[${JSON.stringify(node.componentName)}]`);
|
|
1326
1335
|
// Registry lookups can resolve to `undefined` when a JSX tag names a
|
|
1327
1336
|
// component that was never declared, imported, or registered (e.g.
|
|
1328
1337
|
// `<Slot/>` in a framework where `{props.children}` is the channel).
|
|
@@ -2746,7 +2755,8 @@ function buildParamInit(paramNames, propsAlias = null) {
|
|
|
2746
2755
|
}
|
|
2747
2756
|
function buildComponentMap(irRoot, hydrate = false, alloc, markerless = true) {
|
|
2748
2757
|
const mapLines = [];
|
|
2749
|
-
|
|
2758
|
+
const registry = hydrate ? '__hydrators' : '__components';
|
|
2759
|
+
mapLines.push(`const ${registry} = {};`);
|
|
2750
2760
|
const asyncComps = computeAsyncComponents(irRoot.components);
|
|
2751
2761
|
// Wipe-style router components (Link/NavLink) replace their SSR children on
|
|
2752
2762
|
// hydration, so their children fragments must be rebuilt in full. Track the
|
|
@@ -2788,11 +2798,11 @@ function buildComponentMap(irRoot, hydrate = false, alloc, markerless = true) {
|
|
|
2788
2798
|
? `__hydrate.claimOnly(undefined, (typeof __hydrate.takeSkipK === 'function' ? __hydrate.takeSkipK() : 0))`
|
|
2789
2799
|
: `__hydrate.claimOnly()`;
|
|
2790
2800
|
const stub = `(props, __registry, __hydrate) => { return ${claimExpr}; }`;
|
|
2791
|
-
mapLines.push(
|
|
2801
|
+
mapLines.push(`${registry}[${JSON.stringify(comp.name)}] = ${stub};`);
|
|
2792
2802
|
continue;
|
|
2793
2803
|
}
|
|
2794
2804
|
const code = generateComponent(comp, directNames, hydrate, asyncComps, linkNames, selfClaimNames, alloc, markerless);
|
|
2795
|
-
mapLines.push(
|
|
2805
|
+
mapLines.push(`${registry}[${JSON.stringify(comp.name)}] = ${code};`);
|
|
2796
2806
|
}
|
|
2797
2807
|
mapLines.push(`function __cleanup(start, end) {`);
|
|
2798
2808
|
mapLines.push(`\tlet n = start.nextSibling;`);
|
|
@@ -3117,13 +3127,14 @@ function emitClientFromIR(ir, options) {
|
|
|
3117
3127
|
const importLines = ir.imports.length > 0 ? ir.imports.join('\n') + '\n' : '';
|
|
3118
3128
|
const topCode = (options.includeTopLevel === false ? [] : transformTopLevelForActions(ir.topLevelCode, 'client')).join('\n') + '\n';
|
|
3119
3129
|
const exportLines = [];
|
|
3130
|
+
const registry = options.hydrate ? '__hydrators' : '__components';
|
|
3120
3131
|
for (const comp of ir.components) {
|
|
3121
3132
|
if (comp.exported) {
|
|
3122
3133
|
if (comp.defaultExport) {
|
|
3123
|
-
exportLines.push(`export default
|
|
3134
|
+
exportLines.push(`export default ${registry}[${JSON.stringify(comp.name)}];`);
|
|
3124
3135
|
}
|
|
3125
3136
|
else {
|
|
3126
|
-
exportLines.push(`export const ${comp.name} =
|
|
3137
|
+
exportLines.push(`export const ${comp.name} = ${registry}[${JSON.stringify(comp.name)}];`);
|
|
3127
3138
|
}
|
|
3128
3139
|
}
|
|
3129
3140
|
}
|
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
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module-imports.d.ts","sourceRoot":"","sources":["../src/module-imports.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAGlD,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAyEtD,gFAAgF;AAChF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAM7D;AAED,gEAAgE;AAChE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAOzD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAgC1F;AAED,yEAAyE;AACzE,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAQpE;AAED,0FAA0F;AAC1F,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAIvD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,UAAU,EAAE,MAAM,EAAE,EACpB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,IAAI,CAuBN;AAiBD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"module-imports.d.ts","sourceRoot":"","sources":["../src/module-imports.ts"],"names":[],"mappings":"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"}
|
package/dist/module-imports.js
CHANGED
|
@@ -259,12 +259,21 @@ export function loadSsrModule(absPath) {
|
|
|
259
259
|
}
|
|
260
260
|
else {
|
|
261
261
|
exportsObj = {};
|
|
262
|
+
// Wrap exportsObj in a module object so CJS modules that do
|
|
263
|
+
// `module.exports = factory()` (e.g. qrcode-generator) have their
|
|
264
|
+
// export captured correctly. Without this, module.exports is replaced
|
|
265
|
+
// but exportsObj still points to the original empty object.
|
|
266
|
+
const moduleObj = { exports: exportsObj };
|
|
262
267
|
EVALUATING.set(absPath, exportsObj);
|
|
263
|
-
const ran = evaluateModuleFile(absPath,
|
|
268
|
+
const ran = evaluateModuleFile(absPath, moduleObj, src);
|
|
264
269
|
// Runtime evaluation failure (already warned) — give Node's own loader
|
|
265
270
|
// a chance before giving up.
|
|
266
271
|
if (!ran)
|
|
267
272
|
exportsObj = null;
|
|
273
|
+
// Prefer module.exports when a CJS module replaced it (e.g.
|
|
274
|
+
// qrcode-generator), otherwise fall back to exportsObj.
|
|
275
|
+
else if (moduleObj.exports !== exportsObj)
|
|
276
|
+
exportsObj = moduleObj.exports;
|
|
268
277
|
}
|
|
269
278
|
}
|
|
270
279
|
if (exportsObj === null) {
|
|
@@ -601,7 +610,7 @@ function createLazyBarrelExports(spec, dir) {
|
|
|
601
610
|
* `src` carries a read+parse already done by the caller (either a barrel probe
|
|
602
611
|
* or a plain load) so big files aren't read/parsed twice.
|
|
603
612
|
*/
|
|
604
|
-
function evaluateModuleFile(absPath,
|
|
613
|
+
function evaluateModuleFile(absPath, moduleObj, src) {
|
|
605
614
|
let raw;
|
|
606
615
|
if (src) {
|
|
607
616
|
raw = src.raw;
|
|
@@ -636,7 +645,7 @@ function evaluateModuleFile(absPath, exportsObj, src) {
|
|
|
636
645
|
// Not parseable as ESM — run verbatim as CJS.
|
|
637
646
|
try {
|
|
638
647
|
const fn = new Function('require', 'module', 'exports', '__dirname', '__filename', raw);
|
|
639
|
-
fn(createModuleRequire(dirname(absPath)),
|
|
648
|
+
fn(createModuleRequire(dirname(absPath)), moduleObj, moduleObj.exports, dirname(absPath), absPath);
|
|
640
649
|
}
|
|
641
650
|
catch (err) {
|
|
642
651
|
console.warn(`[vesk] SSR: failed to load ${absPath}: ${err?.message ?? String(err)}`);
|
|
@@ -661,7 +670,7 @@ function evaluateModuleFile(absPath, exportsObj, src) {
|
|
|
661
670
|
// ESM modules are always strict; mirror per-spec `this === undefined`.
|
|
662
671
|
try {
|
|
663
672
|
const fn = new Function('require', 'module', 'exports', '__dirname', '__filename', "'use strict';\n" + body);
|
|
664
|
-
fn(createModuleRequire(dirname(absPath)),
|
|
673
|
+
fn(createModuleRequire(dirname(absPath)), moduleObj, moduleObj.exports, dirname(absPath), absPath);
|
|
665
674
|
}
|
|
666
675
|
catch (err) {
|
|
667
676
|
console.warn(`[vesk] SSR: failed to load ${absPath}: ${err?.message ?? String(err)}`);
|
|
@@ -949,6 +958,12 @@ export function resolveImportPath(spec, fromDir) {
|
|
|
949
958
|
const aliased = resolveAliasModule(spec, fromDir);
|
|
950
959
|
if (aliased)
|
|
951
960
|
return aliased;
|
|
961
|
+
// Bare npm specifiers (e.g. "vesk-qr") must resolve to absolute paths so
|
|
962
|
+
// the client-chunk bundler (esbuild from a temp dir) can find them in
|
|
963
|
+
// the app's node_modules via the package's exports map.
|
|
964
|
+
const resolved = resolveSsrModule(spec, fromDir);
|
|
965
|
+
if (resolved)
|
|
966
|
+
return isBuiltinPath(resolved) ? resolved.slice(BUILTIN_PREFIX.length) : resolved;
|
|
952
967
|
}
|
|
953
968
|
return spec;
|
|
954
969
|
}
|
|
@@ -1161,8 +1176,13 @@ export function resolveViaPackageModuleField(specifier, fromDir) {
|
|
|
1161
1176
|
if (statOrNull(pkgPath) !== null) {
|
|
1162
1177
|
try {
|
|
1163
1178
|
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
1164
|
-
|
|
1165
|
-
|
|
1179
|
+
const entry = typeof pkg.module === 'string' && pkg.module.length > 0
|
|
1180
|
+
? pkg.module
|
|
1181
|
+
: typeof pkg.main === 'string' && pkg.main.length > 0
|
|
1182
|
+
? pkg.main
|
|
1183
|
+
: null;
|
|
1184
|
+
if (entry) {
|
|
1185
|
+
const found = probeFile(resolve(join(dir, 'node_modules', pkgName), entry));
|
|
1166
1186
|
if (found)
|
|
1167
1187
|
return toRealPath(found);
|
|
1168
1188
|
}
|
|
@@ -1486,9 +1506,6 @@ export class ModuleRegistry {
|
|
|
1486
1506
|
return `export const __veskModules = {${parts.join(',')}};\n`;
|
|
1487
1507
|
}
|
|
1488
1508
|
}
|
|
1489
|
-
function isRelativeSpec(spec) {
|
|
1490
|
-
return spec.startsWith('./') || spec.startsWith('../');
|
|
1491
|
-
}
|
|
1492
1509
|
function stringLiteralValue(node) {
|
|
1493
1510
|
const n = node;
|
|
1494
1511
|
if (n && (n.type === 'Literal' || n.type === 'StringLiteral') && typeof n.value === 'string') {
|
|
@@ -1603,10 +1620,15 @@ export function collectModuleBundled(absPath, registry) {
|
|
|
1603
1620
|
}
|
|
1604
1621
|
const deps = {};
|
|
1605
1622
|
for (const spec of specifiers) {
|
|
1606
|
-
|
|
1623
|
+
// Compiler-owned (`@vesk/*`) targets are injected by the SSR require shim
|
|
1624
|
+
// at runtime (`precompile-runtime.ts`), never bundled: their built JS may
|
|
1625
|
+
// contain identifiers the `.vsk` grammar reserves (`component` in
|
|
1626
|
+
// ripple-runtime.js) and would fail to parse here. `.vsk`/`.css`/`.md`
|
|
1627
|
+
// carry no SSR value. Node builtins load natively through the shim.
|
|
1628
|
+
if (isCompilerOwnedTarget(spec) || isValueLessTarget(spec))
|
|
1607
1629
|
continue;
|
|
1608
1630
|
const depPath = resolveSsrModule(spec, dir);
|
|
1609
|
-
if (!depPath)
|
|
1631
|
+
if (!depPath || isBuiltinPath(depPath))
|
|
1610
1632
|
continue;
|
|
1611
1633
|
deps[spec] = collectModuleBundled(depPath, registry);
|
|
1612
1634
|
}
|
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;;;;;;;;;;;;;;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,
|
|
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,CA+NzE"}
|
package/dist/parser.js
CHANGED
|
@@ -573,26 +573,317 @@ export function parse(source, options = {}) {
|
|
|
573
573
|
column = (err.loc.column ?? 0) + 1;
|
|
574
574
|
}
|
|
575
575
|
else {
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
576
|
+
// Parse "(line:column)" from the message without regex.
|
|
577
|
+
const parenOpen = err.message.indexOf('(');
|
|
578
|
+
if (parenOpen >= 0) {
|
|
579
|
+
const parenClose = err.message.indexOf(')', parenOpen);
|
|
580
|
+
if (parenClose > parenOpen) {
|
|
581
|
+
const inside = err.message.slice(parenOpen + 1, parenClose);
|
|
582
|
+
const colon = inside.indexOf(':');
|
|
583
|
+
if (colon > 0) {
|
|
584
|
+
line = parseInt(inside.slice(0, colon), 10);
|
|
585
|
+
column = parseInt(inside.slice(colon + 1), 10) + 1;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
580
588
|
}
|
|
581
589
|
}
|
|
582
590
|
const filename = options.filename || '';
|
|
583
|
-
|
|
584
|
-
|
|
591
|
+
// Strip the trailing "(line:column)" acorn appends, without regex.
|
|
592
|
+
let cleanMessage = err.message;
|
|
593
|
+
const lastParen = cleanMessage.lastIndexOf('(');
|
|
594
|
+
if (lastParen >= 0) {
|
|
595
|
+
const closeParen = cleanMessage.indexOf(')', lastParen);
|
|
596
|
+
if (closeParen > lastParen) {
|
|
597
|
+
const inside = cleanMessage.slice(lastParen + 1, closeParen);
|
|
598
|
+
const colon = inside.indexOf(':');
|
|
599
|
+
if (colon > 0 &&
|
|
600
|
+
inside.slice(0, colon).split('').every((ch) => ch >= '0' && ch <= '9') &&
|
|
601
|
+
inside.slice(colon + 1).split('').every((ch) => ch >= '0' && ch <= '9')) {
|
|
602
|
+
cleanMessage = cleanMessage.slice(0, lastParen).trimEnd();
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
// --- Convert acorn's raw JSX errors into descriptive Vesk messages ---
|
|
607
|
+
let message = cleanMessage;
|
|
608
|
+
let errLine = line;
|
|
609
|
+
let errColumn = column;
|
|
610
|
+
let code;
|
|
611
|
+
const extraHelp = [];
|
|
612
|
+
const frame = errLine > 0 ? codeFrame(source, errLine, errColumn, 5, 5) : '';
|
|
585
613
|
const hint = filename ? ` in ${filename}` : '';
|
|
586
|
-
|
|
614
|
+
// "Unterminated JSX contents" — acorn hit EOF inside a JSX block.
|
|
615
|
+
if (cleanMessage.includes('Unterminated JSX contents')) {
|
|
616
|
+
throw VeskError.jsxUnterminated({
|
|
617
|
+
file: filename,
|
|
618
|
+
line: errLine,
|
|
619
|
+
column: errColumn,
|
|
620
|
+
frame,
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
// "JSX attributes must only be assigned a non-empty expression" — `{}`.
|
|
624
|
+
if (cleanMessage.includes('JSX attributes must only be assigned a non-empty expression')) {
|
|
625
|
+
throw VeskError.jsxEmptyExpression({
|
|
626
|
+
file: filename,
|
|
627
|
+
line: errLine,
|
|
628
|
+
column: errColumn,
|
|
629
|
+
frame,
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
// "Expected corresponding JSX closing tag for <X>" — acorn points at the
|
|
633
|
+
// opening tag, but it could mean either a mismatched closing tag
|
|
634
|
+
// (<div></span>) or a genuinely unclosed tag (<div><span>...</div>).
|
|
635
|
+
const jsxClosePrefix = 'Expected corresponding JSX closing tag for <';
|
|
636
|
+
if (cleanMessage.startsWith(jsxClosePrefix)) {
|
|
637
|
+
const mismatched = findMismatchedJsxTag(source, errLine);
|
|
638
|
+
if (mismatched) {
|
|
639
|
+
throw VeskError.jsxMismatchedTag(mismatched.openingTag, mismatched.closingTag, {
|
|
640
|
+
file: filename,
|
|
641
|
+
line: mismatched.line,
|
|
642
|
+
column: mismatched.column,
|
|
643
|
+
frame: codeFrame(source, mismatched.line, mismatched.column, 5, 5),
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
const rest = cleanMessage.slice(jsxClosePrefix.length);
|
|
647
|
+
const gt = rest.indexOf('>');
|
|
648
|
+
const tagName = gt >= 0 ? rest.slice(0, gt) : rest;
|
|
649
|
+
throw VeskError.jsxUnclosedTag(tagName, {
|
|
650
|
+
file: filename,
|
|
651
|
+
line: errLine,
|
|
652
|
+
column: errColumn,
|
|
653
|
+
frame,
|
|
654
|
+
suggestedFix: `Insert </${tagName}> before the tag that closes the parent element.`,
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
// "Unexpected token" inside a component body often means a JSX tag was
|
|
658
|
+
// opened but never closed — acorn reports it late, at the closing `}`.
|
|
659
|
+
if (cleanMessage.includes('Unexpected token') && errLine > 0) {
|
|
660
|
+
const srcLine = (source.split('\n')[errLine - 1] ?? '').trim();
|
|
661
|
+
const isBlockCloser = srcLine.startsWith('}') || srcLine.startsWith(')');
|
|
662
|
+
if (isBlockCloser) {
|
|
663
|
+
const unclosed = findUnclosedJsxTag(source, errLine);
|
|
664
|
+
if (unclosed) {
|
|
665
|
+
throw VeskError.jsxUnclosedTag(unclosed.tagName, {
|
|
666
|
+
file: filename,
|
|
667
|
+
line: unclosed.line,
|
|
668
|
+
column: unclosed.column,
|
|
669
|
+
frame: errLine > 0 ? codeFrame(source, unclosed.line, unclosed.column, 5, 5) : '',
|
|
670
|
+
suggestedFix: `Insert </${unclosed.tagName}> before the tag that closes the parent element.`,
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
// `{if (cond) { <jsx/> }}` — LLMs often wrap statements in braces in
|
|
675
|
+
// statement mode, but Vesk doesn't need them. Look for `{` immediately
|
|
676
|
+
// before the offending token (skipping whitespace) followed by a
|
|
677
|
+
// statement keyword (`if`, `for`, `while`, `switch`, `try`, `const`,
|
|
678
|
+
// `let`, `var`, `function`, `class`, `return`).
|
|
679
|
+
if (errColumn > 1) {
|
|
680
|
+
const lineText = source.split('\n')[errLine - 1] ?? '';
|
|
681
|
+
// Walk back from the error column to find the `{` that opened the
|
|
682
|
+
// block (or JSX expression container) that shouldn't be there.
|
|
683
|
+
let j = errColumn - 2; // 0-based index of char before error token
|
|
684
|
+
while (j >= 0 && (lineText[j] === ' ' || lineText[j] === '\t'))
|
|
685
|
+
j--;
|
|
686
|
+
if (j >= 0 && lineText[j] === '{') {
|
|
687
|
+
// Read the keyword after `{` (skipping whitespace)
|
|
688
|
+
let k = errColumn - 1; // 0-based index of error token start
|
|
689
|
+
while (k < lineText.length && (lineText[k] === ' ' || lineText[k] === '\t'))
|
|
690
|
+
k++;
|
|
691
|
+
let keyword = '';
|
|
692
|
+
while (k < lineText.length && ((lineText[k] >= 'a' && lineText[k] <= 'z') || (lineText[k] >= 'A' && lineText[k] <= 'Z') || lineText[k] === '_' || lineText[k] === '$')) {
|
|
693
|
+
keyword += lineText[k];
|
|
694
|
+
k++;
|
|
695
|
+
}
|
|
696
|
+
const statementKeywords = new Set(['if', 'for', 'while', 'switch', 'try', 'const', 'let', 'var', 'function', 'class', 'return', 'do', 'with']);
|
|
697
|
+
if (statementKeywords.has(keyword)) {
|
|
698
|
+
const startLine = errLine;
|
|
699
|
+
const startColumn = j + 1; // 1-based column of `{`
|
|
700
|
+
throw VeskError.blockStatementInStatementMode({
|
|
701
|
+
file: filename,
|
|
702
|
+
line: startLine,
|
|
703
|
+
column: startColumn,
|
|
704
|
+
frame: codeFrame(source, startLine, startColumn, 5, 5),
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
// Targeted help for a bare template literal used as a JSX attribute value,
|
|
711
|
+
// e.g. <Link href=`/admin/learners/${id}`>. JSX attributes must be a quoted
|
|
712
|
+
// string or a `{...}` expression; a backtick on its own is not valid JSX.
|
|
713
|
+
if (errLine > 0 &&
|
|
714
|
+
cleanMessage.includes('JSX value should be either an expression or a quoted JSX text')) {
|
|
715
|
+
const srcLine = (source.split('\n')[errLine - 1] ?? '');
|
|
716
|
+
if (srcLine.includes('`')) {
|
|
717
|
+
extraHelp.push('JSX attribute values must be a quoted string (`href="..."`) or a `{...}` expression (`href={expr}`).', 'A bare backtick template literal is not valid JSX — wrap it in braces:', ' href={`/admin/learners/${id}`}');
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
throw new VeskError(`${message}${hint}`, {
|
|
587
721
|
file: filename,
|
|
588
|
-
line,
|
|
589
|
-
column,
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
'If you wrote literal { or } inside JSX text, escape them as {\'{\'} and {\'}\'} or use { }.',
|
|
594
|
-
'Ensure all JSX tags are properly closed and component names start with an uppercase letter.',
|
|
595
|
-
],
|
|
722
|
+
line: errLine,
|
|
723
|
+
column: errColumn,
|
|
724
|
+
frame,
|
|
725
|
+
code,
|
|
726
|
+
nextSteps: extraHelp.length ? extraHelp : [],
|
|
596
727
|
});
|
|
597
728
|
}
|
|
598
729
|
}
|
|
730
|
+
/**
|
|
731
|
+
* Scan the source (character by character, no regex) tracking JSX opening and
|
|
732
|
+
* closing tags up to `upToLine`. Returns the most recently opened tag that
|
|
733
|
+
* was never closed, or `null` if all tags are balanced. Used only to produce
|
|
734
|
+
* a better error message when acorn reports a generic "Unexpected token"
|
|
735
|
+
* for an unclosed JSX tag.
|
|
736
|
+
*/
|
|
737
|
+
function findUnclosedJsxTag(source, upToLine) {
|
|
738
|
+
const stack = [];
|
|
739
|
+
let i = 0;
|
|
740
|
+
let currentLine = 1;
|
|
741
|
+
const isIdentStart = (code) => (code >= 97 && code <= 122) || (code >= 65 && code <= 90) || code === 95; // a-z A-Z _
|
|
742
|
+
while (i < source.length) {
|
|
743
|
+
if (source[i] === '\n') {
|
|
744
|
+
currentLine++;
|
|
745
|
+
i++;
|
|
746
|
+
if (currentLine > upToLine)
|
|
747
|
+
break;
|
|
748
|
+
continue;
|
|
749
|
+
}
|
|
750
|
+
if (currentLine > upToLine)
|
|
751
|
+
break;
|
|
752
|
+
if (source[i] === '<') {
|
|
753
|
+
// Closing tag: </tagName>
|
|
754
|
+
if (i + 1 < source.length && source[i + 1] === '/') {
|
|
755
|
+
let j = i + 2;
|
|
756
|
+
while (j < source.length && source[j] !== '>' && source[j] !== '\n')
|
|
757
|
+
j++;
|
|
758
|
+
const tagName = source.slice(i + 2, j);
|
|
759
|
+
// Only pop if it matches the most recently opened unclosed tag.
|
|
760
|
+
if (stack.length > 0 && stack[stack.length - 1].tagName === tagName) {
|
|
761
|
+
stack.pop();
|
|
762
|
+
}
|
|
763
|
+
// Skip past the closing >.
|
|
764
|
+
while (j < source.length && source[j] !== '>')
|
|
765
|
+
j++;
|
|
766
|
+
if (j < source.length)
|
|
767
|
+
j++;
|
|
768
|
+
i = j;
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
// Opening tag: <tagName ...> (skip namespace prefixes like ns.)
|
|
772
|
+
let j = i + 1;
|
|
773
|
+
while (j < source.length &&
|
|
774
|
+
(source[j] === '.' || source[j] === ':' ||
|
|
775
|
+
(source.charCodeAt(j) >= 97 && source.charCodeAt(j) <= 122) ||
|
|
776
|
+
(source.charCodeAt(j) >= 65 && source.charCodeAt(j) <= 90) ||
|
|
777
|
+
source.charCodeAt(j) === 95)) {
|
|
778
|
+
j++;
|
|
779
|
+
}
|
|
780
|
+
const tagName = source.slice(i + 1, j);
|
|
781
|
+
if (tagName.length > 0 && isIdentStart(tagName.charCodeAt(0))) {
|
|
782
|
+
// Check for self-closing: <tagName />
|
|
783
|
+
let k = j;
|
|
784
|
+
let selfClosing = false;
|
|
785
|
+
while (k < source.length && source[k] !== '>') {
|
|
786
|
+
if (source[k] === '/' && k + 1 < source.length && source[k + 1] === '>') {
|
|
787
|
+
selfClosing = true;
|
|
788
|
+
break;
|
|
789
|
+
}
|
|
790
|
+
k++;
|
|
791
|
+
}
|
|
792
|
+
if (!selfClosing) {
|
|
793
|
+
const loc = offsetToLineCol(source, i);
|
|
794
|
+
stack.push({ tagName, line: loc.line, column: loc.column });
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
// Skip past the closing >.
|
|
798
|
+
while (j < source.length && source[j] !== '>')
|
|
799
|
+
j++;
|
|
800
|
+
if (j < source.length)
|
|
801
|
+
j++;
|
|
802
|
+
i = j;
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
i++;
|
|
806
|
+
}
|
|
807
|
+
// The most recently opened tag still on the stack is the unclosed one.
|
|
808
|
+
if (stack.length > 0) {
|
|
809
|
+
return stack[stack.length - 1];
|
|
810
|
+
}
|
|
811
|
+
return null;
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Scan the source tracking JSX tags up to `upToLine`. Returns the first
|
|
815
|
+
* closing tag that does not match the most recently opened tag,
|
|
816
|
+
* e.g. `</span>` after `<div>`. Character scan only — no regex.
|
|
817
|
+
*/
|
|
818
|
+
function findMismatchedJsxTag(source, upToLine) {
|
|
819
|
+
const stack = [];
|
|
820
|
+
let i = 0;
|
|
821
|
+
let currentLine = 1;
|
|
822
|
+
const isIdentStart = (code) => (code >= 97 && code <= 122) || (code >= 65 && code <= 90) || code === 95;
|
|
823
|
+
while (i < source.length) {
|
|
824
|
+
if (source[i] === '\n') {
|
|
825
|
+
currentLine++;
|
|
826
|
+
i++;
|
|
827
|
+
if (currentLine > upToLine)
|
|
828
|
+
break;
|
|
829
|
+
continue;
|
|
830
|
+
}
|
|
831
|
+
if (currentLine > upToLine)
|
|
832
|
+
break;
|
|
833
|
+
if (source[i] === '<') {
|
|
834
|
+
// Closing tag: </tagName>
|
|
835
|
+
if (i + 1 < source.length && source[i + 1] === '/') {
|
|
836
|
+
let j = i + 2;
|
|
837
|
+
while (j < source.length && source[j] !== '>' && source[j] !== '\n')
|
|
838
|
+
j++;
|
|
839
|
+
const closingTag = source.slice(i + 2, j);
|
|
840
|
+
while (j < source.length && source[j] !== '>')
|
|
841
|
+
j++;
|
|
842
|
+
if (j < source.length)
|
|
843
|
+
j++;
|
|
844
|
+
const loc = offsetToLineCol(source, i);
|
|
845
|
+
// Mismatch: a closing tag that doesn't match the most recent opening tag.
|
|
846
|
+
if (stack.length > 0 && stack[stack.length - 1] !== closingTag) {
|
|
847
|
+
return { openingTag: stack[stack.length - 1], closingTag, line: loc.line, column: loc.column };
|
|
848
|
+
}
|
|
849
|
+
// Match: pop the stack.
|
|
850
|
+
if (stack.length > 0)
|
|
851
|
+
stack.pop();
|
|
852
|
+
i = j;
|
|
853
|
+
continue;
|
|
854
|
+
}
|
|
855
|
+
// Opening tag: <tagName ...>
|
|
856
|
+
let j = i + 1;
|
|
857
|
+
while (j < source.length &&
|
|
858
|
+
(source[j] === '.' || source[j] === ':' ||
|
|
859
|
+
(source.charCodeAt(j) >= 97 && source.charCodeAt(j) <= 122) ||
|
|
860
|
+
(source.charCodeAt(j) >= 65 && source.charCodeAt(j) <= 90) ||
|
|
861
|
+
source.charCodeAt(j) === 95)) {
|
|
862
|
+
j++;
|
|
863
|
+
}
|
|
864
|
+
const tagName = source.slice(i + 1, j);
|
|
865
|
+
if (tagName.length > 0 && isIdentStart(tagName.charCodeAt(0))) {
|
|
866
|
+
// Self-closing: <tagName />
|
|
867
|
+
let k = j;
|
|
868
|
+
let selfClosing = false;
|
|
869
|
+
while (k < source.length && source[k] !== '>') {
|
|
870
|
+
if (source[k] === '/' && k + 1 < source.length && source[k + 1] === '>') {
|
|
871
|
+
selfClosing = true;
|
|
872
|
+
break;
|
|
873
|
+
}
|
|
874
|
+
k++;
|
|
875
|
+
}
|
|
876
|
+
if (!selfClosing)
|
|
877
|
+
stack.push(tagName);
|
|
878
|
+
}
|
|
879
|
+
while (j < source.length && source[j] !== '>')
|
|
880
|
+
j++;
|
|
881
|
+
if (j < source.length)
|
|
882
|
+
j++;
|
|
883
|
+
i = j;
|
|
884
|
+
continue;
|
|
885
|
+
}
|
|
886
|
+
i++;
|
|
887
|
+
}
|
|
888
|
+
return null;
|
|
889
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-render.d.ts","sourceRoot":"","sources":["../src/server-render.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"server-render.d.ts","sourceRoot":"","sources":["../src/server-render.ts"],"names":[],"mappings":"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"}
|
package/dist/server-render.js
CHANGED
|
@@ -463,23 +463,24 @@ export function buildDataScripts(ssrProps, ssrData, external) {
|
|
|
463
463
|
return scripts;
|
|
464
464
|
}
|
|
465
465
|
export async function renderFullPage(source, componentName, props = {}, registry = new Map(), options = {}) {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
466
|
+
// Adopt the ambient request store when one is open: renderPage runs in the
|
|
467
|
+
// enclosing withSsrStore scope and attributes setSsrData writes to that
|
|
468
|
+
// store's token, so renderFullPage must read the same slot to hand off SSR
|
|
469
|
+
// data. Only mint a fresh store when called stand-alone (no ambient scope),
|
|
470
|
+
// which keeps concurrent requests isolated through their distinct tokens.
|
|
471
|
+
const store = adoptSsrStore();
|
|
472
|
+
const renderToken = store.token;
|
|
473
|
+
keepSsrSlot(renderToken);
|
|
474
|
+
pruneSsrDataSlots();
|
|
475
|
+
globalThis.__vsk_ssr = true;
|
|
476
|
+
// Start this render's handoff store fresh: setSsrData mirrors into
|
|
477
|
+
// globalThis.__vsk_ssr_data (see resource.ts) because the AsyncLocalStorage
|
|
478
|
+
// sink snapshot can be empty when the write runs in a forked async context.
|
|
479
|
+
// Resetting here also gives the /about-after-/posts no-leak behavior.
|
|
480
|
+
globalThis.__vsk_ssr_data = {};
|
|
481
|
+
let result;
|
|
482
|
+
try {
|
|
483
|
+
result = await withSsrStoreOf(store, async () => {
|
|
483
484
|
let ssrProps = { ...props };
|
|
484
485
|
let serializedProps = null;
|
|
485
486
|
const cached = options.cached || undefined;
|
|
@@ -500,18 +501,20 @@ export async function renderFullPage(source, componentName, props = {}, registry
|
|
|
500
501
|
// Defensive settle: page/layout renders already awaited their own SSR
|
|
501
502
|
// promises before cleanup; this covers any resource started here (loadFn).
|
|
502
503
|
await settleSsrPromises(renderToken);
|
|
503
|
-
// Merge the
|
|
504
|
-
//
|
|
505
|
-
//
|
|
506
|
-
//
|
|
507
|
-
//
|
|
504
|
+
// Merge the request's per-token data slot. The slot is the deterministic
|
|
505
|
+
// channel for writes that happened in a forked async context (native fetch
|
|
506
|
+
// .then() callbacks run outside the ALS scope) and is keyed per render
|
|
507
|
+
// token, so concurrent renders never mix data. The ALS sink (ssrSink) is
|
|
508
|
+
// NOT read here because withSsrStore reuses the ambient store when one is
|
|
509
|
+
// already open, so concurrent renderFullPage calls share a single sink and
|
|
510
|
+
// their writes would cross-contaminate. Every setSsrData write also lands
|
|
511
|
+
// in the slot, so the slot alone is a complete handoff source.
|
|
508
512
|
const slot = globalThis[`__vsk_ssr_data_${renderToken}`] || {};
|
|
509
513
|
const ssrData = {
|
|
510
|
-
...ssrSink.snapshot(),
|
|
511
514
|
...slot,
|
|
512
515
|
};
|
|
513
516
|
if (process.env.VESK_SSR_TRACE) {
|
|
514
|
-
console.error(`[render-trace] renderFullPage comp=${componentName} keys=${Object.keys(ssrData).join(',') || '∅'} slot=${Object.keys(slot).join(',') || '∅'}
|
|
517
|
+
console.error(`[render-trace] renderFullPage comp=${componentName} keys=${Object.keys(ssrData).join(',') || '∅'} slot=${Object.keys(slot).join(',') || '∅'}`);
|
|
515
518
|
}
|
|
516
519
|
let headHtml = rendered.head;
|
|
517
520
|
if (options.pageHead) {
|
|
@@ -565,17 +568,15 @@ ${bodyHtml}
|
|
|
565
568
|
${dataScriptBlock}${clientScript}</body>
|
|
566
569
|
</html>`;
|
|
567
570
|
return applyHtmlPlugins(docHtml, options.plugins, { sourcePath: options.sourcePath, url: options.__vesk?.url || undefined });
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
}
|
|
578
|
-
});
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
finally {
|
|
574
|
+
delete globalThis.__vsk_ssr;
|
|
575
|
+
delete globalThis[`__vsk_ssr_data_${renderToken}`];
|
|
576
|
+
dropSsrSlot(renderToken);
|
|
577
|
+
pruneSsrDataSlots();
|
|
578
|
+
}
|
|
579
|
+
return result;
|
|
579
580
|
}
|
|
580
581
|
export function renderPageStream(source, componentName, props = {}, registry = new Map(), options = {}) {
|
|
581
582
|
// One store for the whole stream. `next()` is driven by the consumer, so a
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vesk/compiler",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.93",
|
|
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.93",
|
|
24
24
|
"acorn": "^8.15.0",
|
|
25
25
|
"esrap": "^2.3.0",
|
|
26
26
|
"is-reference": "^3.0.0",
|