@vesk/compiler 0.2.10 → 0.2.12

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.
@@ -25,7 +25,7 @@ export declare function compileClient(source: string, _componentName: string | n
25
25
  * `resolveComponentName`, so callers don't need a second full parse just
26
26
  * to learn the name.
27
27
  */
28
- export declare function compileClientBoth(source: string, _componentName: string | null): {
28
+ export declare function compileClientBoth(source: string, _componentName: string | null, sourcePath?: string): {
29
29
  comp: string;
30
30
  hyd: string;
31
31
  name: string | null;
@@ -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,EAOjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAwCxD,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;AAoBD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,gBAAgB,GAAG,cAAc,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,CAwGlI;AAID,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAU5E;AAqjCD,iBAAS,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAe3C;AA2HD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO9C;AAoED,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,CAAA;CAAO,GAAG,MAAM,CAOnN;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAqBnI;AAED,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,UAAU,EAAE,CAAC"}
1
+ {"version":3,"file":"client-codegen.d.ts","sourceRoot":"","sources":["../src/client-codegen.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAuB,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAGL,cAAc,EAQd,gBAAgB,EAOjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAwCxD,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,CAwGlI;AAID,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAU5E;AA8kCD,iBAAS,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAe3C;AA2HD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO9C;AAoED,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,CAAA;CAAO,GAAG,MAAM,CAOnN;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAqBxJ;AAED,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,UAAU,EAAE,CAAC"}
@@ -6,8 +6,8 @@ import { StaticNode, TextNode, DynamicBinding, OpaqueDynamicRegion, MapRegion, W
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';
9
- import { extractRuntimeNames } from '@vesk/compiler/src/server-utils';
10
- import { importBindingPairs } from '@vesk/compiler/src/module-imports';
9
+ import { extractRuntimeNames, extractTopLevelNames } from '@vesk/compiler/src/server-utils';
10
+ import { importBindingPairs, localValueImportNames } from '@vesk/compiler/src/module-imports';
11
11
  import { stripTrackGeneric } from '@vesk/compiler/src/scan';
12
12
  import { inlineMdImportsFrom } from '@vesk/compiler/src/md-inline';
13
13
  import { stripTsTypes, hasTsSyntax } from '@vesk/compiler/src/strip-ts';
@@ -69,6 +69,36 @@ function containsJsx(node, depth = 0) {
69
69
  function printAst(ast) {
70
70
  return print(ast, containsJsx(ast) ? tsx() : ts()).code;
71
71
  }
72
+ /**
73
+ * Converts a bare effect block (`{ <setup>; effect(fn); }`, the shape every
74
+ * `ctx.effects.push` template produces) into an expression that runs the
75
+ * block immediately and evaluates to the effect handle:
76
+ * `(() => { <setup>; return effect(fn); })()`.
77
+ *
78
+ * Required when an effect is collected into a per-item effects array
79
+ * (`__e.push(<block>)`): a raw block in expression position parses as an
80
+ * object literal, so `let` declarations inside it are a SyntaxError that
81
+ * kills the entire client chunk — and with it hydration for the whole page.
82
+ * Falls back to a value-less IIFE when the block shape is unexpected.
83
+ */
84
+ function effectBlockToHandlerExpr(eff) {
85
+ try {
86
+ const ast = parse(eff);
87
+ const block = ast.body?.[0];
88
+ const stmts = block?.type === 'BlockStatement' ? block.body ?? [] : [];
89
+ const last = stmts[stmts.length - 1];
90
+ if (last?.type === 'ExpressionStatement' && last.expression) {
91
+ const returning = { ...block, body: [...stmts.slice(0, -1), { type: 'ReturnStatement', argument: last.expression }] };
92
+ const code = printAst(returning);
93
+ if (code)
94
+ return `(() => ${code})()`;
95
+ }
96
+ }
97
+ catch {
98
+ // fall through to the value-less IIFE below
99
+ }
100
+ return `(() => ${eff})()`;
101
+ }
72
102
  export function transformTracked(irNode, tracked) {
73
103
  const ast = irNode.ast;
74
104
  if (!ast)
@@ -260,7 +290,7 @@ function emitNode(ctx, node, tracked, effectsVar, parentVar, compPrefix = '__com
260
290
  if (node instanceof ComponentCall)
261
291
  return emitComponentCall(ctx, node, tracked, effectsVar, parentVar, compPrefix);
262
292
  if (node instanceof OpaqueDynamicRegion)
263
- return emitOpaque(ctx, node, tracked, parentVar);
293
+ return emitOpaque(ctx, node, tracked, effectsVar, parentVar);
264
294
  if (node instanceof MapRegion)
265
295
  return emitMap(ctx, node, tracked, parentVar);
266
296
  if (node instanceof ServerBlock)
@@ -573,10 +603,14 @@ function emitComponentCall(ctx, node, tracked, effectsVar, parentVar, compPrefix
573
603
  const propsObj = `{ ${propsEntries.join(', ')} }`;
574
604
  const v = ctx.n();
575
605
  const awaitKw = ctx.asyncComps.has(node.componentName) ? 'await ' : '';
606
+ // Member-expression tags (`<it.icon>`) carry the raw component-valued
607
+ // expression — invoke it directly; it is never a registry name.
608
+ const calleeExpr = node.calleeExpr ? `(${node.calleeExpr})` : null;
576
609
  if (ctx.hydrate) {
577
- const access = ctx.importedNames.has(node.componentName)
578
- ? node.componentName
579
- : `__components[${JSON.stringify(node.componentName)}]`;
610
+ const access = calleeExpr
611
+ ?? (ctx.importedNames.has(node.componentName)
612
+ ? node.componentName
613
+ : `__components[${JSON.stringify(node.componentName)}]`);
580
614
  const subScope = () => ctx.inTryBody
581
615
  ? `${ctx.walker}.subWalker(${parentVar})`
582
616
  : `${ctx.walker}.subWalker(${ctx.walker}.nextElement())`;
@@ -595,7 +629,7 @@ function emitComponentCall(ctx, node, tracked, effectsVar, parentVar, compPrefix
595
629
  ctx.push(`$f.appendChild(${childVar});`);
596
630
  }
597
631
  for (const eff of ctx.effects)
598
- ctx.push(effectsVar ? `${effectsVar}.push(${eff});` : eff);
632
+ ctx.push(effectsVar ? `${effectsVar}.push(${effectBlockToHandlerExpr(eff)});` : eff);
599
633
  ctx.effects = savedEffects;
600
634
  ctx.walker = savedWalker;
601
635
  ctx.push(`return $f; })();`);
@@ -618,12 +652,15 @@ function emitComponentCall(ctx, node, tracked, effectsVar, parentVar, compPrefix
618
652
  ctx.push(`$f.appendChild(${childVar});`);
619
653
  }
620
654
  for (const eff of ctx.effects)
621
- ctx.push(effectsVar ? `${effectsVar}.push(${eff});` : eff);
655
+ ctx.push(effectsVar ? `${effectsVar}.push(${effectBlockToHandlerExpr(eff)});` : eff);
622
656
  ctx.effects = savedEffects;
623
657
  ctx.push(`return $f; })();`);
624
658
  propsEntries.push(`children: ${frag}`);
625
659
  }
626
- if (ctx.importedNames.has(node.componentName)) {
660
+ if (calleeExpr) {
661
+ ctx.push(`const ${v} = ${calleeExpr}({ ${propsEntries.join(', ')} });`);
662
+ }
663
+ else if (ctx.importedNames.has(node.componentName)) {
627
664
  ctx.push(`const ${v} = ${awaitKw}${node.componentName}({ ${propsEntries.join(', ')} });`);
628
665
  }
629
666
  else {
@@ -711,7 +748,7 @@ function emitTryCatch(ctx, node, tracked, effectsVar, parentVar) {
711
748
  }`);
712
749
  return null;
713
750
  }
714
- function emitOpaque(ctx, node, tracked, parentVar) {
751
+ function emitOpaque(ctx, node, tracked, effTarget, parentVar) {
715
752
  const condExpr = transformTracked(node.condition, tracked);
716
753
  const hasElse = node.alternateNodes.length > 0;
717
754
  const anchor = ctx.n();
@@ -778,21 +815,32 @@ function emitOpaque(ctx, node, tracked, parentVar) {
778
815
  if (!hyd)
779
816
  ctx.push(`${parent}.appendChild(${endAnchor});`);
780
817
  ctx.push(`if (${condExpr}) { ${asyncKw}${conRenderName}(); }` + (hasElse ? ` else { ${asyncKw}${altRenderName}(); }` : ''));
781
- ctx.effects.push(`{
782
- let __iv = ${condExpr};
783
- let __first = true;
784
- effect(${ctx.isAsyncScope ? 'async () => {' : '() => {'}
818
+ const renderBranch = `if (__nv) { ${asyncKw}${conRenderName}(); }` + (hasElse ? ` else { ${asyncKw}${altRenderName}(); }` : '');
819
+ const effectBody = `effect(${ctx.isAsyncScope ? 'async () => {' : '() => {'}
785
820
  if (__first) { __first = false; return; }
786
821
  const __nv = ${condExpr};
787
822
  if (__nv !== __iv) {
788
823
  for (const e of ${effectsVar}) destroy_block(e);
789
824
  ${effectsVar}.length = 0;
790
825
  __cleanup(${anchor}, ${endAnchor});
791
- if (__nv) { ${asyncKw}${conRenderName}(); }` + (hasElse ? ` else { ${asyncKw}${altRenderName}(); }` : '') + `
826
+ ${renderBranch}
792
827
  __iv = __nv;
793
828
  }
794
- });
829
+ });`;
830
+ if (effTarget) {
831
+ ctx.push(`${effTarget}.push((() => {
832
+ let __iv = ${condExpr};
833
+ let __first = true;
834
+ return ${effectBody}
835
+ })());`);
836
+ }
837
+ else {
838
+ ctx.effects.push(`{
839
+ let __iv = ${condExpr};
840
+ let __first = true;
841
+ ${effectBody}
795
842
  }`);
843
+ }
796
844
  return null;
797
845
  }
798
846
  function emitWhileLoop(ctx, node, tracked, parentVar) {
@@ -1258,13 +1306,20 @@ function buildComponentMap(irRoot, hydrate = false) {
1258
1306
  const mapLines = [];
1259
1307
  mapLines.push(`const __components = {};`);
1260
1308
  const asyncComps = computeAsyncComponents(irRoot.components);
1309
+ // Top-level value bindings (`const MyIcon = Cpu`) live in module scope
1310
+ // alongside the component map, so a same-named JSX tag must invoke that
1311
+ // in-scope value directly — not a registry lookup. File-defined components
1312
+ // keep registry precedence.
1313
+ const componentNames = new Set(irRoot.components.map((c) => c.name));
1314
+ const topValueNames = extractTopLevelNames(irRoot.topLevelCode).filter((n) => !componentNames.has(n));
1315
+ const directNames = new Set([...extractRuntimeNames(irRoot.imports), ...localValueImportNames(irRoot.imports), ...topValueNames]);
1261
1316
  for (const comp of irRoot.components) {
1262
1317
  if (hydrate && isStaticComponent(comp)) {
1263
1318
  const stub = `(props, __registry, __hydrate) => { return __hydrate.root; }`;
1264
1319
  mapLines.push(`__components[${JSON.stringify(comp.name)}] = ${stub};`);
1265
1320
  continue;
1266
1321
  }
1267
- const code = generateComponent(comp, new Set(extractRuntimeNames(irRoot.imports)), hydrate, asyncComps);
1322
+ const code = generateComponent(comp, directNames, hydrate, asyncComps);
1268
1323
  mapLines.push(`__components[${JSON.stringify(comp.name)}] = ${code};`);
1269
1324
  }
1270
1325
  mapLines.push(`function __cleanup(start, end) {`);
@@ -1556,8 +1611,8 @@ export function compileClient(source, _componentName, options = {}) {
1556
1611
  if (options.sourcePath) {
1557
1612
  source = inlineMdImportsFrom(source, options.sourcePath, options.mdRoots || []);
1558
1613
  }
1559
- const ast = parse(source);
1560
- const ir = generateIR(ast, source);
1614
+ const ast = parse(source, options.sourcePath ? { filename: options.sourcePath } : {});
1615
+ const ir = generateIR(ast, source, options.sourcePath);
1561
1616
  return emitClientFromIR(ir, options);
1562
1617
  }
1563
1618
  /**
@@ -1568,14 +1623,14 @@ export function compileClient(source, _componentName, options = {}) {
1568
1623
  * `resolveComponentName`, so callers don't need a second full parse just
1569
1624
  * to learn the name.
1570
1625
  */
1571
- export function compileClientBoth(source, _componentName) {
1572
- const ast = parse(source);
1626
+ export function compileClientBoth(source, _componentName, sourcePath) {
1627
+ const ast = parse(source, sourcePath ? { filename: sourcePath } : {});
1573
1628
  // Downstream type-stripping mutates AST nodes in place (stripTsTypes),
1574
1629
  // so each emit mode needs its own tree. Cloning is far cheaper than the
1575
1630
  // second full acorn+TS parse this replaces.
1576
1631
  const hydAst = structuredClone(ast);
1577
- const ir = generateIR(ast, source);
1578
- const irHyd = generateIR(hydAst, source);
1632
+ const ir = generateIR(ast, source, sourcePath);
1633
+ const irHyd = generateIR(hydAst, source, sourcePath);
1579
1634
  const defaultComp = ir.components.find((c) => c.defaultExport);
1580
1635
  let name = null;
1581
1636
  if (defaultComp)
package/dist/errors.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export declare function levenshtein(a: string, b: string): number;
2
2
  export declare function didYouMean(name: string, candidates: string[], maxDistance?: number): string | null;
3
+ export declare function codeFrame(source: string, line: number, column: number, before?: number, after?: number): string;
3
4
  export interface VeskErrorOptions {
4
5
  file?: string;
5
6
  line?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAoBxD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,WAAW,GAAE,MAAU,GAAG,MAAM,GAAG,IAAI,CAWrG;AAqCD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAgBD,qBAAa,SAAU,SAAQ,KAAK;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;gBAEF,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,gBAAqB;IAYxD,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,GAAE,MAAM,EAAO,EAAE,OAAO,GAAE,gBAAqB,GAAG,SAAS;IA6BnG,MAAM,CAAC,SAAS,CAAC,OAAO,GAAE,gBAAqB,GAAG,SAAS;IAmB3D,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,SAAS;IAkBvF,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,SAAS;IAkBvF,MAAM,CAAC,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,GAAE,MAAM,EAAO,EAAE,OAAO,GAAE,gBAAqB,GAAG,SAAS;IAO3G,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,GAAE,MAAM,EAAO,EAAE,OAAO,GAAE,gBAAqB,GAAG,SAAS;IAWvG,MAAM,CAAC,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,SAAS;IAkB/G,QAAQ,IAAI,MAAM;IAgBlB,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAclC"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAoBxD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,WAAW,GAAE,MAAU,GAAG,MAAM,GAAG,IAAI,CAWrG;AAqCD,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAI,EAAE,KAAK,SAAI,GAAG,MAAM,CAkBrG;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAgBD,qBAAa,SAAU,SAAQ,KAAK;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;gBAEF,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,gBAAqB;IAYxD,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,GAAE,MAAM,EAAO,EAAE,OAAO,GAAE,gBAAqB,GAAG,SAAS;IA6BnG,MAAM,CAAC,SAAS,CAAC,OAAO,GAAE,gBAAqB,GAAG,SAAS;IAmB3D,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,SAAS;IAkBvF,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,SAAS;IAkBvF,MAAM,CAAC,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,GAAE,MAAM,EAAO,EAAE,OAAO,GAAE,gBAAqB,GAAG,SAAS;IAO3G,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,GAAE,MAAM,EAAO,EAAE,OAAO,GAAE,gBAAqB,GAAG,SAAS;IAWvG,MAAM,CAAC,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,SAAS;IAkB/G,QAAQ,IAAI,MAAM;IA0BlB,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAclC"}
package/dist/errors.js CHANGED
@@ -66,6 +66,26 @@ function findKeywordNumber(message, keywords) {
66
66
  }
67
67
  return 0;
68
68
  }
69
+ export function codeFrame(source, line, column, before = 5, after = 5) {
70
+ if (line <= 0)
71
+ return '';
72
+ const lines = source.split('\n');
73
+ const start = Math.max(1, line - before);
74
+ const end = Math.min(lines.length, line + after);
75
+ const width = String(end).length;
76
+ let out = '';
77
+ for (let i = start; i <= end; i++) {
78
+ const text = lines[i - 1] ?? '';
79
+ const ln = String(i).padStart(width, ' ');
80
+ out += `${ln} | ${text}\n`;
81
+ if (i === line) {
82
+ const pointerCol = Math.max(0, column - 1);
83
+ const prefix = ' '.repeat(width) + ' | ';
84
+ out += prefix + ' '.repeat(pointerCol) + '^\n';
85
+ }
86
+ }
87
+ return out.trimEnd();
88
+ }
69
89
  const VESK_BUILTINS = [
70
90
  'useFetch', 'useRouter', 'useParams', 'usePathname', 'useSearchParams',
71
91
  'useNavigate', 'useHead', 'useTitle',
@@ -204,8 +224,21 @@ export class VeskError extends Error {
204
224
  }
205
225
  toString() {
206
226
  let out = `[vesk] ${this.message}`;
207
- if (this.file)
208
- out += `\n File: ${this.file}${this.line ? `:${this.line}` : ''}`;
227
+ if (this.file) {
228
+ out += `\n File: ${this.file}`;
229
+ if (this.line) {
230
+ out += `:${this.line}`;
231
+ if (this.column)
232
+ out += `:${this.column}`;
233
+ }
234
+ if (this.line && this.column)
235
+ out += ` (line ${this.line}, column ${this.column})`;
236
+ else if (this.line)
237
+ out += ` (line ${this.line})`;
238
+ }
239
+ else if (this.line) {
240
+ out += `\n at line ${this.line}${this.column ? `, column ${this.column}` : ''}`;
241
+ }
209
242
  if (this.code)
210
243
  out += `\n\n${this.code}`;
211
244
  if (this.suggestions.length) {
@@ -6,5 +6,5 @@ export declare function getPropsType(params: Array<{
6
6
  typeAnnotation?: any;
7
7
  properties?: any[];
8
8
  }> | undefined | null, source: string): string | null;
9
- export declare function generateIR(ast: any, source: string): IRRoot;
9
+ export declare function generateIR(ast: any, source: string, filename?: string): IRRoot;
10
10
  //# sourceMappingURL=ir-generator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ir-generator.d.ts","sourceRoot":"","sources":["../src/ir-generator.ts"],"names":[],"mappings":"AACA,OAAO,EACL,MAAM,EAoBP,MAAM,uBAAuB,CAAC;AAgJ/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;AAuxBD,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAiO3D"}
1
+ {"version":3,"file":"ir-generator.d.ts","sourceRoot":"","sources":["../src/ir-generator.ts"],"names":[],"mappings":"AACA,OAAO,EACL,MAAM,EAoBP,MAAM,uBAAuB,CAAC;AA0L/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;AAg0BD,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAoO9E"}
@@ -1,5 +1,5 @@
1
1
  import { IRRoot, ComponentIR, StaticNode, TextNode, DynamicBinding, OpaqueDynamicRegion, MapRegion, WhileLoop, SwitchBlock, TryCatch, RuntimeStatement, ForLoop, TrackDecl, ComponentRef, ComponentCall, ServerBlock, ClientBlock, HeadBlock, Expression, SlotNode, } from '@vesk/compiler/src/ir';
2
- import { VeskError } from '@vesk/compiler/src/errors';
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';
5
5
  import { stripCodeTypes } from '@vesk/compiler/src/strip-ts';
@@ -83,6 +83,52 @@ function offsetToLineCol(source, offset) {
83
83
  }
84
84
  return { line, column };
85
85
  }
86
+ function calleeIsFetch(callee) {
87
+ if (!callee || typeof callee !== 'object')
88
+ return false;
89
+ const c = callee;
90
+ if (c.type === 'Identifier')
91
+ return c.name === 'useFetch';
92
+ if (c.type === 'MemberExpression') {
93
+ const object = c.object;
94
+ if (object && object.type === 'Identifier')
95
+ return object.name === 'useFetch';
96
+ }
97
+ return false;
98
+ }
99
+ /**
100
+ * Walks an ESTree subtree looking for a real `useFetch(...)` / `useFetch.stream(...)`
101
+ * call. String and template-literal *content* is not part of the expression tree,
102
+ * so `const md = \`useFetch.stream(...)\`` no longer counts as a fetch usage.
103
+ */
104
+ function estreeCallsFetch(ast) {
105
+ if (!ast)
106
+ return false;
107
+ const stack = [ast];
108
+ while (stack.length > 0) {
109
+ const candidate = stack.pop();
110
+ if (!candidate || typeof candidate !== 'object')
111
+ continue;
112
+ const node = candidate;
113
+ if ((node.type === 'CallExpression' || node.type === 'NewExpression') &&
114
+ calleeIsFetch(node.callee)) {
115
+ return true;
116
+ }
117
+ for (const key of Object.keys(node)) {
118
+ if (key === 'parent' || key === 'loc' || key === 'start' || key === 'end' || key === 'range')
119
+ continue;
120
+ const value = node[key];
121
+ if (Array.isArray(value)) {
122
+ for (const item of value)
123
+ stack.push(item);
124
+ }
125
+ else {
126
+ stack.push(value);
127
+ }
128
+ }
129
+ }
130
+ return false;
131
+ }
86
132
  function componentUsesFetch(nodes) {
87
133
  for (const node of nodes) {
88
134
  if (node instanceof ServerBlock || node instanceof ClientBlock) {
@@ -90,11 +136,11 @@ function componentUsesFetch(nodes) {
90
136
  return true;
91
137
  }
92
138
  else if (node instanceof RuntimeStatement) {
93
- if (node.raw.includes('useFetch(') || node.raw.includes('useFetch.'))
139
+ if (estreeCallsFetch(node.ast))
94
140
  return true;
95
141
  }
96
142
  else if (node instanceof DynamicBinding) {
97
- if (node.expression.raw.includes('useFetch(') || node.expression.raw.includes('useFetch.'))
143
+ if (estreeCallsFetch(node.expression.ast))
98
144
  return true;
99
145
  }
100
146
  else if (node instanceof MapRegion) {
@@ -574,12 +620,23 @@ function processJSXCallbackBody(source, body) {
574
620
  return exprToIR(source, body);
575
621
  }
576
622
  function processJSXElement(source, element) {
577
- const tagName = getJSXTagName(element.openingElement.name);
623
+ const nameNode = element.openingElement.name;
624
+ const tagName = getJSXTagName(nameNode);
578
625
  const selfClosing = element.openingElement.selfClosing;
579
626
  if (tagName === 'Head') {
580
627
  const children = selfClosing ? [] : processJSXChildren(source, element.children || []);
581
628
  return [new HeadBlock(children)];
582
629
  }
630
+ // A dotted JSX tag (`<it.icon>`, `<Foo.Bar>`) is a component-valued member
631
+ // expression — never an HTML element (dots are not valid in tag names) and
632
+ // never a registry key. Carry the raw expression so codegen invokes the
633
+ // actual in-scope value instead of `document.createElement("it.icon")` or a
634
+ // registry lookup by dotted string.
635
+ if (nameNode && nameNode.type === 'JSXMemberExpression') {
636
+ const { props, spreadProps } = extractProps(source, element);
637
+ const children = selfClosing ? [] : processJSXChildren(source, element.children || []);
638
+ return [new ComponentCall(tagName, props, children, spreadProps, element.start, getSource(source, nameNode))];
639
+ }
583
640
  if (!isHTMLTag(tagName) && selfClosing) {
584
641
  const { props, spreadProps } = extractProps(source, element);
585
642
  return [new ComponentCall(tagName, props, [], spreadProps, element.start)];
@@ -830,7 +887,7 @@ function processTryStatement(source, stmt) {
830
887
  const catchParamName = stmt.handler?.param?.name ?? null;
831
888
  return [new TryCatch(bodyTemplate, catchBody, catchParamName)];
832
889
  }
833
- function processStatementModeBody(source, bodyStmts) {
890
+ function processStatementModeBody(source, bodyStmts, filename) {
834
891
  const nodes = [];
835
892
  for (let i = 0; i < bodyStmts.length; i++) {
836
893
  const stmt = bodyStmts[i];
@@ -859,7 +916,7 @@ function processStatementModeBody(source, bodyStmts) {
859
916
  else if (stmt.type === 'VeskBlock') {
860
917
  if (stmt.tag === 'empty')
861
918
  continue;
862
- const inner = processStatementModeBody(source, stmt.body);
919
+ const inner = processStatementModeBody(source, stmt.body, filename);
863
920
  if (stmt.tag === 'server') {
864
921
  nodes.push(new ServerBlock(inner));
865
922
  }
@@ -879,6 +936,18 @@ function processStatementModeBody(source, bodyStmts) {
879
936
  nodes.push(new ComponentRef(refName));
880
937
  }
881
938
  else if (stmt.type === 'IfStatement') {
939
+ // Guard-clause early return (`if (c) return X` with no else): everything
940
+ // after this statement becomes the alternate branch, mirroring
941
+ // expression-mode `buildGuardChain`. Without this the `return` is
942
+ // silently swallowed and execution falls through into code that
943
+ // assumes the guard held.
944
+ const guardArg = !stmt.alternate ? getReturnArgument(stmt.consequent) : null;
945
+ if (guardArg) {
946
+ const consequent = exprToIR(source, guardArg);
947
+ const alternate = processStatementModeBody(source, bodyStmts.slice(i + 1), filename);
948
+ nodes.push(new OpaqueDynamicRegion(toExpression(source, stmt.test), consequent, alternate));
949
+ break;
950
+ }
882
951
  nodes.push(...processIfStatement(source, stmt));
883
952
  }
884
953
  else if (stmt.type === 'ForOfStatement') {
@@ -886,7 +955,7 @@ function processStatementModeBody(source, bodyStmts) {
886
955
  let consumed = 1;
887
956
  const next = bodyStmts[i + 1];
888
957
  if (next && next.type === 'VeskBlock' && next.tag === 'empty') {
889
- alternate = processStatementModeBody(source, next.body);
958
+ alternate = processStatementModeBody(source, next.body, filename);
890
959
  consumed = 2;
891
960
  }
892
961
  nodes.push(...processForStatement(source, stmt, alternate));
@@ -926,7 +995,8 @@ function processStatementModeBody(source, bodyStmts) {
926
995
  }
927
996
  }
928
997
  else if (stmt.type === 'ClassDeclaration') {
929
- throw VeskError.classDecl();
998
+ const { line, column } = offsetToLineCol(source, stmt.start ?? 0);
999
+ throw VeskError.classDecl({ file: filename || '', line, column, code: codeFrame(source, line, column) });
930
1000
  }
931
1001
  else {
932
1002
  const raw = getSource(source, stmt);
@@ -953,20 +1023,36 @@ function extractStyle(body) {
953
1023
  }
954
1024
  return { body: filtered, css: cssParts.join('\n') || null };
955
1025
  }
956
- function validateBlocks(compName, isClient, body) {
1026
+ function validateBlocks(compName, isClient, body, file, source) {
957
1027
  for (const node of body) {
958
1028
  if (isClient) {
959
1029
  if (node instanceof ServerBlock) {
960
- throw VeskError.serverBlockInClient(compName);
1030
+ const opts = file ? { file } : {};
1031
+ if (source && node.start !== undefined) {
1032
+ const pos = node.start;
1033
+ const { line, column } = offsetToLineCol(source, pos);
1034
+ opts.line = line;
1035
+ opts.column = column;
1036
+ opts.code = codeFrame(source, line, column);
1037
+ }
1038
+ throw VeskError.serverBlockInClient(compName, opts);
961
1039
  }
962
1040
  }
963
1041
  else {
964
1042
  if (node instanceof ClientBlock) {
965
- throw VeskError.clientBlockInServer(compName);
1043
+ const opts = file ? { file } : {};
1044
+ if (source && node.start !== undefined) {
1045
+ const pos = node.start;
1046
+ const { line, column } = offsetToLineCol(source, pos);
1047
+ opts.line = line;
1048
+ opts.column = column;
1049
+ opts.code = codeFrame(source, line, column);
1050
+ }
1051
+ throw VeskError.clientBlockInServer(compName, opts);
966
1052
  }
967
1053
  }
968
1054
  if (node instanceof StaticNode || node instanceof ServerBlock || node instanceof ClientBlock) {
969
- validateBlocks(compName, isClient, node.children || []);
1055
+ validateBlocks(compName, isClient, node.children || [], file, source);
970
1056
  }
971
1057
  }
972
1058
  }
@@ -1024,8 +1110,9 @@ function idxOfQuote(s, from) {
1024
1110
  }
1025
1111
  return -1;
1026
1112
  }
1027
- export function generateIR(ast, source) {
1113
+ export function generateIR(ast, source, filename) {
1028
1114
  __vskAnnotations = ast.__vskAnnotations ?? [];
1115
+ const file = filename || '';
1029
1116
  const components = [];
1030
1117
  const imports = [];
1031
1118
  const importedNames = new Set();
@@ -1089,7 +1176,8 @@ export function generateIR(ast, source) {
1089
1176
  // handled below
1090
1177
  }
1091
1178
  else if (inner.type === 'ClassDeclaration') {
1092
- throw VeskError.classDecl();
1179
+ const { line, column } = offsetToLineCol(source, inner.start ?? 0);
1180
+ throw VeskError.classDecl({ file, line, column, code: codeFrame(source, line, column) });
1093
1181
  }
1094
1182
  else if (inner.type === 'TSEnumDeclaration') {
1095
1183
  const code = processEnum(inner, source, exported);
@@ -1106,9 +1194,9 @@ export function generateIR(ast, source) {
1106
1194
  const bodyStmts = inner.body.body;
1107
1195
  const isClientComp = !!inner.client;
1108
1196
  if (isStatementMode(bodyStmts)) {
1109
- const raw = processStatementModeBody(source, bodyStmts);
1197
+ const raw = processStatementModeBody(source, bodyStmts, file);
1110
1198
  const { body, css } = extractStyle(raw);
1111
- validateBlocks(name, isClientComp, body);
1199
+ validateBlocks(name, isClientComp, body, file, source);
1112
1200
  const comp = new ComponentIR(name, paramNames, body, { mode: 'statement', exported, defaultExport, isClient: inner.client, isAsync: inner.async, ssrAwait: componentUsesFetch(body), propsType });
1113
1201
  comp.style = css;
1114
1202
  components.push(comp);
@@ -1119,7 +1207,7 @@ export function generateIR(ast, source) {
1119
1207
  const preamble = [];
1120
1208
  for (const stmt of bodyStmts) {
1121
1209
  if (stmt.type === 'VeskBlock') {
1122
- const innerBody = processStatementModeBody(source, stmt.body);
1210
+ const innerBody = processStatementModeBody(source, stmt.body, file);
1123
1211
  if (stmt.tag === 'server') {
1124
1212
  preamble.push(new ServerBlock(innerBody));
1125
1213
  }
@@ -1145,7 +1233,8 @@ export function generateIR(ast, source) {
1145
1233
  guardClauses.push(stmt);
1146
1234
  }
1147
1235
  else if (stmt.type === 'ClassDeclaration') {
1148
- throw VeskError.classDecl();
1236
+ const { line, column } = offsetToLineCol(source, stmt.start ?? 0);
1237
+ throw VeskError.classDecl({ file, line, column, code: codeFrame(source, line, column) });
1149
1238
  }
1150
1239
  else {
1151
1240
  const raw = getSource(source, stmt);
@@ -1155,7 +1244,7 @@ export function generateIR(ast, source) {
1155
1244
  }
1156
1245
  const guardBody = buildGuardChain(source, guardClauses, mainReturn);
1157
1246
  const { body, css } = extractStyle([...preamble, ...guardBody]);
1158
- validateBlocks(name, isClientComp, body);
1247
+ validateBlocks(name, isClientComp, body, file, source);
1159
1248
  const comp = new ComponentIR(name, paramNames, body, { exported, defaultExport, isClient: inner.client, isAsync: inner.async, ssrAwait: componentUsesFetch(body), propsType });
1160
1249
  comp.style = css;
1161
1250
  components.push(comp);
@@ -1171,7 +1260,7 @@ export function generateIR(ast, source) {
1171
1260
  if (!asyncNames.has(childName))
1172
1261
  continue;
1173
1262
  const { line, column } = offsetToLineCol(source, start >= 0 ? start : 0);
1174
- throw VeskError.asyncChildInSyncParent(comp.name, childName, { line, column });
1263
+ throw VeskError.asyncChildInSyncParent(comp.name, childName, { file, line, column, code: codeFrame(source, line, column) });
1175
1264
  }
1176
1265
  }
1177
1266
  const autoImportable = [
package/dist/ir.d.ts CHANGED
@@ -89,10 +89,17 @@ export declare class ComponentCall {
89
89
  spreadProps: Expression[];
90
90
  children: IRNode[];
91
91
  start: number;
92
+ /**
93
+ * Raw JS expression for the component value when the JSX tag is a member
94
+ * expression (e.g. `<it.icon>` → `it.icon`). Dotted tags can never be HTML
95
+ * elements or registry names, so codegen must invoke this expression
96
+ * directly instead of resolving `componentName` through the registry.
97
+ */
98
+ calleeExpr: string | null;
92
99
  constructor(componentName: string, props: {
93
100
  name: string;
94
101
  value: Expression;
95
- }[], children?: IRNode[], spreadProps?: Expression[], start?: number);
102
+ }[], children?: IRNode[], spreadProps?: Expression[], start?: number, calleeExpr?: string | null);
96
103
  }
97
104
  export declare class SlotNode {
98
105
  constructor();
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
@@ -120,12 +120,20 @@ export class ComponentCall {
120
120
  spreadProps;
121
121
  children;
122
122
  start;
123
- constructor(componentName, props, children = [], spreadProps = [], start = -1) {
123
+ /**
124
+ * Raw JS expression for the component value when the JSX tag is a member
125
+ * expression (e.g. `<it.icon>` → `it.icon`). Dotted tags can never be HTML
126
+ * elements or registry names, so codegen must invoke this expression
127
+ * directly instead of resolving `componentName` through the registry.
128
+ */
129
+ calleeExpr;
130
+ constructor(componentName, props, children = [], spreadProps = [], start = -1, calleeExpr = null) {
124
131
  this.componentName = componentName;
125
132
  this.props = props;
126
133
  this.children = children;
127
134
  this.spreadProps = spreadProps;
128
135
  this.start = start;
136
+ this.calleeExpr = calleeExpr;
129
137
  }
130
138
  }
131
139
  export class SlotNode {
@@ -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;AAKtC,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;AAYD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,cAAc,EAAE,CAAA;CAAE,CA0KpG;AAED,wBAAgB,gBAAgB,IAAI,OAAO,KAAK,CAAC,MAAM,CAEtD;AAED,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAczE"}
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;AAMtC,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;AAYD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,cAAc,EAAE,CAAA;CAAE,CA0KpG;AAED,wBAAgB,gBAAgB,IAAI,OAAO,KAAK,CAAC,MAAM,CAEtD;AAED,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CA+CzE"}
package/dist/parser.js CHANGED
@@ -2,6 +2,7 @@ import * as acorn from 'acorn';
2
2
  import { tsPlugin } from './acorn-ts-plugin/index.js';
3
3
  import { VeskParserPlugin } from '@vesk/compiler/src/vesk-plugin';
4
4
  import { containsForOfIn } from '@vesk/compiler/src/scan';
5
+ import { VeskError, codeFrame } from '@vesk/compiler/src/errors';
5
6
  function isIdentChar(code) {
6
7
  return ((code >= 97 && code <= 122) || (code >= 65 && code <= 90) || (code >= 48 && code <= 57) || code === 95 || code === 36);
7
8
  }
@@ -228,15 +229,50 @@ export function createBaseParser() {
228
229
  export function parse(source, options = {}) {
229
230
  const ParserClass = createBaseParser();
230
231
  const { code, annotations } = preprocessForClauses(source);
231
- const ast = ParserClass.parse(code, {
232
- ecmaVersion: 'latest',
233
- sourceType: 'module',
234
- locations: true,
235
- ranges: true,
236
- ...(options.filename ? { sourceFilename: options.filename } : {}),
237
- });
238
- if (annotations.length > 0) {
239
- ast.__vskAnnotations = annotations;
232
+ try {
233
+ const ast = ParserClass.parse(code, {
234
+ ecmaVersion: 'latest',
235
+ sourceType: 'module',
236
+ locations: true,
237
+ ranges: true,
238
+ ...(options.filename ? { sourceFilename: options.filename } : {}),
239
+ });
240
+ if (annotations.length > 0) {
241
+ ast.__vskAnnotations = annotations;
242
+ }
243
+ return ast;
244
+ }
245
+ catch (e) {
246
+ const err = e;
247
+ // Acorn's SyntaxError already contains "(line:column)" in message and loc
248
+ let line = 0;
249
+ let column = 0;
250
+ if (err.loc && typeof err.loc.line === 'number') {
251
+ line = err.loc.line;
252
+ column = (err.loc.column ?? 0) + 1;
253
+ }
254
+ else {
255
+ const m = err.message.match(/\((\d+):(\d+)\)/);
256
+ if (m) {
257
+ line = parseInt(m[1], 10);
258
+ column = parseInt(m[2], 10) + 1;
259
+ }
260
+ }
261
+ const filename = options.filename || '';
262
+ const cleanMessage = err.message.replace(/\s*\(\d+:\d+\)\s*$/, '');
263
+ const frame = line > 0 ? codeFrame(source, line, column, 5, 5) : '';
264
+ const hint = filename ? ` in ${filename}` : '';
265
+ throw new VeskError(`${cleanMessage}${hint}`, {
266
+ file: filename,
267
+ line,
268
+ column,
269
+ code: frame,
270
+ nextSteps: [
271
+ 'Check the line indicated by the ^ marker for missing brackets, quotes, or JSX syntax.',
272
+ 'If you wrote literal { or } inside JSX text, escape them as {\'{\'} and {\'}\'} or use &lbrace; &rbrace;.',
273
+ 'Ensure all JSX tags are properly closed and component names start with an uppercase letter.',
274
+ ],
275
+ tip: 'Vesk parses with Acorn + TypeScript + JSX — use the code frame above to locate the error.',
276
+ });
240
277
  }
241
- return ast;
242
278
  }
@@ -1 +1 @@
1
- {"version":3,"file":"server-jsgen.d.ts","sourceRoot":"","sources":["../src/server-jsgen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAOzE,OAAO,EAA4E,KAAK,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAW/I,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,CA8CjJ;AA4SD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAmD1F;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CA4BhG"}
1
+ {"version":3,"file":"server-jsgen.d.ts","sourceRoot":"","sources":["../src/server-jsgen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAOzE,OAAO,EAA4E,KAAK,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAW/I,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,CA8CjJ;AAyTD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAmD1F;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAkChG"}
@@ -358,14 +358,27 @@ function componentCallToJS(node, importedNames, isAsync = false, tracked) {
358
358
  }
359
359
  const propsObj = `{ ${propsEntries.join(', ')} }`;
360
360
  const compName = node.componentName;
361
- const isImported = importedNames && importedNames.has(compName);
362
- const callee = isImported ? compName : `__registry.get(${JSON.stringify(compName)})`;
361
+ // Member-expression tags (`<it.icon>`) carry the raw component-valued
362
+ // expression — invoke it directly; it is never a registry name.
363
+ const callee = node.calleeExpr
364
+ ? `(${node.calleeExpr})`
365
+ : (() => {
366
+ const isImported = importedNames && importedNames.has(compName);
367
+ return isImported ? compName : `__registry.get(${JSON.stringify(compName)})`;
368
+ })();
363
369
  const awaitKw = isAsync ? 'await ' : '';
370
+ // Hoist the callee so its defining-file scope (`__veskScope`, attached in
371
+ // server-render.ts) is preferred over the merged caller scope. Same-named
372
+ // top-level bindings in different `.vsk` files must not leak across
373
+ // components during SSR.
374
+ const calleeVar = `__cc${nextVskId()}`;
375
+ lines.push(`const ${calleeVar} = ${callee};`);
376
+ const callExpr = `${awaitKw}${calleeVar}(${propsObj}, __registry, (${calleeVar}.__veskScope || __vesk))`;
364
377
  if (__vskHydrate) {
365
- lines.push(`__out.push('<!--vsk--><div>' + (${awaitKw}${callee}(${propsObj}, __registry, __vesk) || '') + '</div>');`);
378
+ lines.push(`__out.push('<!--vsk--><div>' + (${callExpr} || '') + '</div>');`);
366
379
  }
367
380
  else {
368
- lines.push(`__out.push(${awaitKw}${callee}(${propsObj}, __registry, __vesk) || '');`);
381
+ lines.push(`__out.push(${callExpr} || '');`);
369
382
  }
370
383
  return lines.join('\n');
371
384
  }
@@ -424,8 +437,14 @@ export function buildComponentMap(irRoot, useSharedScope) {
424
437
  const map = new Map();
425
438
  const runtimeNames = extractRuntimeNames(irRoot.imports);
426
439
  const localValueNames = localValueImportNames(irRoot.imports);
427
- const importedNames = new Set([...runtimeNames, ...localValueNames]);
428
440
  const topNames = extractTopLevelNames(irRoot.topLevelCode);
441
+ // Top-level value bindings (`const MyIcon = Cpu`) are destructured into
442
+ // every component scope from `__vesk` (see `scopeDecl` below), so a
443
+ // same-named JSX tag must invoke that in-scope value directly — not a
444
+ // registry lookup. File-defined components keep registry precedence.
445
+ const componentNames = new Set(irRoot.components.map((c) => c.name));
446
+ const topValueNames = topNames.filter((n) => !componentNames.has(n));
447
+ const importedNames = new Set([...runtimeNames, ...localValueNames, ...topValueNames]);
429
448
  const hasTracked = irRoot.components.some((c) => c.body.some((n) => n instanceof TrackDecl));
430
449
  const extraNames = hasTracked ? ['get', 'set', 'track'] : [];
431
450
  const allNames = [...new Set([...runtimeNames, ...topNames, ...extraNames, ...localValueNames])];
@@ -8,6 +8,7 @@ export declare function ssg(source: string, componentName?: string, customProps?
8
8
  registry?: Map<string, Function>;
9
9
  cssUrl?: string;
10
10
  cssUrls?: string[];
11
+ sourcePath?: string;
11
12
  [key: string]: unknown;
12
13
  }): Promise<SSGResult>;
13
14
  export declare function buildDataScripts(ssrProps: Record<string, unknown> | null | undefined, ssrData: Record<string, unknown>, external?: ExternalDataScript | null): string[];
@@ -1 +1 @@
1
- {"version":3,"file":"server-render.d.ts","sourceRoot":"","sources":["../src/server-render.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,SAAS,EAAE,eAAe,EAAqB,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAiBvJ,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,iBAAiB,CAEhG;AAsCD,wBAAgB,MAAM,CACpB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,QAAQ,GAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAa,EAC3C,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACpC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkC1B;AAED,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,QAAQ,GAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAa,EAC3C,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACpC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAiE9C;AAcD,wBAAsB,GAAG,CACvB,MAAM,EAAE,MAAM,EACd,aAAa,CAAC,EAAE,MAAM,EACtB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAO,GAC9G,OAAO,CAAC,SAAS,CAAC,CA8DpB;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,EACpD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,QAAQ,CAAC,EAAE,kBAAkB,GAAG,IAAI,GACnC,MAAM,EAAE,CAuBV;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,QAAQ,GAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAa,EAC3C,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,MAAM,CAAC,CAoEjB;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,QAAQ,GAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAa,EAC3C,OAAO,GAAE,eAAoB,GAC5B,cAAc,CAAC,MAAM,CAAC,CAsFxB"}
1
+ {"version":3,"file":"server-render.d.ts","sourceRoot":"","sources":["../src/server-render.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,SAAS,EAAE,eAAe,EAAqB,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AA6BvJ,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,iBAAiB,CAEhG;AA6CD,wBAAgB,MAAM,CACpB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,QAAQ,GAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAa,EAC3C,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACpC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkC1B;AAED,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,QAAQ,GAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAa,EAC3C,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACpC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAiE9C;AAcD,wBAAsB,GAAG,CACvB,MAAM,EAAE,MAAM,EACd,aAAa,CAAC,EAAE,MAAM,EACtB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAO,GACnI,OAAO,CAAC,SAAS,CAAC,CA8DpB;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,EACpD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,QAAQ,CAAC,EAAE,kBAAkB,GAAG,IAAI,GACnC,MAAM,EAAE,CAuBV;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,QAAQ,GAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAa,EAC3C,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,MAAM,CAAC,CAoEjB;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,QAAQ,GAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAa,EAC3C,OAAO,GAAE,eAAoB,GAC5B,cAAc,CAAC,MAAM,CAAC,CA2FxB"}
@@ -12,6 +12,15 @@ import { collectVskImportPaths } from '@vesk/compiler/src/vsk-imports';
12
12
  import { inlineMdImportsFrom, guessProjectRoots } from '@vesk/compiler/src/md-inline';
13
13
  import { withSsrStore, ssrSink } from '@vesk/compiler/src/ssr-store';
14
14
  import { applyLocalModuleImports } from '@vesk/compiler/src/module-imports';
15
+ // Registry-hoisted sub components are invoked with the caller's `__vesk`,
16
+ // but each component's scope declaration destructures its DEFINING file's
17
+ // top-level bindings. When two `.vsk` files declare the same top-level name
18
+ // (e.g. both define `const stages`), the hoist-first-wins merge below would
19
+ // hand every component the wrong file's value. Attaching the defining scope
20
+ // lets every call site prefer the callee's own bindings.
21
+ function scopedVesk(fn, fallback) {
22
+ return fn.__veskScope || fallback;
23
+ }
15
24
  export function compileFile(source, options) {
16
25
  return compileFileInternal(source, options?.sourcePath, new Set());
17
26
  }
@@ -20,9 +29,10 @@ function compileFileInternal(source, sourcePath, seenImportFiles) {
20
29
  const dir = dirname(sourcePath);
21
30
  source = inlineMdImportsFrom(source, sourcePath, guessProjectRoots(dir));
22
31
  }
23
- const ast = parse(source);
24
- const ir = generateIR(ast, source);
32
+ const ast = parse(source, sourcePath ? { filename: sourcePath } : {});
33
+ const ir = generateIR(ast, source, sourcePath);
25
34
  const componentMap = buildComponentMap(ir, true);
35
+ const ownComponentNames = ir.components.map((c) => c.name);
26
36
  const __vesk = loadRuntimeImports(ir.imports);
27
37
  applyLocalModuleImports(__vesk, ir.imports, sourcePath);
28
38
  if (sourcePath) {
@@ -52,6 +62,13 @@ function compileFileInternal(source, sourcePath, seenImportFiles) {
52
62
  }
53
63
  }
54
64
  evalTopLevelCode(transformTopLevelForActions(ir.topLevelCode, 'server'), __vesk);
65
+ // Sub-file components arrive pre-scoped from the recursive call below;
66
+ // scope the remaining (own) components to this file's bindings.
67
+ for (const name of ownComponentNames) {
68
+ const fn = componentMap.get(name);
69
+ if (fn && !fn.__veskScope)
70
+ fn.__veskScope = __vesk;
71
+ }
55
72
  return { ir, componentMap, __vesk };
56
73
  }
57
74
  export function render(source, componentName, props = {}, registry = new Map(), options = {}) {
@@ -72,7 +89,7 @@ export function render(source, componentName, props = {}, registry = new Map(),
72
89
  return withSsrStore(() => (async () => {
73
90
  let bodyHtml;
74
91
  try {
75
- bodyHtml = await renderFn(props, fullRegistry, __vesk);
92
+ bodyHtml = await renderFn(props, fullRegistry, scopedVesk(renderFn, __vesk));
76
93
  }
77
94
  finally {
78
95
  delete globalThis.__vsk_ssr;
@@ -83,7 +100,7 @@ export function render(source, componentName, props = {}, registry = new Map(),
83
100
  }
84
101
  let bodyHtml;
85
102
  try {
86
- bodyHtml = withSsrStore(() => renderFn(props, fullRegistry, __vesk));
103
+ bodyHtml = withSsrStore(() => renderFn(props, fullRegistry, scopedVesk(renderFn, __vesk)));
87
104
  }
88
105
  finally {
89
106
  delete globalThis.__vsk_ssr;
@@ -119,7 +136,7 @@ export function renderPage(source, componentName, props = {}, registry = new Map
119
136
  return withSsrStore(() => (async () => {
120
137
  let bodyHtml;
121
138
  try {
122
- bodyHtml = await renderFn(ssrProps, fullRegistry, __vesk);
139
+ bodyHtml = await renderFn(ssrProps, fullRegistry, scopedVesk(renderFn, __vesk));
123
140
  }
124
141
  finally {
125
142
  delete globalThis.__vsk_ssr;
@@ -134,7 +151,7 @@ export function renderPage(source, componentName, props = {}, registry = new Map
134
151
  }
135
152
  let bodyHtml;
136
153
  try {
137
- bodyHtml = withSsrStore(() => renderFn(ssrProps, fullRegistry, __vesk));
154
+ bodyHtml = withSsrStore(() => renderFn(ssrProps, fullRegistry, scopedVesk(renderFn, __vesk)));
138
155
  }
139
156
  finally {
140
157
  delete globalThis.__vsk_ssr;
@@ -175,8 +192,8 @@ function clearSsrCells(token) {
175
192
  }
176
193
  }
177
194
  export async function ssg(source, componentName, customProps, options = {}) {
178
- const ast = parse(source);
179
- const ir = generateIR(ast, source);
195
+ const ast = parse(source, options.sourcePath ? { filename: options.sourcePath } : {});
196
+ const ir = generateIR(ast, source, options.sourcePath);
180
197
  if (!componentName) {
181
198
  const defaultComp = ir.components.find((c) => c.defaultExport);
182
199
  const exportedComp = ir.components.find((c) => c.exported);
@@ -268,7 +285,7 @@ export async function renderFullPage(source, componentName, props = {}, registry
268
285
  let ssrProps = { ...props };
269
286
  let serializedProps = null;
270
287
  const cached = options.cached || undefined;
271
- const ir = cached ? cached.ir : generateIR(parse(source), source);
288
+ const ir = cached ? cached.ir : generateIR(parse(source, options.sourcePath ? { filename: options.sourcePath } : {}), source, options.sourcePath);
272
289
  if (ir.loadFn) {
273
290
  const __vesk = options.__vesk || cached?.__vesk || loadRuntimeImports(ir.imports);
274
291
  const loadResult = await callLoadFunction(ir.loadFn, ssrProps, __vesk);
@@ -334,7 +351,7 @@ ${dataScriptBlock}${clientScript}</body>
334
351
  export function renderPageStream(source, componentName, props = {}, registry = new Map(), options = {}) {
335
352
  async function* raw() {
336
353
  const cached = options.cached || undefined;
337
- const ir = cached ? cached.ir : generateIR(parse(source), source);
354
+ const ir = cached ? cached.ir : generateIR(parse(source, options.sourcePath ? { filename: options.sourcePath } : {}), source, options.sourcePath);
338
355
  let ssrProps = { ...props };
339
356
  let serializedProps = null;
340
357
  let __vesk = options.__vesk || cached?.__vesk || null;
@@ -394,7 +411,7 @@ export function renderPageStream(source, componentName, props = {}, registry = n
394
411
  const renderToken = globalThis.__vsk_ssr_token;
395
412
  let bodyHtml;
396
413
  try {
397
- bodyHtml = await Promise.resolve(renderFn(ssrProps, fullRegistry, __vesk));
414
+ bodyHtml = await Promise.resolve(renderFn(ssrProps, fullRegistry, scopedVesk(renderFn, __vesk)));
398
415
  }
399
416
  finally {
400
417
  delete globalThis.__vsk_ssr;
@@ -404,7 +421,12 @@ export function renderPageStream(source, componentName, props = {}, registry = n
404
421
  const ssrData = { ...ssrSink.snapshot() };
405
422
  const dataScripts = buildDataScripts(ssrProps, ssrData, options.externalDataScript);
406
423
  const dataScriptBlock = dataScripts.length > 0 ? '\n' + dataScripts.join('\n') : '';
407
- yield `\n</div>${dataScriptBlock}</body>\n</html>\n`;
424
+ // Mirror renderFullPage: a full document must carry the client bundle or
425
+ // the page can never hydrate (dev-server streams hit this path).
426
+ const clientScript = options.clientScriptUrl
427
+ ? `\t<script type="module" src="${options.clientScriptUrl}"></script>\n`
428
+ : '';
429
+ yield `\n</div>${dataScriptBlock}${clientScript}</body>\n</html>\n`;
408
430
  }
409
431
  const gen = raw();
410
432
  async function* scoped() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vesk/compiler",
3
- "version": "0.2.10",
3
+ "version": "0.2.12",
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.10",
23
+ "@vesk/types": "^0.2.12",
24
24
  "acorn": "^8.15.0",
25
25
  "esrap": "^2.3.0",
26
26
  "is-reference": "^3.0.0",