@vesk/compiler 0.2.8 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"client-codegen.d.ts","sourceRoot":"","sources":["../src/client-codegen.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAuB,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAGL,cAAc,EAQd,gBAAgB,EAOjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAuCxD,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;AA2DD,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;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"}
@@ -7,6 +7,7 @@ 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
9
  import { extractRuntimeNames } from '@vesk/compiler/src/server-utils';
10
+ import { importBindingPairs } from '@vesk/compiler/src/module-imports';
10
11
  import { stripTrackGeneric } from '@vesk/compiler/src/scan';
11
12
  import { inlineMdImportsFrom } from '@vesk/compiler/src/md-inline';
12
13
  import { stripTsTypes, hasTsSyntax } from '@vesk/compiler/src/strip-ts';
@@ -1532,7 +1533,16 @@ function emitClientFromIR(ir, options) {
1532
1533
  runtimeNames.push(name);
1533
1534
  }
1534
1535
  }
1535
- const runtimeImport = `import { ${runtimeNames.join(', ')} } from '@vesk/runtime';`;
1536
+ // A name the file already binds via its own imports (e.g. a local module that
1537
+ // exports its own `effect`) shadows the runtime binding — never inject a
1538
+ // duplicate `import { x } from '@vesk/runtime'`. Mirrors SSR, so the two agree.
1539
+ const boundLocally = new Set();
1540
+ for (const imp of ir.imports) {
1541
+ for (const pair of importBindingPairs(imp))
1542
+ boundLocally.add(pair.local);
1543
+ }
1544
+ const shadowedRuntimeNames = runtimeNames.filter(n => !boundLocally.has(n));
1545
+ const runtimeImport = `import { ${shadowedRuntimeNames.length > 0 ? shadowedRuntimeNames.join(', ') : 'destroy_block'} } from '@vesk/runtime';`;
1536
1546
  const moduleCode = `
1537
1547
  ${runtimeImport}
1538
1548
  ${importLines}
@@ -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;AA+I/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;AAmsBD,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CA0N3D"}
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"}
@@ -4,6 +4,7 @@ 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';
6
6
  import { stripTypeImport } from '@vesk/compiler/src/vsk-imports';
7
+ import { importBindingPairs } from '@vesk/compiler/src/module-imports';
7
8
  import { collectCalledIdentifiers, extractImportNames, importModuleTarget } from '@vesk/compiler/src/tokens';
8
9
  let __vskAnnotations = [];
9
10
  function parseExprNode(text) {
@@ -89,11 +90,11 @@ function componentUsesFetch(nodes) {
89
90
  return true;
90
91
  }
91
92
  else if (node instanceof RuntimeStatement) {
92
- if (node.raw.includes('useFetch('))
93
+ if (node.raw.includes('useFetch(') || node.raw.includes('useFetch.'))
93
94
  return true;
94
95
  }
95
96
  else if (node instanceof DynamicBinding) {
96
- if (node.expression.raw.includes('useFetch('))
97
+ if (node.expression.raw.includes('useFetch(') || node.expression.raw.includes('useFetch.'))
97
98
  return true;
98
99
  }
99
100
  else if (node instanceof MapRegion) {
@@ -467,6 +468,32 @@ function processJSXChildren(source, children) {
467
468
  result.push(...processJSXChildren(source, [c]));
468
469
  i++;
469
470
  }
471
+ else if (child.type === 'ForOfStatement') {
472
+ let alternate = [];
473
+ let consumed = 1;
474
+ const emptyText = children[i + 1];
475
+ const emptyContainer = children[i + 2];
476
+ if (emptyText && emptyText.type === 'JSXText' &&
477
+ ['#empty', 'empty'].includes(emptyText.value.trim()) &&
478
+ emptyContainer && emptyContainer.type === 'JSXExpressionContainer' &&
479
+ emptyContainer.expression.type !== 'JSXEmptyExpression') {
480
+ alternate = exprToIR(source, emptyContainer.expression);
481
+ consumed = 3;
482
+ }
483
+ result.push(...processForStatement(source, child, alternate));
484
+ i += consumed;
485
+ continue;
486
+ }
487
+ else if (child.type === 'IfStatement' || child.type === 'ForStatement' ||
488
+ child.type === 'ForInStatement' ||
489
+ child.type === 'WhileStatement' || child.type === 'DoWhileStatement' ||
490
+ child.type === 'SwitchStatement' || child.type === 'TryStatement' ||
491
+ child.type === 'VariableDeclaration' || child.type === 'ExpressionStatement' ||
492
+ child.type === 'ReturnStatement' || child.type === 'WithStatement' ||
493
+ child.type === 'LabeledStatement') {
494
+ result.push(...processStatementModeBody(source, [child]));
495
+ i++;
496
+ }
470
497
  else {
471
498
  i++;
472
499
  }
@@ -509,6 +536,30 @@ function exprToIR(source, expr) {
509
536
  }
510
537
  return [new DynamicBinding(toExpression(source, expr))];
511
538
  }
539
+ /**
540
+ * True when an `ExpressionStatement` expression is a pure value that should
541
+ * render as output (a bare reference, member access, literal, template,
542
+ * conditional, etc.) rather than a side-effecting statement (call, assignment,
543
+ * update, `new`, `await`, `yield`, tagged template, `delete`/`void`). Side
544
+ * effects stay as runtime statements; pure values become dynamic bindings.
545
+ */
546
+ function isRenderableExpression(expr) {
547
+ const t = expr.type;
548
+ if (t === 'CallExpression' || t === 'NewExpression' ||
549
+ t === 'AssignmentExpression' || t === 'UpdateExpression' ||
550
+ t === 'AwaitExpression' || t === 'YieldExpression' ||
551
+ t === 'TaggedTemplateExpression' || t === 'ImportExpression' ||
552
+ t === 'MetaProperty') {
553
+ return false;
554
+ }
555
+ if (t === 'UnaryExpression') {
556
+ return expr.operator !== 'delete' && expr.operator !== 'void';
557
+ }
558
+ if (t === 'SequenceExpression') {
559
+ return isRenderableExpression(expr.expressions[expr.expressions.length - 1]);
560
+ }
561
+ return true;
562
+ }
512
563
  function processJSXCallbackBody(source, body) {
513
564
  if (body.type === 'JSXElement')
514
565
  return processJSXElement(source, body);
@@ -606,12 +657,17 @@ function buildGuardChain(source, guardClauses, mainReturn) {
606
657
  const guard = guardClauses[i];
607
658
  const condExpr = toExpression(source, guard.test);
608
659
  const consequent = [];
609
- if (guard.consequent.type === 'ReturnStatement' && guard.consequent.argument) {
610
- if (guard.consequent.argument.type === 'JSXElement') {
611
- consequent.push(...processJSXElement(source, guard.consequent.argument));
660
+ const guardReturn = getReturnArgument(guard.consequent);
661
+ if (guardReturn) {
662
+ if (guardReturn.type === 'JSXElement') {
663
+ consequent.push(...processJSXElement(source, guardReturn));
664
+ }
665
+ else if (guardReturn.type === 'JSXFragment') {
666
+ for (const c of guardReturn.children)
667
+ consequent.push(...processJSXChildren(source, [c]));
612
668
  }
613
669
  else {
614
- consequent.push(new DynamicBinding(toExpression(source, guard.consequent.argument)));
670
+ consequent.push(new DynamicBinding(toExpression(source, guardReturn)));
615
671
  }
616
672
  }
617
673
  currentAlternate = [new OpaqueDynamicRegion(condExpr, consequent, currentAlternate)];
@@ -655,7 +711,24 @@ function hasJSXInSubtree(node) {
655
711
  return false;
656
712
  }
657
713
  function isGuardClause(node) {
658
- return node.type === 'IfStatement' && node.consequent.type === 'ReturnStatement' && hasJSXInSubtree(node.consequent);
714
+ return (node.type === 'IfStatement' &&
715
+ !node.alternate &&
716
+ getReturnArgument(node.consequent) !== null &&
717
+ hasJSXInSubtree(node.consequent));
718
+ }
719
+ /**
720
+ * Returns the `return` argument of a statement that represents an early
721
+ * return — either a bare `ReturnStatement` or a `BlockStatement` wrapping a
722
+ * single `ReturnStatement`. Returns `null` when the statement is not such a
723
+ * return (including `return null` / `return;`, whose argument is absent).
724
+ */
725
+ function getReturnArgument(node) {
726
+ if (node.type === 'ReturnStatement')
727
+ return node.argument ?? null;
728
+ if (node.type === 'BlockStatement' && node.body.length === 1 && node.body[0].type === 'ReturnStatement') {
729
+ return node.body[0].argument ?? null;
730
+ }
731
+ return null;
659
732
  }
660
733
  function isStatementMode(bodyStmts) {
661
734
  if (bodyStmts.some((s) => s.type === 'JSXElement' || s.type === 'JSXExpressionContainer' || s.type === 'JSXFragment'))
@@ -842,6 +915,16 @@ function processStatementModeBody(source, bodyStmts) {
842
915
  else if (stmt.type === 'ForStatement') {
843
916
  nodes.push(...processForStatement(source, stmt));
844
917
  }
918
+ else if (stmt.type === 'ExpressionStatement') {
919
+ if (isRenderableExpression(stmt.expression)) {
920
+ nodes.push(...exprToIR(source, stmt.expression));
921
+ }
922
+ else {
923
+ const raw = getSource(source, stmt);
924
+ if (raw)
925
+ nodes.push(new RuntimeStatement(raw, stmt, source));
926
+ }
927
+ }
845
928
  else if (stmt.type === 'ClassDeclaration') {
846
929
  throw VeskError.classDecl();
847
930
  }
@@ -1161,7 +1244,15 @@ export function generateIR(ast, source) {
1161
1244
  for (const n of extractImportNames(imp))
1162
1245
  existing.add(n);
1163
1246
  }
1164
- const missing = [...usedFunctions].filter(f => !existing.has(f));
1247
+ // A name already bound by the file's own imports (any target — including a
1248
+ // local module with the same export) wins; never auto-import a duplicate
1249
+ // binding. Mirrors client shadowing so SSR and client agree.
1250
+ const boundLocally = new Set();
1251
+ for (const imp of imports) {
1252
+ for (const pair of importBindingPairs(imp))
1253
+ boundLocally.add(pair.local);
1254
+ }
1255
+ const missing = [...usedFunctions].filter(f => !existing.has(f) && !boundLocally.has(f));
1165
1256
  if (missing.length > 0) {
1166
1257
  imports.push(`import { ${missing.join(', ')} } from '@vesk/runtime';`);
1167
1258
  }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * AST-driven extraction of the value bindings introduced by one import
3
+ * statement (`import { a as b } from 'm'` → `[{ local: 'b', imported: 'a' }]`;
4
+ * `import X from 'm'` → `[{ local: 'X', imported: 'default' }]`;
5
+ * `import * as X from 'm'` → `[{ local: 'X', imported: '*' }]`). Type-only
6
+ * specifiers are skipped. Side-effect imports return `[]`.
7
+ */
8
+ export declare function importBindingPairs(imp: string): Array<{
9
+ local: string;
10
+ imported: string;
11
+ }>;
12
+ /** Local binding names imported from non-runtime, non-`.vsk` modules. */
13
+ export declare function localValueImportNames(importStrs: string[]): string[];
14
+ /** True when an import line should be SSR-loaded (non-runtime, non-empty, non-`.vsk`). */
15
+ export declare function isLocalValueImport(imp: string): boolean;
16
+ /**
17
+ * Loads every local value import and merges its exports into `__vesk` so SSR
18
+ * component bodies can reference them. Pure side-effect imports (`import './x'`)
19
+ * are also resolved and executed so their module-level setup runs client AND
20
+ * server. Resolution is relative to `sourcePath` (the importing `.vsk` file).
21
+ *
22
+ * Best-effort: unresolvable/unloadable modules warn and are skipped, matching
23
+ * how unresolvable `.vsk` imports are handled. Unsupported ESM constructs
24
+ * (import.meta/top-level await) THROW a specific error — loading them would
25
+ * silently yield `undefined` at render.
26
+ */
27
+ export declare function applyLocalModuleImports(__vesk: Record<string, unknown>, importStrs: string[], sourcePath: string | undefined): void;
28
+ /**
29
+ * Loads (and caches) a module's export object. Cache entries track both the
30
+ * module's own mtime and the mtimes of everything it transitively resolved,
31
+ * so a change to any dependency invalidates every module that pulls it in.
32
+ * Builtin markers load through Node and are cached separately (never stale).
33
+ */
34
+ export declare function loadSsrModule(absPath: string): Record<string, unknown> | null;
35
+ /**
36
+ * Resolves a specifier to an absolute file path (or a `BUILTIN_PREFIX` marker
37
+ * for Node builtins). Relative/absolute localities use extension probing;
38
+ * bare specifiers (`npm-package`, `pkg/subpath`, `node:fs`, `fs`) prefer
39
+ * Node's own resolver — which understands `exports` maps, conditions, scoped
40
+ * packages and builtins — and fall back to a `node_modules` walk-up.
41
+ *
42
+ * Every resolved file path is normalized through `realpathSync` so two
43
+ * specifiers pointing at the same physical file (e.g. pnpm/yarn symlinked
44
+ * `node_modules`) share one cache entry and one module instance.
45
+ */
46
+ export declare function resolveSsrModule(specifier: string, fromDir: string): string | null;
47
+ /** Rewrites an ESM program body (imports/exports removed) into CJS statements. */
48
+ export declare function esmToCjs(body: Array<{
49
+ type: string;
50
+ } & Record<string, unknown>>): string;
51
+ //# sourceMappingURL=module-imports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-imports.d.ts","sourceRoot":"","sources":["../src/module-imports.ts"],"names":[],"mappings":"AAmGA;;;;;;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,CAqD7E;AAqJD;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAyBlF;AAoJD,kFAAkF;AAClF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,CAiGxF"}