@vesk/compiler 0.2.26 → 0.2.27

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,4 +1,4 @@
1
- import { readFileSync, realpathSync, statSync } from 'node:fs';
1
+ import { readFileSync, existsSync, realpathSync, statSync } from 'node:fs';
2
2
  import { createRequire } from 'node:module';
3
3
  import { dirname, extname, isAbsolute, join, resolve } from 'node:path';
4
4
  import { print } from 'esrap';
@@ -644,10 +644,180 @@ function createModuleRequire(fromDir) {
644
644
  return loaded;
645
645
  };
646
646
  }
647
+ const EMPTY_ALIASES = { exact: new Map(), wildcard: [] };
648
+ const ALIAS_CACHE = new Map();
649
+ /** Strips `//` + block comments and trailing commas so a tsconfig.json parses. */
650
+ function parseJsonc(raw) {
651
+ let out = '';
652
+ let inString = false;
653
+ let quote = '';
654
+ for (let i = 0; i < raw.length; i++) {
655
+ const ch = raw[i];
656
+ const next = raw[i + 1];
657
+ if (inString) {
658
+ out += ch;
659
+ if (ch === '\\') {
660
+ out += next ?? '';
661
+ i++;
662
+ }
663
+ else if (ch === quote) {
664
+ inString = false;
665
+ }
666
+ continue;
667
+ }
668
+ if (ch === '"' || ch === "'") {
669
+ inString = true;
670
+ quote = ch;
671
+ out += ch;
672
+ continue;
673
+ }
674
+ if (ch === '/' && next === '/') {
675
+ while (i < raw.length && raw[i] !== '\n')
676
+ i++;
677
+ out += '\n';
678
+ continue;
679
+ }
680
+ if (ch === '/' && next === '*') {
681
+ i += 2;
682
+ while (i < raw.length && !(raw[i] === '*' && raw[i + 1] === '/'))
683
+ i++;
684
+ i++;
685
+ continue;
686
+ }
687
+ out += ch;
688
+ }
689
+ return JSON.parse(out.replace(/,\s*([}\]])/g, '$1'));
690
+ }
691
+ /** Nearest `tsconfig.json` at or above `fromDir`, or null. */
692
+ export function findTsconfigPath(fromDir) {
693
+ let dir = fromDir;
694
+ for (let depth = 0; depth < 64; depth++) {
695
+ const st = statOrNull(join(dir, 'tsconfig.json'));
696
+ if (st && st.isFile())
697
+ return join(dir, 'tsconfig.json');
698
+ const parent = dirname(dir);
699
+ if (parent === dir)
700
+ break;
701
+ dir = parent;
702
+ }
703
+ return null;
704
+ }
705
+ function buildAliases(tsconfig) {
706
+ let raw;
707
+ try {
708
+ raw = readFileSync(tsconfig, 'utf-8');
709
+ }
710
+ catch {
711
+ return EMPTY_ALIASES;
712
+ }
713
+ let parsed = null;
714
+ try {
715
+ parsed = parseJsonc(raw);
716
+ }
717
+ catch {
718
+ return EMPTY_ALIASES;
719
+ }
720
+ const co = parsed?.compilerOptions;
721
+ if (!co || typeof co.paths !== 'object' || co.paths === null)
722
+ return EMPTY_ALIASES;
723
+ const configDir = dirname(tsconfig);
724
+ const baseUrl = typeof co.baseUrl === 'string' && co.baseUrl.length > 0 ? resolve(configDir, co.baseUrl) : configDir;
725
+ const exact = new Map();
726
+ const wildcard = [];
727
+ for (const [key, value] of Object.entries(co.paths)) {
728
+ if (!Array.isArray(value))
729
+ continue;
730
+ const targets = value.filter((t) => typeof t === 'string').map((t) => resolve(baseUrl, t));
731
+ if (targets.length === 0)
732
+ continue;
733
+ const star = key.indexOf('*');
734
+ if (star === -1)
735
+ exact.set(key, targets);
736
+ else
737
+ wildcard.push({ prefix: key.slice(0, star), suffix: key.slice(star + 1), targets });
738
+ }
739
+ // Longest prefix first so a specific alias (`@app/`) beats a broad one (`@/`).
740
+ wildcard.sort((a, b) => b.prefix.length - a.prefix.length);
741
+ return { exact, wildcard };
742
+ }
743
+ function loadAliases(fromDir) {
744
+ const tsconfig = findTsconfigPath(fromDir);
745
+ if (!tsconfig)
746
+ return EMPTY_ALIASES;
747
+ const st = statOrNull(tsconfig);
748
+ if (!st)
749
+ return EMPTY_ALIASES;
750
+ const cached = ALIAS_CACHE.get(tsconfig);
751
+ if (cached && cached.mtimeMs === st.mtimeMs)
752
+ return cached.aliases;
753
+ const aliases = buildAliases(tsconfig);
754
+ if (ALIAS_CACHE.size >= 64)
755
+ ALIAS_CACHE.clear();
756
+ ALIAS_CACHE.set(tsconfig, { mtimeMs: st.mtimeMs, aliases });
757
+ return aliases;
758
+ }
759
+ /**
760
+ * Resolves a tsconfig-alias specifier (`@/x`, `@app/x`) to an existing file via
761
+ * the nearest tsconfig's `baseUrl` + `paths`, or null when the specifier is not
762
+ * aliased or no target exists (caller then falls through to normal resolution).
763
+ */
764
+ export function resolveAliasModule(spec, fromDir) {
765
+ const aliases = loadAliases(fromDir);
766
+ if (aliases.exact.size === 0 && aliases.wildcard.length === 0)
767
+ return null;
768
+ const candidates = [];
769
+ const exact = aliases.exact.get(spec);
770
+ if (exact)
771
+ candidates.push(...exact);
772
+ for (const w of aliases.wildcard) {
773
+ if (spec.startsWith(w.prefix) && spec.endsWith(w.suffix) && spec.length >= w.prefix.length + w.suffix.length) {
774
+ const middle = spec.slice(w.prefix.length, spec.length - w.suffix.length);
775
+ candidates.push(...w.targets.map((t) => t.replace('*', middle)));
776
+ }
777
+ }
778
+ for (const candidate of candidates) {
779
+ const found = probeFile(candidate);
780
+ if (found)
781
+ return found;
782
+ }
783
+ return null;
784
+ }
785
+ /**
786
+ * Resolves a relative import specifier to an absolute file path for a bundler
787
+ * (esbuild/rollup) to consume. An explicit extension is preserved — `./x.ts`
788
+ * stays `./x.ts` and must never become `./x.ts.ts`. An extensionless specifier
789
+ * is probed against `EXTENSIONS` (then `.vsk`, so component imports resolve),
790
+ * and falls back to `.ts` so the bundler reports the missing file by name.
791
+ * tsconfig `paths` aliases (`@/x`, `@app/x`) resolve against the app tsconfig;
792
+ * other bare/absolute specifiers are returned unchanged. This is the single
793
+ * source of truth for every relative-import rewrite (SSR loader and client
794
+ * chunk bundler).
795
+ */
796
+ export function resolveImportPath(spec, fromDir) {
797
+ if (spec.startsWith('./') || spec.startsWith('../')) {
798
+ const base = resolve(fromDir, spec);
799
+ if (extname(base))
800
+ return base;
801
+ for (const suffix of EXTENSIONS) {
802
+ if (existsSync(base + suffix))
803
+ return base + suffix;
804
+ }
805
+ if (existsSync(`${base}.vsk`))
806
+ return `${base}.vsk`;
807
+ return `${base}.ts`;
808
+ }
809
+ if (!isAbsolute(spec)) {
810
+ const aliased = resolveAliasModule(spec, fromDir);
811
+ if (aliased)
812
+ return aliased;
813
+ }
814
+ return spec;
815
+ }
647
816
  /**
648
817
  * Resolves a specifier to an absolute file path (or a `BUILTIN_PREFIX` marker
649
818
  * for Node builtins). Relative/absolute localities use extension probing;
650
- * bare specifiers (`npm-package`, `pkg/subpath`, `node:fs`, `fs`) prefer
819
+ * tsconfig `paths` aliases (`@/x`, `@app/x`) resolve against the app tsconfig;
820
+ * other bare specifiers (`npm-package`, `pkg/subpath`, `node:fs`, `fs`) prefer
651
821
  * Node's own resolver — which understands `exports` maps, conditions, scoped
652
822
  * packages and builtins — and fall back to a `node_modules` walk-up.
653
823
  *
@@ -670,6 +840,11 @@ export function resolveSsrModule(specifier, fromDir) {
670
840
  resolved = probeFile(resolve(fromDir, specifier));
671
841
  }
672
842
  else {
843
+ // tsconfig `paths` alias (`@/x`, `@app/x`) — resolved before Node's
844
+ // resolver so app-local aliases win over any same-named package.
845
+ const aliased = resolveAliasModule(specifier, fromDir);
846
+ if (aliased)
847
+ return toRealPath(aliased);
673
848
  // Bare specifier — prefer the native resolver (exports map, conditions,
674
849
  // builtins, symlinks), then fall back to a node_modules walk-up.
675
850
  const native = nativeResolve(specifier, fromDir);
@@ -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,EAAkG,KAAK,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAoCrK,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,CAmDjJ;AAoWD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAmD1F;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,EAAE,CA4BzE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAYhG"}
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,EAAkG,KAAK,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAmDrK,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,CAqDjJ;AAkXD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAqD1F;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,EAAE,CA4BzE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAYhG"}
@@ -1,4 +1,4 @@
1
- import { StaticNode, TextNode, DynamicBinding, OpaqueDynamicRegion, MapRegion, WhileLoop, SwitchBlock, TryCatch, ForLoop, TrackDecl, RuntimeStatement, ComponentRef, ComponentCall, ServerBlock, ClientBlock, HeadBlock, SlotNode, } from '@vesk/compiler/src/ir';
1
+ import { StaticNode, TextNode, DynamicBinding, OpaqueDynamicRegion, MapRegion, WhileLoop, SwitchBlock, TryCatch, ForLoop, TrackDecl, RuntimeStatement, ComponentRef, ComponentCall, ServerBlock, ClientBlock, HeadBlock, SlotNode, PropSlot, PropSlotRender, } from '@vesk/compiler/src/ir';
2
2
  import { isStaticIR, collectTrackedNames, transformTracked, transformTrackedInit, semicolonizeStatement } from '@vesk/compiler/src/client-codegen';
3
3
  import { unwrapTrackCall, stripTrackGeneric, hasTopLevelComma, skipWhitespace, findBalancedEnd, startsWithIdentifier } from '@vesk/compiler/src/scan';
4
4
  import { escapeHtml, indent, exprJS, extractTopLevelNames, extractRuntimeNames, buildParamInit, __vskHydrate, __vskImportedNames, setVskImportedNames, setVskForceClaim, takeVskForceClaim, nextVskId, } from '@vesk/compiler/src/server-utils';
@@ -21,12 +21,26 @@ import { localValueImportNames } from '@vesk/compiler/src/module-imports';
21
21
  // The wrapper tag must be `<span>`, not `<div>`. The HTML parser implicitly
22
22
  // Marker-ONLY component boundary. The client's shared walker claims a
23
23
  // component call-site by reading the component's own root element off this
24
- // marker — the SSR output of a component call is exactly `<!--vsk-->` followed
24
+ // marker — the SSR output of a component call is exactly the marker followed
25
25
  // by the callee's content. A `<span style="display:contents">` box here would
26
26
  // sit between the marker and that root, so a `nextElement(tag)` claim (e.g.
27
27
  // LoadingIndicator claiming `div`) hits a tag mismatch, never advances the
28
28
  // walker cursor, and every later claim miss wipes the SSR content.
29
- const HYDRATE_COMPONENT_WRAPPER = '<!--vsk-->';
29
+ //
30
+ // Every marker is keyed, no bare markers (Hydrate-Todo: keyed markers):
31
+ // component boundaries carry the callee name (`<!--vsk:c:Name-->`),
32
+ // static-subtree boundaries carry the tag (`<!--vsk:t:tag-->`). The walker
33
+ // asserts the tag identity on adopt, reports skew deterministically, and names
34
+ // every miss/orphan — divergence is pinpointed at the exact boundary instead
35
+ // of cascading anonymously.
36
+ function sanitizeMarkerName(name) {
37
+ // No regex (compiler rule): identifiers cannot contain comment-breaking
38
+ // sequences, but guard anyway — `--` would terminate the HTML comment.
39
+ return String(name).split('--').join('-').split('<').join('').split('>').join('');
40
+ }
41
+ function componentMarker(compName) {
42
+ return `<!--vsk:c:${sanitizeMarkerName(compName)}-->`;
43
+ }
30
44
  export function irNodeToJS(node, importedNames, isAsync = false, tracked) {
31
45
  importedNames = importedNames || __vskImportedNames;
32
46
  if (node instanceof StaticNode)
@@ -93,6 +107,10 @@ export function irNodeToJS(node, importedNames, isAsync = false, tracked) {
93
107
  return semicolonizeStatement(transformTracked(node, tracked || new Map()));
94
108
  if (node instanceof SlotNode)
95
109
  return `__out.push(props.children || '');`;
110
+ if (node instanceof PropSlotRender)
111
+ return `__out.push(props.${node.propName} || '');`;
112
+ if (node instanceof PropSlot)
113
+ return '';
96
114
  return '';
97
115
  }
98
116
  let __currentCompName = '';
@@ -108,13 +126,11 @@ function exprJSX(node, tracked) {
108
126
  function staticNodeToJS(node, isAsync = false, tracked) {
109
127
  const lines = [];
110
128
  const dynAttrTargets = new Set();
111
- const dynAttrOrder = [];
112
129
  for (const child of node.children) {
113
130
  if (child instanceof DynamicBinding && child.kind === 'attribute' && child.target !== null && child.target !== 'ref') {
114
131
  if (isEvent(child.target))
115
132
  continue;
116
133
  dynAttrTargets.add(child.target);
117
- dynAttrOrder.push(child.target);
118
134
  }
119
135
  }
120
136
  const hasDynamicAttrs = dynAttrTargets.size > 0;
@@ -122,7 +138,9 @@ function staticNodeToJS(node, isAsync = false, tracked) {
122
138
  const forceClaim = takeVskForceClaim();
123
139
  const subtreeNeedsJS = __vskHydrate && (forceClaim || !isStaticIR(node.children));
124
140
  if (subtreeNeedsJS) {
125
- lines.push(`__out.push('<!--vsk-->');`);
141
+ // Keyed static boundary (Hydrate-Todo: no bare markers): the walker
142
+ // asserts this tag on adopt and names it in every miss/orphan report.
143
+ lines.push('__out.push(' + JSON.stringify(`<!--vsk:t:${sanitizeMarkerName(node.tag)}-->`) + ');');
126
144
  }
127
145
  for (const attr of node.attributes) {
128
146
  if (isEvent(attr.name))
@@ -136,17 +154,13 @@ function staticNodeToJS(node, isAsync = false, tracked) {
136
154
  openTag += ` ${attr.name}="${escapeHtml(attr.value)}"`;
137
155
  }
138
156
  }
139
- for (const target of dynAttrOrder) {
140
- openTag += ` ${target}=""`;
141
- }
142
157
  if (node.selfClosing) {
143
158
  let tag = openTag + ' />';
144
159
  if (hasDynamicAttrs) {
145
160
  let expr = JSON.stringify(tag);
146
161
  for (const child of node.children) {
147
162
  if (child instanceof DynamicBinding && child.kind === 'attribute' && child.target !== 'ref' && !isEvent(child.target)) {
148
- const val = `__escape(String(${exprJSX(child.expression, tracked)}))`;
149
- expr = `${expr}.replace(${JSON.stringify(' ' + child.target + '=""')}, ' ' + ${JSON.stringify(child.target)} + '=\"' + ${val} + '\"')`;
163
+ expr += ` + __attr(${JSON.stringify(child.target)}, ${exprJSX(child.expression, tracked)})`;
150
164
  }
151
165
  }
152
166
  lines.push(`__out.push(${expr});`);
@@ -161,8 +175,7 @@ function staticNodeToJS(node, isAsync = false, tracked) {
161
175
  let expr = JSON.stringify(openTag);
162
176
  for (const child of node.children) {
163
177
  if (child instanceof DynamicBinding && child.kind === 'attribute' && child.target !== 'ref' && !isEvent(child.target)) {
164
- const val = `__escape(String(${exprJSX(child.expression, tracked)}))`;
165
- expr = `${expr}.replace(${JSON.stringify(' ' + child.target + '=""')}, ' ' + ${JSON.stringify(child.target)} + '=\"' + ${val} + '\"')`;
178
+ expr += ` + __attr(${JSON.stringify(child.target)}, ${exprJSX(child.expression, tracked)})`;
166
179
  }
167
180
  }
168
181
  lines.push(`__out.push(${expr});`);
@@ -413,9 +426,29 @@ function componentCallToJS(node, importedNames, isAsync = false, tracked) {
413
426
  propsEntries.push(`...${exprJSX(sp, tracked)}`);
414
427
  }
415
428
  const lines = [];
416
- if (node.children.length > 0) {
429
+ // Named content slots (`trigger={<Button/>}`) thread through the same channel
430
+ // as `children`: a closed-over IIFE that serializes the slot body to a string.
431
+ const slotChildren = node.children.filter((c) => c instanceof PropSlot);
432
+ const regularChildren = node.children.filter((c) => !(c instanceof PropSlot));
433
+ for (const slot of slotChildren) {
434
+ const slotLines = [];
435
+ for (const child of slot.body) {
436
+ const code = irNodeToJS(child, importedNames, isAsync, tracked);
437
+ if (code)
438
+ slotLines.push(code);
439
+ }
440
+ if (slotLines.length > 0) {
441
+ const slotVar = `__sl${nextVskId()}`;
442
+ propsEntries.push(`${JSON.stringify(slot.propName)}: ${slotVar}`);
443
+ lines.push(`const ${slotVar} = ${isAsync ? 'await (async ' : '('}() => {`);
444
+ lines.push(`const __out = [];`);
445
+ lines.push(indent(slotLines.join('\n')));
446
+ lines.push(`return __out.join(''); })();`);
447
+ }
448
+ }
449
+ if (regularChildren.length > 0) {
417
450
  const childLines = [];
418
- for (const child of node.children) {
451
+ for (const child of regularChildren) {
419
452
  const code = irNodeToJS(child, importedNames, isAsync, tracked);
420
453
  if (code)
421
454
  childLines.push(code);
@@ -448,7 +481,7 @@ function componentCallToJS(node, importedNames, isAsync = false, tracked) {
448
481
  lines.push(`const ${calleeVar} = ${callee};`);
449
482
  const callExpr = `${awaitKw}${calleeVar}(${propsObj}, __registry, (${calleeVar}.__veskScope || __vesk))`;
450
483
  if (__vskHydrate) {
451
- lines.push(`__out.push(${JSON.stringify(HYDRATE_COMPONENT_WRAPPER)} + (${callExpr} || ''));`);
484
+ lines.push(`__out.push(${JSON.stringify(componentMarker(compName))} + (${callExpr} || ''));`);
452
485
  }
453
486
  else {
454
487
  lines.push(`__out.push(${callExpr} || '');`);
@@ -466,6 +499,8 @@ export function generateFunctionBody(comp, importedNames) {
466
499
  lines.push(`__sa({ c: null, p: __prev });`);
467
500
  lines.push(`try {`);
468
501
  lines.push(`const __escape = (s) => { s = String(s); return s.split('&').join('&amp;').split('<').join('&lt;').split('>').join('&gt;').split(${JSON.stringify('"')}).join('&quot;'); };`);
502
+ lines.push(`const __styleText = (v) => { if (typeof v === 'string') return v; if (v && typeof v === 'object') { let s = ''; for (const k in v) { const x = v[k]; if (x == null || x === false) continue; s += k.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase()) + ':' + x + ';'; } return s; } return String(v); };`);
503
+ lines.push(`const __attr = (n, v) => (v == null || v === false) ? '' : ' ' + n + '="' + __escape(n === 'style' ? __styleText(v) : String(v)) + '"';`);
469
504
  lines.push(`const raw = (s) => s == null ? '' : String(s);`);
470
505
  lines.push(`const __tk = globalThis.__vsk_ssr_token || '';`);
471
506
  if (asyncMode) {
@@ -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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"}
@@ -40,24 +40,29 @@ function compileFileInternal(source, sourcePath, seenImportFiles) {
40
40
  if (seenImportFiles.has(importPath))
41
41
  continue;
42
42
  seenImportFiles.add(importPath);
43
+ // Only truly unresolvable imports are skipped. A `.vsk` file that EXISTS
44
+ // but fails to compile must surface its error — silently dropping it here
45
+ // corrupts the component registry (the caller later crashes on
46
+ // `undefined.__veskScope` with no pointer back to the real cause).
47
+ let importedSrc;
43
48
  try {
44
- const importedSrc = readFileSync(importPath, 'utf-8');
45
- const sub = compileFileInternal(importedSrc, importPath, seenImportFiles);
46
- for (const [name, fn] of sub.componentMap) {
47
- if (!componentMap.has(name))
48
- componentMap.set(name, fn);
49
- }
50
- // Hoist the sub-`.vsk`'s scope into this file's so that
51
- // registry-hoisted sub components (called with this `__vesk`) can
52
- // destructure runtime imports, module values and top-level helpers.
53
- for (const key of Object.keys(sub.__vesk)) {
54
- if (key in __vesk)
55
- continue;
56
- __vesk[key] = sub.__vesk[key];
57
- }
49
+ importedSrc = readFileSync(importPath, 'utf-8');
58
50
  }
59
51
  catch {
60
- // skip unresolvable imports
52
+ continue;
53
+ }
54
+ const sub = compileFileInternal(importedSrc, importPath, seenImportFiles);
55
+ for (const [name, fn] of sub.componentMap) {
56
+ if (!componentMap.has(name))
57
+ componentMap.set(name, fn);
58
+ }
59
+ // Hoist the sub-`.vsk`'s scope into this file's so that
60
+ // registry-hoisted sub components (called with this `__vesk`) can
61
+ // destructure runtime imports, module values and top-level helpers.
62
+ for (const key of Object.keys(sub.__vesk)) {
63
+ if (key in __vesk)
64
+ continue;
65
+ __vesk[key] = sub.__vesk[key];
61
66
  }
62
67
  }
63
68
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vesk/compiler",
3
- "version": "0.2.26",
3
+ "version": "0.2.27",
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.26",
23
+ "@vesk/types": "^0.2.27",
24
24
  "acorn": "^8.15.0",
25
25
  "esrap": "^2.3.0",
26
26
  "is-reference": "^3.0.0",