@vesk/vesk-cli 0.2.43 → 0.2.44

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.
Files changed (2) hide show
  1. package/dist/cli.js +585 -138
  2. package/package.json +5 -5
package/dist/cli.js CHANGED
@@ -184,21 +184,36 @@ var init_errors = __esm({
184
184
  tip: isBuiltin ? `"${name}" is a Vesk built-in. Use it directly \u2014 no manual import needed.` : ""
185
185
  });
186
186
  }
187
- static vskNamespaceTag(context = {}) {
187
+ /**
188
+ * A `.vsk` namespace is resolved at the JSX-tag level: `<ns.Icon />` looks
189
+ * the component up in the registry by its exported name, exactly like
190
+ * `import { Icon }`, and no module object is ever built. The two forms that
191
+ * genuinely need a real module object are reported here.
192
+ *
193
+ * - `nested` — `<ns.Sub.Icon />`. A `.vsk` module's exports are flat
194
+ * component names, so there is no object to walk into.
195
+ * - `value` — `ns.max` read as a value. Component tags resolve; plain value
196
+ * reads do not, because nothing binds `ns` at runtime.
197
+ */
198
+ static vskNamespaceMember(context = {}) {
199
+ const nested = context.form === "nested";
188
200
  return new _VeskError(
189
- "Namespace imports are not supported for `.vsk` modules.",
201
+ nested ? "A `.vsk` namespace is flat, so `<ns.Sub.Icon />` cannot be resolved." : "A `.vsk` namespace resolves component tags only, so `ns.value` is not available.",
190
202
  {
191
203
  ...context,
192
204
  code: context.code || "V0410",
193
- suggestions: [
194
- "Import the component by name: import { MyIcon } from './icons.vsk'",
195
- "Rename on import: import { MyIcon as Icon } from './icons.vsk'"
205
+ suggestions: nested ? [
206
+ "Import the component by name: import { Icon } from './icons.vsk'",
207
+ "Import from the module that owns Sub: import * as Sub from './sub.vsk'"
208
+ ] : [
209
+ "Import the value by name: import { MAX } from './constants.vsk'",
210
+ "Move the value into a .ts module and import it from there \u2014 those are real ES modules with live bindings."
196
211
  ],
197
212
  nextSteps: [
198
- "A `.vsk` component is a registry entry keyed by its name, not a module namespace object, so `<ns.Icon />` cannot be resolved.",
199
- "If you need a namespace object, import from a `.ts`/`.js` module instead \u2014 those are real ES modules."
213
+ "A `.vsk` tag resolves by component name in the global registry, not by module exports, so `import * as ns` + `<ns.Icon />` needs no module object.",
214
+ nested ? "Re-export with a flat name (`export { Icon }`) or import the component directly." : "Re-export the value under a flat name and import it directly, or keep the value in a .ts module."
200
215
  ],
201
- tip: "A `.vsk` module exposes its components as named exports; use a named or renamed import rather than `import * as ns`."
216
+ tip: "`<ns.Icon />` works; only nested paths and value reads need a real module namespace."
202
217
  }
203
218
  );
204
219
  }
@@ -2017,6 +2032,16 @@ function startRequest(handle2, skipCache) {
2017
2032
  if (getInflight().get(key) === prom2) getInflight().delete(key);
2018
2033
  });
2019
2034
  } else {
2035
+ const ownerToken = g().__vsk_ssr_token || void 0;
2036
+ if (ownerToken) {
2037
+ prom2.then(
2038
+ (data2) => {
2039
+ setSsrData(key, data2, ownerToken);
2040
+ },
2041
+ () => {
2042
+ }
2043
+ );
2044
+ }
2020
2045
  attachSettle(handle2, prom2);
2021
2046
  }
2022
2047
  trackSsrPromise(prom2);
@@ -20181,17 +20206,125 @@ function jsxTagRootName(nameNode) {
20181
20206
  while (node && node.type === "JSXMemberExpression") node = node.object;
20182
20207
  return node && node.type === "JSXIdentifier" ? node.name : "";
20183
20208
  }
20209
+ function jsxTagMemberName(nameNode) {
20210
+ let node = nameNode;
20211
+ while (node && node.type === "JSXMemberExpression") {
20212
+ node = node.property;
20213
+ if (node && node.type === "JSXIdentifier") return node.name;
20214
+ }
20215
+ return "";
20216
+ }
20217
+ function jsxTagDepth(nameNode) {
20218
+ let depth = 0;
20219
+ let node = nameNode;
20220
+ while (node && node.type === "JSXMemberExpression") {
20221
+ depth++;
20222
+ node = node.object;
20223
+ }
20224
+ return depth;
20225
+ }
20184
20226
  function collectVskNamespaceLocals(ast) {
20185
- const out = /* @__PURE__ */ new Set();
20227
+ const namespaceLocals = /* @__PURE__ */ new Map();
20186
20228
  for (const node of ast.body || []) {
20187
20229
  if (node.type !== "ImportDeclaration") continue;
20188
20230
  if (typeof node.source?.value !== "string" || !node.source.value.endsWith(".vsk")) continue;
20189
20231
  for (const spec of node.specifiers || []) {
20190
- if (spec.type === "ImportNamespaceSpecifier" && spec.local?.name) out.add(spec.local.name);
20232
+ if (spec.type === "ImportNamespaceSpecifier" && spec.local?.name) {
20233
+ namespaceLocals.set(spec.local.name, { path: node.source.value, shadowed: false });
20234
+ }
20191
20235
  }
20192
20236
  }
20237
+ if (namespaceLocals.size === 0) return /* @__PURE__ */ new Set();
20238
+ markShadowedBindings(ast, namespaceLocals);
20239
+ return new Set([...namespaceLocals].filter(([, v]) => !v.shadowed).map(([k]) => k));
20240
+ }
20241
+ function markShadowedBindings(node, namespaceLocals) {
20242
+ if (!node || typeof node !== "object") return;
20243
+ if (Array.isArray(node)) {
20244
+ for (const child of node) markShadowedBindings(child, namespaceLocals);
20245
+ return;
20246
+ }
20247
+ if (typeof node.type === "string") {
20248
+ if (node.type === "ImportNamespaceSpecifier") return;
20249
+ for (const name of declaredBindingNames(node)) {
20250
+ const entry = namespaceLocals.get(name);
20251
+ if (entry) entry.shadowed = true;
20252
+ }
20253
+ }
20254
+ for (const key of Object.keys(node)) {
20255
+ if (key === "loc" || key === "range" || key === "start" || key === "end") continue;
20256
+ markShadowedBindings(node[key], namespaceLocals);
20257
+ }
20258
+ }
20259
+ function declaredBindingNames(node) {
20260
+ const out = [];
20261
+ const pushPattern = (id) => {
20262
+ if (!id) return;
20263
+ if (id.type === "Identifier") {
20264
+ out.push(id.name);
20265
+ return;
20266
+ }
20267
+ if (id.type === "ObjectPattern") {
20268
+ for (const prop of id.properties || []) {
20269
+ if (prop.type === "RestElement") pushPattern(prop.argument);
20270
+ else pushPattern(prop.value);
20271
+ }
20272
+ return;
20273
+ }
20274
+ if (id.type === "ArrayPattern") {
20275
+ for (const el of id.elements || []) if (el) pushPattern(el.type === "RestElement" ? el.argument : el);
20276
+ return;
20277
+ }
20278
+ if (id.type === "AssignmentPattern") pushPattern(id.left);
20279
+ if (id.type === "RestElement") pushPattern(id.argument);
20280
+ };
20281
+ switch (node.type) {
20282
+ case "VariableDeclarator":
20283
+ pushPattern(node.id);
20284
+ break;
20285
+ case "FunctionDeclaration":
20286
+ case "FunctionExpression":
20287
+ case "ArrowFunctionExpression":
20288
+ if (node.id) pushPattern(node.id);
20289
+ for (const param of node.params || []) pushPattern(param);
20290
+ break;
20291
+ case "ClassDeclaration":
20292
+ case "ClassExpression":
20293
+ if (node.id) pushPattern(node.id);
20294
+ break;
20295
+ case "CatchClause":
20296
+ pushPattern(node.param);
20297
+ break;
20298
+ default:
20299
+ break;
20300
+ }
20193
20301
  return out;
20194
20302
  }
20303
+ function assertNoVskNamespaceValue(source, expr) {
20304
+ if (!currentVskNamespaceLocals || currentVskNamespaceLocals.size === 0) return;
20305
+ const hit = findNamespaceValueUse(expr, currentVskNamespaceLocals);
20306
+ if (!hit) return;
20307
+ const { line, column } = offsetToLineCol2(source, hit.start ?? 0);
20308
+ throw VeskError.vskNamespaceMember({ file: currentSourceFile, line, column, code: codeFrame(source, line, column), form: "value" });
20309
+ }
20310
+ function findNamespaceValueUse(node, locals2) {
20311
+ if (!node || typeof node !== "object") return null;
20312
+ if (Array.isArray(node)) {
20313
+ for (const child of node) {
20314
+ const hit = findNamespaceValueUse(child, locals2);
20315
+ if (hit) return hit;
20316
+ }
20317
+ return null;
20318
+ }
20319
+ if (typeof node.type !== "string") return null;
20320
+ if (node.type === "MemberExpression" && node.object?.type === "Identifier" && locals2.has(node.object.name)) return node;
20321
+ for (const key of Object.keys(node)) {
20322
+ if (key === "loc" || key === "range" || key === "start" || key === "end") continue;
20323
+ const hit = findNamespaceValueUse(node[key], locals2);
20324
+ if (hit) return hit;
20325
+ }
20326
+ return null;
20327
+ }
20195
20328
  function isHTMLTag(name) {
20196
20329
  return name.length > 0 && name[0] === name[0].toLowerCase();
20197
20330
  }
@@ -20199,6 +20332,7 @@ function isMapCall(expr) {
20199
20332
  return expr.type === "CallExpression" && expr.callee.type === "MemberExpression" && expr.callee.property.name === "map" && expr.arguments.length === 1 && expr.arguments[0].type === "ArrowFunctionExpression";
20200
20333
  }
20201
20334
  function toExpression(source, expr) {
20335
+ assertNoVskNamespaceValue(source, expr);
20202
20336
  return new Expression(getSource(source, expr), [], expr, source);
20203
20337
  }
20204
20338
  function processAttribute(source, attr) {
@@ -20561,12 +20695,16 @@ function processJSXElement(source, element) {
20561
20695
  }
20562
20696
  if (nameNode && nameNode.type === "JSXMemberExpression") {
20563
20697
  const root3 = jsxTagRootName(nameNode);
20564
- if (currentVskNamespaceLocals && currentVskNamespaceLocals.has(root3)) {
20698
+ const isVskNamespace = !!currentVskNamespaceLocals && currentVskNamespaceLocals.has(root3);
20699
+ if (isVskNamespace && jsxTagDepth(nameNode) > 1) {
20565
20700
  const { line, column } = offsetToLineCol2(source, nameNode.start ?? 0);
20566
- throw VeskError.vskNamespaceTag({ file: currentSourceFile, line, column, code: codeFrame(source, line, column) });
20701
+ throw VeskError.vskNamespaceMember({ file: currentSourceFile, line, column, code: codeFrame(source, line, column), form: "nested" });
20567
20702
  }
20568
20703
  const { props, spreadProps, slots } = extractProps(source, element);
20569
20704
  const children2 = selfClosing ? [] : processJSXChildren(source, element.children || []);
20705
+ if (isVskNamespace) {
20706
+ return [new ComponentCall(jsxTagMemberName(nameNode), props, [...children2, ...slots.map((s) => new PropSlot(s.name, s.nodes))], spreadProps, element.start)];
20707
+ }
20570
20708
  return [new ComponentCall(tagName, props, [...children2, ...slots.map((s) => new PropSlot(s.name, s.nodes))], spreadProps, element.start, getSource(source, nameNode))];
20571
20709
  }
20572
20710
  if (!isHTMLTag(tagName) && selfClosing) {
@@ -30999,15 +31137,23 @@ function compileClientBoth(source, _componentName, sourcePath2, opts) {
30999
31137
  const comp = emitClientFromIR(ir, { forceClient: true, nameAllocator: alloc });
31000
31138
  const aliases = vskRegistryAliases(ir.imports, ir.exportAliases);
31001
31139
  const reexportPaths = sourcePath2 ? collectVskReexportPaths(ir.reexportSources, sourcePath2) : [];
31002
- if (opts?.skipHyd) return { comp, hyd: "", name, aliases, reexportPaths };
31140
+ if (opts?.skipHyd) return { comp, hyd: "", name, aliases, reexportPaths, componentNames: ir.components.map((c) => c.name) };
31003
31141
  return {
31004
31142
  comp,
31005
31143
  hyd: emitClientFromIR(irHyd, { forceClient: true, hydrate: true, includeTopLevel: false, nameAllocator: alloc, markerless: opts?.markerless !== false }),
31006
31144
  name,
31007
31145
  aliases,
31008
- reexportPaths
31146
+ reexportPaths,
31147
+ // Every component NAME this file registers, not just the resolved one —
31148
+ // the bundle needs the full list to spot same-name declarations across
31149
+ // files (see findVskComponentCollisions).
31150
+ componentNames: ir.components.map((c) => c.name)
31009
31151
  };
31010
31152
  }
31153
+ function vskComponentNames(source, sourcePath2) {
31154
+ const ast = parse4(source, sourcePath2 ? { filename: sourcePath2 } : {});
31155
+ return generateIR(ast, source, sourcePath2).components.map((c) => c.name);
31156
+ }
31011
31157
  var BINDING_REF_CALLS, NON_BUBBLING_EVENTS, Ctx, PROPERTY_ATTRS;
31012
31158
  var init_client_codegen = __esm({
31013
31159
  "../compiler/src/client-codegen.ts"() {
@@ -31608,35 +31754,148 @@ var init_server_jsgen = __esm({
31608
31754
 
31609
31755
  // ../compiler/src/ssr-store.ts
31610
31756
  import { AsyncLocalStorage } from "node:async_hooks";
31757
+ function newSsrToken() {
31758
+ return Math.random().toString(36).slice(2);
31759
+ }
31760
+ function createSsrStore() {
31761
+ return { token: newSsrToken(), data: {} };
31762
+ }
31763
+ function adoptSsrStore() {
31764
+ return storage.getStore() ?? createSsrStore();
31765
+ }
31766
+ function isThenable(value) {
31767
+ return typeof value?.then === "function";
31768
+ }
31611
31769
  function withSsrStore(fn) {
31612
31770
  const existing = storage.getStore();
31613
31771
  if (existing) return fn();
31614
- return storage.run({}, fn);
31772
+ const store = createSsrStore();
31773
+ keepSsrSlot(store.token);
31774
+ let result;
31775
+ try {
31776
+ result = storage.run(store, fn);
31777
+ } catch (err) {
31778
+ dropSsrSlot(store.token);
31779
+ throw err;
31780
+ }
31781
+ if (isThenable(result)) {
31782
+ return result.then(
31783
+ (value) => {
31784
+ dropSsrSlot(store.token);
31785
+ return value;
31786
+ },
31787
+ (err) => {
31788
+ dropSsrSlot(store.token);
31789
+ throw err;
31790
+ }
31791
+ );
31792
+ }
31793
+ dropSsrSlot(store.token);
31794
+ return result;
31795
+ }
31796
+ function withSsrStoreOf(store, fn) {
31797
+ return storage.run(store, fn);
31798
+ }
31799
+ function keepSsrSlot(token) {
31800
+ if (!token) return;
31801
+ liveSlots.set(token, (liveSlots.get(token) ?? 0) + 1);
31615
31802
  }
31616
- var storage, ssrSink2;
31803
+ function dropSsrSlot(token) {
31804
+ if (!token) return;
31805
+ const count = liveSlots.get(token);
31806
+ if (count === void 0) return;
31807
+ if (count <= 1) liveSlots.delete(token);
31808
+ else liveSlots.set(token, count - 1);
31809
+ }
31810
+ function isSsrSlotLive(token) {
31811
+ return liveSlots.has(token);
31812
+ }
31813
+ var TOKEN_KEY, storage, liveSlots, ssrSink2;
31617
31814
  var init_ssr_store = __esm({
31618
31815
  "../compiler/src/ssr-store.ts"() {
31619
31816
  "use strict";
31620
31817
  init_index_server();
31818
+ TOKEN_KEY = "__vsk_ssr_token";
31621
31819
  storage = new AsyncLocalStorage();
31820
+ liveSlots = /* @__PURE__ */ new Map();
31622
31821
  ssrSink2 = {
31623
31822
  set: (key, value) => {
31624
31823
  const store = storage.getStore();
31625
- if (store) store[key] = value;
31824
+ if (store) store.data[key] = value;
31626
31825
  },
31627
31826
  get: (key) => {
31628
31827
  const store = storage.getStore();
31629
- return store ? store[key] : void 0;
31828
+ return store ? store.data[key] : void 0;
31630
31829
  },
31631
- snapshot: () => ({ ...storage.getStore() ?? {} }),
31830
+ snapshot: () => ({ ...storage.getStore()?.data ?? {} }),
31632
31831
  clear: () => {
31633
31832
  const store = storage.getStore();
31634
31833
  if (store) {
31635
- for (const key of Object.keys(store)) delete store[key];
31834
+ for (const key of Object.keys(store.data)) delete store.data[key];
31636
31835
  }
31637
31836
  }
31638
31837
  };
31639
31838
  setSsrSink(ssrSink2);
31839
+ Object.defineProperty(globalThis, TOKEN_KEY, {
31840
+ configurable: true,
31841
+ get() {
31842
+ return storage.getStore()?.token;
31843
+ },
31844
+ set(value) {
31845
+ const store = storage.getStore();
31846
+ if (store) store.token = value;
31847
+ }
31848
+ });
31849
+ }
31850
+ });
31851
+
31852
+ // ../compiler/src/vsk-collision.ts
31853
+ var VskComponentOwners, reportedCollisions;
31854
+ var init_vsk_collision = __esm({
31855
+ "../compiler/src/vsk-collision.ts"() {
31856
+ "use strict";
31857
+ VskComponentOwners = class {
31858
+ owners = /* @__PURE__ */ new Map();
31859
+ /** Records that `file` declares a component called `name`. */
31860
+ claim(name, file) {
31861
+ let set2 = this.owners.get(name);
31862
+ if (!set2) {
31863
+ set2 = /* @__PURE__ */ new Set();
31864
+ this.owners.set(name, set2);
31865
+ }
31866
+ set2.add(file);
31867
+ }
31868
+ /** Names declared by more than one file, each with its declaring files. */
31869
+ collisions() {
31870
+ const out = [];
31871
+ for (const [name, files] of this.owners) {
31872
+ if (files.size > 1) out.push({ name, files: [...files] });
31873
+ }
31874
+ return out.sort((a, b) => a.name.localeCompare(b.name));
31875
+ }
31876
+ /**
31877
+ * Warns once per colliding name. Returns the message per collision so a
31878
+ * caller (or a test) can assert on the text without capturing stderr.
31879
+ *
31880
+ * Deduped by name for the life of the process: the SSR path compiles one
31881
+ * file graph per `compileFile` call, so without this a single build would
31882
+ * repeat the same warning once per entry page.
31883
+ */
31884
+ report(context) {
31885
+ const collisions = this.collisions();
31886
+ const fresh = [];
31887
+ for (const { name, files } of collisions) {
31888
+ if (reportedCollisions.has(name)) continue;
31889
+ reportedCollisions.add(name);
31890
+ fresh.push(`${name}: ${files.join(", ")}`);
31891
+ console.warn(
31892
+ `[vesk] ${context}: component "${name}" is declared in ${files.length} files and resolves to only one of them \u2014 ${files.join(", ")}. Which one wins depends on compile order, and the client and server registries pick differently, so this can render a different component in the browser than in the SSR HTML. Rename one of them.`
31893
+ );
31894
+ }
31895
+ return fresh;
31896
+ }
31897
+ };
31898
+ reportedCollisions = /* @__PURE__ */ new Set();
31640
31899
  }
31641
31900
  });
31642
31901
 
@@ -31660,9 +31919,12 @@ function scopedVesk(fn, fallback) {
31660
31919
  return fn.__veskScope || fallback;
31661
31920
  }
31662
31921
  function compileFile(source, options) {
31663
- return compileFileInternal(source, options?.sourcePath, /* @__PURE__ */ new Set());
31922
+ const owners = new VskComponentOwners();
31923
+ const result = compileFileInternal(source, options?.sourcePath, /* @__PURE__ */ new Set(), owners);
31924
+ owners.report("SSR");
31925
+ return result;
31664
31926
  }
31665
- function compileFileInternal(source, sourcePath2, seenImportFiles) {
31927
+ function compileFileInternal(source, sourcePath2, seenImportFiles, owners) {
31666
31928
  if (sourcePath2) {
31667
31929
  const dir = dirname7(sourcePath2);
31668
31930
  source = inlineMdImportsFrom(source, sourcePath2, guessProjectRoots(dir));
@@ -31671,6 +31933,7 @@ function compileFileInternal(source, sourcePath2, seenImportFiles) {
31671
31933
  const ir = generateIR(ast, source, sourcePath2);
31672
31934
  const componentMap = buildComponentMap2(ir, true);
31673
31935
  const ownComponentNames = ir.components.map((c) => c.name);
31936
+ if (sourcePath2 && owners) for (const n of ownComponentNames) owners.claim(n, sourcePath2);
31674
31937
  const __vesk = loadRuntimeImports(ir.imports);
31675
31938
  applyLocalModuleImports(__vesk, ir.imports, sourcePath2);
31676
31939
  if (sourcePath2) {
@@ -31687,7 +31950,7 @@ function compileFileInternal(source, sourcePath2, seenImportFiles) {
31687
31950
  } catch {
31688
31951
  continue;
31689
31952
  }
31690
- const sub = compileFileInternal(importedSrc, importPath, seenImportFiles);
31953
+ const sub = compileFileInternal(importedSrc, importPath, seenImportFiles, owners);
31691
31954
  for (const [name, fn] of sub.componentMap) {
31692
31955
  if (!componentMap.has(name)) componentMap.set(name, fn);
31693
31956
  }
@@ -31716,29 +31979,32 @@ function render2(source, componentName, props = {}, registry = /* @__PURE__ */ n
31716
31979
  const targetComp = compiled.ir.components.find((c) => c.name === componentName);
31717
31980
  const isAsyncComp = !!(targetComp && (targetComp.isAsync || targetComp.ssrAwait));
31718
31981
  globalThis.__vsk_ssr = true;
31719
- if (!globalThis.__vsk_ssr_token) globalThis.__vsk_ssr_token = Math.random().toString(36).slice(2);
31720
- const renderToken = globalThis.__vsk_ssr_token;
31721
- if (isAsyncComp) {
31722
- return withSsrStore(() => (async () => {
31723
- let bodyHtml2;
31724
- try {
31725
- bodyHtml2 = await renderFn(props, fullRegistry, scopedVesk(renderFn, __vesk));
31726
- } finally {
31727
- delete globalThis.__vsk_ssr;
31728
- await settleSsrPromises(renderToken);
31729
- clearSsrCells(renderToken);
31730
- }
31731
- return bodyHtml2;
31732
- })());
31733
- }
31734
- let bodyHtml;
31735
- try {
31736
- bodyHtml = withSsrStore(() => renderFn(props, fullRegistry, scopedVesk(renderFn, __vesk)));
31737
- } finally {
31738
- delete globalThis.__vsk_ssr;
31739
- clearSsrCells(renderToken);
31740
- }
31741
- return bodyHtml;
31982
+ return withSsrStore(() => {
31983
+ if (!globalThis.__vsk_ssr_token) globalThis.__vsk_ssr_token = Math.random().toString(36).slice(2);
31984
+ const renderToken = globalThis.__vsk_ssr_token;
31985
+ keepSsrSlot(renderToken);
31986
+ if (isAsyncComp) {
31987
+ return (async () => {
31988
+ let bodyHtml2;
31989
+ try {
31990
+ bodyHtml2 = await renderFn(props, fullRegistry, scopedVesk(renderFn, __vesk));
31991
+ } finally {
31992
+ delete globalThis.__vsk_ssr;
31993
+ await settleSsrPromises(renderToken);
31994
+ clearSsrCells(renderToken);
31995
+ }
31996
+ return bodyHtml2;
31997
+ })();
31998
+ }
31999
+ let bodyHtml;
32000
+ try {
32001
+ bodyHtml = renderFn(props, fullRegistry, scopedVesk(renderFn, __vesk));
32002
+ } finally {
32003
+ delete globalThis.__vsk_ssr;
32004
+ clearSsrCells(renderToken);
32005
+ }
32006
+ return bodyHtml;
32007
+ });
31742
32008
  }
31743
32009
  function renderPage(source, componentName, props = {}, registry = /* @__PURE__ */ new Map(), options = {}) {
31744
32010
  resetVskState(!!options.hydrate, options.markerless !== false);
@@ -31762,9 +32028,10 @@ function renderPage(source, componentName, props = {}, registry = /* @__PURE__ *
31762
32028
  globalThis.__vsk_ssr = true;
31763
32029
  if (!globalThis.__vsk_ssr_token) globalThis.__vsk_ssr_token = Math.random().toString(36).slice(2);
31764
32030
  const renderToken = globalThis.__vsk_ssr_token;
32031
+ keepSsrSlot(renderToken);
31765
32032
  pruneSsrDataSlots();
31766
32033
  if (targetComp && (targetComp.isAsync || targetComp.ssrAwait)) {
31767
- return withSsrStore(() => (async () => {
32034
+ return (async () => {
31768
32035
  let bodyHtml2;
31769
32036
  try {
31770
32037
  bodyHtml2 = await renderFn(ssrProps, fullRegistry, scopedVesk(renderFn, __vesk));
@@ -31778,22 +32045,22 @@ function renderPage(source, componentName, props = {}, registry = /* @__PURE__ *
31778
32045
  head: renderHeadHtml(targetComp, ssrProps),
31779
32046
  props: ssrProps
31780
32047
  };
31781
- })());
32048
+ })();
31782
32049
  }
31783
32050
  let bodyHtml;
31784
32051
  try {
31785
- bodyHtml = withSsrStore(() => renderFn(ssrProps, fullRegistry, scopedVesk(renderFn, __vesk)));
32052
+ bodyHtml = renderFn(ssrProps, fullRegistry, scopedVesk(renderFn, __vesk));
31786
32053
  } finally {
31787
32054
  delete globalThis.__vsk_ssr;
31788
32055
  }
31789
32056
  const headHtml = targetComp ? renderHeadHtml(targetComp, ssrProps) : "";
31790
32057
  const pending = globalThis[`__vsk_ssr_promises_${renderToken}`];
31791
32058
  if (pending && pending.length > 0) {
31792
- return withSsrStore(() => (async () => {
32059
+ return (async () => {
31793
32060
  await settleSsrPromises(renderToken);
31794
32061
  clearSsrCells(renderToken);
31795
32062
  return { body: bodyHtml, head: headHtml, props: ssrProps };
31796
- })());
32063
+ })();
31797
32064
  }
31798
32065
  clearSsrCells(renderToken);
31799
32066
  return { body: bodyHtml, head: headHtml, props: ssrProps };
@@ -31817,6 +32084,7 @@ function clearSsrCells(token) {
31817
32084
  if (!token) return;
31818
32085
  delete globalThis[`__vsk_ssr_promises_${token}`];
31819
32086
  delete globalThis[`__vsk_ssr_failures_${token}`];
32087
+ dropSsrSlot(token);
31820
32088
  const cells = globalThis.__vsk_ssr_cells;
31821
32089
  if (!cells || !(cells instanceof Map)) return;
31822
32090
  for (const k of cells.keys()) {
@@ -31825,15 +32093,27 @@ function clearSsrCells(token) {
31825
32093
  }
31826
32094
  async function settleSsrPromises(token) {
31827
32095
  if (!token) return;
31828
- const pending = globalThis[`__vsk_ssr_promises_${token}`];
31829
- if (!pending || pending.length === 0) return;
31830
- await Promise.allSettled(pending);
32096
+ const key = `__vsk_ssr_promises_${token}`;
32097
+ for (let round = 0; round < 10; round++) {
32098
+ const pending = globalThis[key];
32099
+ if (!pending || pending.length === 0) return;
32100
+ const before = pending.length;
32101
+ await Promise.allSettled(pending.slice());
32102
+ const after = globalThis[key];
32103
+ if (!after || after.length <= before) return;
32104
+ }
31831
32105
  }
31832
32106
  function pruneSsrDataSlots() {
31833
32107
  const g2 = globalThis;
31834
- const keys = Object.keys(g2).filter((k) => typeof k === "string" && k.startsWith("__vsk_ssr_data_"));
31835
- if (keys.length <= 40) return;
31836
- keys.sort().slice(0, keys.length - 40).forEach((k) => {
32108
+ const prefix = "__vsk_ssr_data_";
32109
+ const stale = [];
32110
+ for (const k of Object.keys(g2)) {
32111
+ if (typeof k !== "string" || !k.startsWith(prefix)) continue;
32112
+ if (isSsrSlotLive(k.slice(prefix.length))) continue;
32113
+ stale.push(k);
32114
+ }
32115
+ if (stale.length <= 40) return;
32116
+ stale.sort().slice(0, stale.length - 40).forEach((k) => {
31837
32117
  delete g2[k];
31838
32118
  });
31839
32119
  }
@@ -31964,6 +32244,7 @@ async function renderFullPage(source, componentName, props = {}, registry = /* @
31964
32244
  globalThis.__vsk_ssr = true;
31965
32245
  if (!globalThis.__vsk_ssr_token) globalThis.__vsk_ssr_token = Math.random().toString(36).slice(2);
31966
32246
  const renderToken = globalThis.__vsk_ssr_token;
32247
+ keepSsrSlot(renderToken);
31967
32248
  pruneSsrDataSlots();
31968
32249
  globalThis.__vsk_ssr_data = {};
31969
32250
  try {
@@ -32040,12 +32321,15 @@ ${dataScriptBlock}${clientScript}</body>
32040
32321
  } finally {
32041
32322
  delete globalThis.__vsk_ssr;
32042
32323
  delete globalThis[`__vsk_ssr_data_${renderToken}`];
32043
- delete globalThis.__vsk_ssr_token;
32324
+ dropSsrSlot(renderToken);
32044
32325
  pruneSsrDataSlots();
32045
32326
  }
32046
32327
  });
32047
32328
  }
32048
32329
  function renderPageStream(source, componentName, props = {}, registry = /* @__PURE__ */ new Map(), options = {}) {
32330
+ const streamStore = adoptSsrStore();
32331
+ keepSsrSlot(streamStore.token);
32332
+ const streamToken = streamStore.token;
32049
32333
  async function* raw2() {
32050
32334
  const cached = options.cached || void 0;
32051
32335
  const ir = cached ? cached.ir : generateIR(parse4(source, options.sourcePath ? { filename: options.sourcePath } : {}), source, options.sourcePath);
@@ -32114,8 +32398,7 @@ function renderPageStream(source, componentName, props = {}, registry = /* @__PU
32114
32398
  yield '</head>\n<body>\n<div id="root">\n';
32115
32399
  globalThis.__vsk_ssr = true;
32116
32400
  globalThis.__vsk_ssr_data = {};
32117
- if (!globalThis.__vsk_ssr_token) globalThis.__vsk_ssr_token = Math.random().toString(36).slice(2);
32118
- const renderToken = globalThis.__vsk_ssr_token;
32401
+ const renderToken = streamToken;
32119
32402
  pruneSsrDataSlots();
32120
32403
  let bodyHtml;
32121
32404
  try {
@@ -32135,7 +32418,7 @@ function renderPageStream(source, componentName, props = {}, registry = /* @__PU
32135
32418
  console.error(`[render-trace] renderPageStream comp=${componentName} keys=${Object.keys(ssrData).join(",") || "\u2205"} slot=${Object.keys(slot).join(",") || "\u2205"} sink=${Object.keys(ssrSink2.snapshot()).join(",") || "\u2205"}`);
32136
32419
  }
32137
32420
  delete globalThis[`__vsk_ssr_data_${renderToken}`];
32138
- delete globalThis.__vsk_ssr_token;
32421
+ dropSsrSlot(renderToken);
32139
32422
  pruneSsrDataSlots();
32140
32423
  const dataScripts = buildDataScripts(ssrProps, ssrData, options.externalDataScript);
32141
32424
  const dataScriptBlock = dataScripts.length > 0 ? "\n" + dataScripts.join("\n") : "";
@@ -32148,11 +32431,20 @@ function renderPageStream(source, componentName, props = {}, registry = /* @__PU
32148
32431
  }
32149
32432
  const gen = raw2();
32150
32433
  async function* scoped() {
32151
- let result;
32152
- do {
32153
- result = await withSsrStore(() => gen.next());
32154
- if (!result.done) yield result.value;
32155
- } while (!result.done);
32434
+ try {
32435
+ let result;
32436
+ do {
32437
+ result = await withSsrStoreOf(streamStore, () => gen.next());
32438
+ if (!result.done) yield result.value;
32439
+ } while (!result.done);
32440
+ } finally {
32441
+ dropSsrSlot(streamToken);
32442
+ delete globalThis[`__vsk_ssr_data_${streamToken}`];
32443
+ delete globalThis[`__vsk_ssr_promises_${streamToken}`];
32444
+ delete globalThis[`__vsk_ssr_failures_${streamToken}`];
32445
+ delete globalThis.__vsk_ssr_data;
32446
+ pruneSsrDataSlots();
32447
+ }
32156
32448
  }
32157
32449
  return scoped();
32158
32450
  }
@@ -32171,6 +32463,7 @@ var init_server_render = __esm({
32171
32463
  init_md_inline();
32172
32464
  init_ssr_store();
32173
32465
  init_module_imports();
32466
+ init_vsk_collision();
32174
32467
  }
32175
32468
  });
32176
32469
 
@@ -37334,19 +37627,34 @@ var init_errors2 = __esm({
37334
37627
  tip: isBuiltin ? `"${name}" is a Vesk built-in. Use it directly \u2014 no manual import needed.` : ""
37335
37628
  });
37336
37629
  }
37337
- static vskNamespaceTag(context = {}) {
37338
- return new _VeskError("Namespace imports are not supported for `.vsk` modules.", {
37630
+ /**
37631
+ * A `.vsk` namespace is resolved at the JSX-tag level: `<ns.Icon />` looks
37632
+ * the component up in the registry by its exported name, exactly like
37633
+ * `import { Icon }`, and no module object is ever built. The two forms that
37634
+ * genuinely need a real module object are reported here.
37635
+ *
37636
+ * - `nested` — `<ns.Sub.Icon />`. A `.vsk` module's exports are flat
37637
+ * component names, so there is no object to walk into.
37638
+ * - `value` — `ns.max` read as a value. Component tags resolve; plain value
37639
+ * reads do not, because nothing binds `ns` at runtime.
37640
+ */
37641
+ static vskNamespaceMember(context = {}) {
37642
+ const nested = context.form === "nested";
37643
+ return new _VeskError(nested ? "A `.vsk` namespace is flat, so `<ns.Sub.Icon />` cannot be resolved." : "A `.vsk` namespace resolves component tags only, so `ns.value` is not available.", {
37339
37644
  ...context,
37340
37645
  code: context.code || "V0410",
37341
- suggestions: [
37342
- "Import the component by name: import { MyIcon } from './icons.vsk'",
37343
- "Rename on import: import { MyIcon as Icon } from './icons.vsk'"
37646
+ suggestions: nested ? [
37647
+ "Import the component by name: import { Icon } from './icons.vsk'",
37648
+ "Import from the module that owns Sub: import * as Sub from './sub.vsk'"
37649
+ ] : [
37650
+ "Import the value by name: import { MAX } from './constants.vsk'",
37651
+ "Move the value into a .ts module and import it from there \u2014 those are real ES modules with live bindings."
37344
37652
  ],
37345
37653
  nextSteps: [
37346
- "A `.vsk` component is a registry entry keyed by its name, not a module namespace object, so `<ns.Icon />` cannot be resolved.",
37347
- "If you need a namespace object, import from a `.ts`/`.js` module instead \u2014 those are real ES modules."
37654
+ "A `.vsk` tag resolves by component name in the global registry, not by module exports, so `import * as ns` + `<ns.Icon />` needs no module object.",
37655
+ nested ? "Re-export with a flat name (`export { Icon }`) or import the component directly." : "Re-export the value under a flat name and import it directly, or keep the value in a .ts module."
37348
37656
  ],
37349
- tip: "A `.vsk` module exposes its components as named exports; use a named or renamed import rather than `import * as ns`."
37657
+ tip: "`<ns.Icon />` works; only nested paths and value reads need a real module namespace."
37350
37658
  });
37351
37659
  }
37352
37660
  static classDecl(context = {}) {
@@ -40688,54 +40996,67 @@ ${layoutDecls.map((d) => " " + d.compiled + ",").join("\n")}
40688
40996
  let dataCode;
40689
40997
  const exposeErr = "process.env.NODE_ENV !== 'production'";
40690
40998
  const dataNavCleanup = [
40691
- " } finally {",
40692
- " const __dnToken = globalThis.__vsk_ssr_token;",
40693
- " delete globalThis.__vsk_ssr;",
40694
- " if (__dnToken) delete globalThis[`__vsk_ssr_data_${__dnToken}`];",
40695
- " delete globalThis.__vsk_ssr_token;",
40696
- " delete globalThis.__vsk_ssr_data;",
40697
- " }"
40999
+ " } finally {",
41000
+ " const __dnToken = globalThis.__vsk_ssr_token;",
41001
+ " delete globalThis.__vsk_ssr;",
41002
+ " if (__dnToken) delete globalThis[`__vsk_ssr_data_${__dnToken}`];",
41003
+ // NOT `delete globalThis.__vsk_ssr_token`: that global is a
41004
+ // configurable AsyncLocalStorage accessor (ssr-store.ts), so `delete`
41005
+ // removes the property definition and the process silently reverts to a
41006
+ // process-global token — the cross-request data-slot race, for good. The
41007
+ // token dies with the withSsrStore scope wrapped around this block instead.
41008
+ " delete globalThis.__vsk_ssr_data;",
41009
+ " }"
41010
+ ];
41011
+ const dataNavWrap = (body) => [
41012
+ " return withSsrStore(async () => {",
41013
+ ...body.map((l) => l ? " " + l : l),
41014
+ " });"
40698
41015
  ];
40699
41016
  if (layoutDecls.length > 0) {
40700
41017
  dataCode = [
40701
41018
  " if (request.headers.get('x-vesk-data') === '1') {",
40702
- " try {",
40703
- " let dataPage;",
40704
- " try {",
40705
- " dataPage = await renderPage('', _pageComp, { params }, __componentRegistry, { hydrate: true, cached: _pageCompiled });",
40706
- " } catch (err) {",
40707
- " if (err && (err.name === 'NotFoundError' || err.name === 'Redirect')) throw err;",
40708
- " const message = err && typeof err === 'object' && 'message' in err ? String(err.message) : String(err);",
40709
- ` return new Response(JSON.stringify({ error: ${exposeErr} ? message : 'Internal Server Error' }), { status: 500, headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', Vary: 'x-vesk-data' } });`,
40710
- " }",
40711
- " let _dataHead = dataPage.head || '';",
40712
- " for (let _i = _layoutCompList.length - 1; _i >= 0; _i--) {",
40713
- " const _dl = await renderPage('', _layoutCompList[_i], { params, children: '' }, __componentRegistry, { hydrate: true, cached: _layoutCompiledList[_i] });",
40714
- " _dataHead = (_dl.head || '') + _dataHead;",
40715
- " }",
40716
- " return new Response(JSON.stringify({ path: url.pathname, params, props: dataPage.props || { params }, head: _dataHead }), {",
40717
- " headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', Vary: 'x-vesk-data' },",
40718
- " });",
40719
- ...dataNavCleanup,
41019
+ ...dataNavWrap([
41020
+ " try {",
41021
+ " let dataPage;",
41022
+ " try {",
41023
+ " dataPage = await renderPage('', _pageComp, { params }, __componentRegistry, { hydrate: true, cached: _pageCompiled });",
41024
+ " } catch (err) {",
41025
+ " if (err && (err.name === 'NotFoundError' || err.name === 'Redirect')) throw err;",
41026
+ " const message = err && typeof err === 'object' && 'message' in err ? String(err.message) : String(err);",
41027
+ ` return new Response(JSON.stringify({ error: ${exposeErr} ? message : 'Internal Server Error' }), { status: 500, headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', Vary: 'x-vesk-data' } });`,
41028
+ " }",
41029
+ " let _dataHead = dataPage.head || '';",
41030
+ " for (let _i = _layoutCompList.length - 1; _i >= 0; _i--) {",
41031
+ " const _dl = await renderPage('', _layoutCompList[_i], { params, children: '' }, __componentRegistry, { hydrate: true, cached: _layoutCompiledList[_i] });",
41032
+ " _dataHead = (_dl.head || '') + _dataHead;",
41033
+ " }",
41034
+ " return new Response(JSON.stringify({ path: url.pathname, params, props: dataPage.props || { params }, head: _dataHead }), {",
41035
+ " headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', Vary: 'x-vesk-data' },",
41036
+ " });",
41037
+ ...dataNavCleanup
41038
+ ]),
40720
41039
  " }",
40721
41040
  " return __renderHtml(params, url.href);"
40722
41041
  ].join("\n");
40723
41042
  } else {
40724
41043
  dataCode = [
40725
41044
  " if (request.headers.get('x-vesk-data') === '1') {",
40726
- " try {",
40727
- " let dataPage;",
40728
- " try {",
40729
- " dataPage = await renderPage('', _comp, { params }, __componentRegistry, { hydrate: true, cached: _compiled });",
40730
- " } catch (err) {",
40731
- " if (err && (err.name === 'NotFoundError' || err.name === 'Redirect')) throw err;",
40732
- " const message = err && typeof err === 'object' && 'message' in err ? String(err.message) : String(err);",
40733
- ` return new Response(JSON.stringify({ error: ${exposeErr} ? message : 'Internal Server Error' }), { status: 500, headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', Vary: 'x-vesk-data' } });`,
40734
- " }",
40735
- " return new Response(JSON.stringify({ path: url.pathname, params, props: dataPage.props || { params }, head: dataPage.head || '' }), {",
40736
- " headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', Vary: 'x-vesk-data' },",
40737
- " });",
40738
- ...dataNavCleanup,
41045
+ ...dataNavWrap([
41046
+ " try {",
41047
+ " let dataPage;",
41048
+ " try {",
41049
+ " dataPage = await renderPage('', _comp, { params }, __componentRegistry, { hydrate: true, cached: _compiled });",
41050
+ " } catch (err) {",
41051
+ " if (err && (err.name === 'NotFoundError' || err.name === 'Redirect')) throw err;",
41052
+ " const message = err && typeof err === 'object' && 'message' in err ? String(err.message) : String(err);",
41053
+ ` return new Response(JSON.stringify({ error: ${exposeErr} ? message : 'Internal Server Error' }), { status: 500, headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', Vary: 'x-vesk-data' } });`,
41054
+ " }",
41055
+ " return new Response(JSON.stringify({ path: url.pathname, params, props: dataPage.props || { params }, head: dataPage.head || '' }), {",
41056
+ " headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', Vary: 'x-vesk-data' },",
41057
+ " });",
41058
+ ...dataNavCleanup
41059
+ ]),
40739
41060
  " }",
40740
41061
  " return __renderHtml(params, url.href);"
40741
41062
  ].join("\n");
@@ -41425,6 +41746,7 @@ init_esbuild_fallback();
41425
41746
  init_strip_ts();
41426
41747
  init_parser();
41427
41748
  init_client_codegen();
41749
+ init_vsk_collision();
41428
41750
  import { readFileSync as readFileSync11, existsSync as existsSync11, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, statSync as statSync3, rmSync, mkdtempSync, mkdirSync as mkdirSync3 } from "node:fs";
41429
41751
  import { resolve as resolve10, join as join7, dirname as dirname8, relative as relative2, sep } from "node:path";
41430
41752
  import { fileURLToPath as fileURLToPath2 } from "node:url";
@@ -41900,7 +42222,8 @@ async function generateClientBundle(routeTree, appDir, componentMap, options) {
41900
42222
  }
41901
42223
  const namesBefore = cache2 ? new Set(runtimeImportNames) : null;
41902
42224
  const importedPaths = resolveVskImports(filePath, (p, n) => compileFile2(p, n, output, imports));
41903
- const { comp: rawComp, hyd: rawHyd, name: actualName, aliases, reexportPaths } = compileClientBoth(src2, null, filePath);
42225
+ const { comp: rawComp, hyd: rawHyd, name: actualName, aliases, reexportPaths, componentNames } = compileClientBoth(src2, null, filePath);
42226
+ for (const cn of componentNames || []) componentOwners.claim(cn, filePath);
41904
42227
  const compWithImports = rawComp ? stripExports(stripVskImports(stripRuntimeImport(rawComp))) : "";
41905
42228
  const hydWithImports = rawHyd ? stripExports(stripVskImports(stripRuntimeImport(rawHyd))) : "";
41906
42229
  const compSpecs = [];
@@ -41950,6 +42273,7 @@ async function generateClientBundle(routeTree, appDir, componentMap, options) {
41950
42273
  }
41951
42274
  const codeSplit = !!options?.codeSplit;
41952
42275
  const transformPlugins = options?.plugins || [];
42276
+ const componentOwners = new VskComponentOwners();
41953
42277
  async function applyTransformPlugins(code, filePath) {
41954
42278
  if (transformPlugins.length === 0) return code;
41955
42279
  let out = code;
@@ -42088,6 +42412,7 @@ ${entry.code}
42088
42412
  for (const chunk of chunks) {
42089
42413
  transformedChunks.push({ ...chunk, code: await applyTransformPlugins(chunk.code, chunk.name) });
42090
42414
  }
42415
+ componentOwners.report("client bundle");
42091
42416
  return { main: await applyTransformPlugins(main, "client.js"), chunks: transformedChunks, cachedFileHits, compiledFiles, mainFromCache, editedSources, editedNames };
42092
42417
  } else {
42093
42418
  let compileFileMono2 = function(filePath, resolvedName) {
@@ -42095,6 +42420,7 @@ ${entry.code}
42095
42420
  seen.add(filePath);
42096
42421
  const src2 = readFileSync11(filePath, "utf-8");
42097
42422
  resolveVskImports(filePath, (p, n) => compileFileMono2(p, n || ""));
42423
+ for (const cn of vskComponentNames(src2, filePath)) componentOwners.claim(cn, filePath);
42098
42424
  const monoAlloc = nameAllocFor(filePath);
42099
42425
  const compCode = compileClient(src2, null, { forceClient: true, sourcePath: filePath, nameAllocator: monoAlloc });
42100
42426
  const hydCode = compileClient(src2, null, { hydrate: true, forceClient: true, includeTopLevel: false, sourcePath: filePath, nameAllocator: monoAlloc });
@@ -42144,6 +42470,7 @@ ${entry.code}
42144
42470
  aliasLines,
42145
42471
  hydratorAliasLines
42146
42472
  }, !!options?.hmr, !!options?.importRuntime, runtimeImportNames, options?.routeDataCache);
42473
+ componentOwners.report("client bundle");
42147
42474
  return { main: await applyTransformPlugins(main, "client.js"), chunks: [] };
42148
42475
  }
42149
42476
  }
@@ -43562,33 +43889,104 @@ init_server_render();
43562
43889
  // ../compiler/dist/ssr-store.js
43563
43890
  init_index_server();
43564
43891
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
43892
+ var TOKEN_KEY2 = "__vsk_ssr_token";
43565
43893
  var storage2 = new AsyncLocalStorage2();
43894
+ var liveSlots2 = /* @__PURE__ */ new Map();
43895
+ function newSsrToken2() {
43896
+ return Math.random().toString(36).slice(2);
43897
+ }
43898
+ function createSsrStore3() {
43899
+ return { token: newSsrToken2(), data: {} };
43900
+ }
43901
+ function isThenable2(value) {
43902
+ return typeof value?.then === "function";
43903
+ }
43566
43904
  function withSsrStore2(fn) {
43567
43905
  const existing = storage2.getStore();
43568
43906
  if (existing)
43569
43907
  return fn();
43570
- return storage2.run({}, fn);
43908
+ const store = createSsrStore3();
43909
+ keepSsrSlot2(store.token);
43910
+ let result;
43911
+ try {
43912
+ result = storage2.run(store, fn);
43913
+ } catch (err) {
43914
+ dropSsrSlot2(store.token);
43915
+ throw err;
43916
+ }
43917
+ if (isThenable2(result)) {
43918
+ return result.then((value) => {
43919
+ dropSsrSlot2(store.token);
43920
+ return value;
43921
+ }, (err) => {
43922
+ dropSsrSlot2(store.token);
43923
+ throw err;
43924
+ });
43925
+ }
43926
+ dropSsrSlot2(store.token);
43927
+ return result;
43928
+ }
43929
+ function withSsrStoreOf2(store, fn) {
43930
+ return storage2.run(store, fn);
43931
+ }
43932
+ function resetSsrToken() {
43933
+ const store = storage2.getStore();
43934
+ const token = newSsrToken2();
43935
+ if (store) {
43936
+ if (store.token)
43937
+ liveSlots2.delete(store.token);
43938
+ store.token = token;
43939
+ }
43940
+ liveSlots2.set(token, 1);
43941
+ return token;
43942
+ }
43943
+ function keepSsrSlot2(token) {
43944
+ if (!token)
43945
+ return;
43946
+ liveSlots2.set(token, (liveSlots2.get(token) ?? 0) + 1);
43947
+ }
43948
+ function dropSsrSlot2(token) {
43949
+ if (!token)
43950
+ return;
43951
+ const count = liveSlots2.get(token);
43952
+ if (count === void 0)
43953
+ return;
43954
+ if (count <= 1)
43955
+ liveSlots2.delete(token);
43956
+ else
43957
+ liveSlots2.set(token, count - 1);
43571
43958
  }
43572
43959
  var ssrSink3 = {
43573
43960
  set: (key, value) => {
43574
43961
  const store = storage2.getStore();
43575
43962
  if (store)
43576
- store[key] = value;
43963
+ store.data[key] = value;
43577
43964
  },
43578
43965
  get: (key) => {
43579
43966
  const store = storage2.getStore();
43580
- return store ? store[key] : void 0;
43967
+ return store ? store.data[key] : void 0;
43581
43968
  },
43582
- snapshot: () => ({ ...storage2.getStore() ?? {} }),
43969
+ snapshot: () => ({ ...storage2.getStore()?.data ?? {} }),
43583
43970
  clear: () => {
43584
43971
  const store = storage2.getStore();
43585
43972
  if (store) {
43586
- for (const key of Object.keys(store))
43587
- delete store[key];
43973
+ for (const key of Object.keys(store.data))
43974
+ delete store.data[key];
43588
43975
  }
43589
43976
  }
43590
43977
  };
43591
43978
  setSsrSink(ssrSink3);
43979
+ Object.defineProperty(globalThis, TOKEN_KEY2, {
43980
+ configurable: true,
43981
+ get() {
43982
+ return storage2.getStore()?.token;
43983
+ },
43984
+ set(value) {
43985
+ const store = storage2.getStore();
43986
+ if (store)
43987
+ store.token = value;
43988
+ }
43989
+ });
43592
43990
 
43593
43991
  // ../compiler/dist/client-codegen.js
43594
43992
  init_ir();
@@ -45890,13 +46288,17 @@ function compileClientBoth2(source, _componentName, sourcePath2, opts) {
45890
46288
  const aliases = vskRegistryAliases(ir.imports, ir.exportAliases);
45891
46289
  const reexportPaths = sourcePath2 ? collectVskReexportPaths(ir.reexportSources, sourcePath2) : [];
45892
46290
  if (opts?.skipHyd)
45893
- return { comp, hyd: "", name, aliases, reexportPaths };
46291
+ return { comp, hyd: "", name, aliases, reexportPaths, componentNames: ir.components.map((c) => c.name) };
45894
46292
  return {
45895
46293
  comp,
45896
46294
  hyd: emitClientFromIR2(irHyd, { forceClient: true, hydrate: true, includeTopLevel: false, nameAllocator: alloc, markerless: opts?.markerless !== false }),
45897
46295
  name,
45898
46296
  aliases,
45899
- reexportPaths
46297
+ reexportPaths,
46298
+ // Every component NAME this file registers, not just the resolved one —
46299
+ // the bundle needs the full list to spot same-name declarations across
46300
+ // files (see findVskComponentCollisions).
46301
+ componentNames: ir.components.map((c) => c.name)
45900
46302
  };
45901
46303
  }
45902
46304
 
@@ -47095,6 +47497,7 @@ async function build3(options) {
47095
47497
  init_strip_ts();
47096
47498
  init_parser();
47097
47499
  init_client_codegen();
47500
+ init_vsk_collision();
47098
47501
  init_vsk_imports();
47099
47502
  init_md_inline();
47100
47503
  init_module_imports();
@@ -47612,7 +48015,9 @@ async function generateClientBundle2(routeTree, appDir, componentMap, options) {
47612
48015
  }
47613
48016
  const namesBefore = cache2 ? new Set(runtimeImportNames) : null;
47614
48017
  const importedPaths = resolveVskImports(filePath, (p, n) => compileFile2(p, n, output, imports));
47615
- const { comp: rawComp, hyd: rawHyd, name: actualName, aliases, reexportPaths } = compileClientBoth(src2, null, filePath);
48018
+ const { comp: rawComp, hyd: rawHyd, name: actualName, aliases, reexportPaths, componentNames } = compileClientBoth(src2, null, filePath);
48019
+ for (const cn of componentNames || [])
48020
+ componentOwners.claim(cn, filePath);
47616
48021
  const compWithImports = rawComp ? stripExports(stripVskImports(stripRuntimeImport(rawComp))) : "";
47617
48022
  const hydWithImports = rawHyd ? stripExports(stripVskImports(stripRuntimeImport(rawHyd))) : "";
47618
48023
  const compSpecs = [];
@@ -47666,6 +48071,7 @@ async function generateClientBundle2(routeTree, appDir, componentMap, options) {
47666
48071
  }
47667
48072
  const codeSplit = !!options?.codeSplit;
47668
48073
  const transformPlugins = options?.plugins || [];
48074
+ const componentOwners = new VskComponentOwners();
47669
48075
  async function applyTransformPlugins(code, filePath) {
47670
48076
  if (transformPlugins.length === 0)
47671
48077
  return code;
@@ -47811,6 +48217,7 @@ ${entry.code}
47811
48217
  for (const chunk of chunks) {
47812
48218
  transformedChunks.push({ ...chunk, code: await applyTransformPlugins(chunk.code, chunk.name) });
47813
48219
  }
48220
+ componentOwners.report("client bundle");
47814
48221
  return { main: await applyTransformPlugins(main, "client.js"), chunks: transformedChunks, cachedFileHits, compiledFiles, mainFromCache, editedSources, editedNames };
47815
48222
  } else {
47816
48223
  let compileFileMono2 = function(filePath, resolvedName) {
@@ -47819,6 +48226,8 @@ ${entry.code}
47819
48226
  seen.add(filePath);
47820
48227
  const src2 = readFileSync24(filePath, "utf-8");
47821
48228
  resolveVskImports(filePath, (p, n) => compileFileMono2(p, n || ""));
48229
+ for (const cn of vskComponentNames(src2, filePath))
48230
+ componentOwners.claim(cn, filePath);
47822
48231
  const monoAlloc = nameAllocFor(filePath);
47823
48232
  const compCode = compileClient(src2, null, { forceClient: true, sourcePath: filePath, nameAllocator: monoAlloc });
47824
48233
  const hydCode = compileClient(src2, null, { hydrate: true, forceClient: true, includeTopLevel: false, sourcePath: filePath, nameAllocator: monoAlloc });
@@ -47878,6 +48287,7 @@ ${entry.code}
47878
48287
  aliasLines,
47879
48288
  hydratorAliasLines
47880
48289
  }, !!options?.hmr, !!options?.importRuntime, runtimeImportNames, options?.routeDataCache);
48290
+ componentOwners.report("client bundle");
47881
48291
  return { main: await applyTransformPlugins(main, "client.js"), chunks: [] };
47882
48292
  }
47883
48293
  }
@@ -54412,8 +54822,41 @@ var ROUTE_STRUCTURAL_VSK = /* @__PURE__ */ new Set([
54412
54822
  "offline.vsk",
54413
54823
  "network.vsk"
54414
54824
  ]);
54415
- var HMR_SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".vesk", "tarballs", ".git"]);
54416
- var HMR_SKIP_MARKERS = ["node_modules/", ".vesk/", "tmp-vesk-chunk-"];
54825
+ var HMR_SKIP_DIRS = /* @__PURE__ */ new Set([
54826
+ "node_modules",
54827
+ ".vesk",
54828
+ "tarballs",
54829
+ ".git",
54830
+ "dist",
54831
+ "build",
54832
+ "coverage",
54833
+ "out",
54834
+ ".next",
54835
+ ".nuxt",
54836
+ ".output",
54837
+ ".svelte-kit",
54838
+ ".turbo",
54839
+ ".cache",
54840
+ ".vercel",
54841
+ ".netlify"
54842
+ ]);
54843
+ var HMR_SKIP_MARKERS = [
54844
+ "node_modules/",
54845
+ ".vesk/",
54846
+ "tmp-vesk-chunk-",
54847
+ "/dist/",
54848
+ "/build/",
54849
+ "/coverage/",
54850
+ "/out/",
54851
+ "/.next/",
54852
+ "/.nuxt/",
54853
+ "/.output/",
54854
+ "/.svelte-kit/",
54855
+ "/.turbo/",
54856
+ "/.cache/",
54857
+ "/.vercel/",
54858
+ "/.netlify/"
54859
+ ];
54417
54860
  var HMR_SCRIPT_EXTS = [".mts", ".ts", ".tsx", ".mjs", ".js", ".cjs"];
54418
54861
  var HMR_CONFIG_NAMES = /* @__PURE__ */ new Set([
54419
54862
  "vesk.config.ts",
@@ -54960,6 +55403,7 @@ async function startDevServer(port, projectDir, config, host) {
54960
55403
  const treeChanged = needsRescan && prevTree !== stripAnnots(routeTree);
54961
55404
  const isOutsideApp = !fullPath.startsWith(appDirPath + "/") && fullPath !== appDirPath;
54962
55405
  const fileDeleted = eventType === "rename" && !fileExists;
55406
+ if (isOutsideApp) ssrCompileCache.clear();
54963
55407
  const cssPromise = rebuildTailwindCss();
54964
55408
  let bundleError = null;
54965
55409
  let hotEditedSource = null;
@@ -55104,6 +55548,7 @@ Object.defineProperty(__components, ${JSON.stringify(cname)}, { get: () => __com
55104
55548
  const t0 = Date.now();
55105
55549
  try {
55106
55550
  bcast({ type: "compiling" });
55551
+ ssrCompileCache.clear();
55107
55552
  await fullRebuild();
55108
55553
  LOG.info(`${kind === "config" ? "config" : "script"} changed, full rebuild + reload (${filename}) \u2014 ${Date.now() - t0}ms`);
55109
55554
  } catch (e) {
@@ -55411,11 +55856,11 @@ Object.defineProperty(__components, ${JSON.stringify(cname)}, { get: () => __com
55411
55856
  const forData = req.headers["x-vesk-data"] === "1";
55412
55857
  async function renderSSR() {
55413
55858
  const ssg2 = globalThis;
55414
- const freshToken = Math.random().toString(36).slice(2);
55415
- ssg2.__vsk_ssr_token = freshToken;
55416
55859
  ssg2.__vsk_ssr_data = {};
55860
+ let freshToken;
55417
55861
  try {
55418
55862
  return await withSsrStore2(async () => {
55863
+ freshToken = resetSsrToken();
55419
55864
  const chain = cleanChain;
55420
55865
  let body = "";
55421
55866
  let head = "";
@@ -55490,17 +55935,16 @@ ${prettifyHtml(body)}
55490
55935
  return { html, props: props || { params: matched.params }, head };
55491
55936
  });
55492
55937
  } finally {
55493
- delete ssg2[`__vsk_ssr_data_${freshToken}`];
55494
- delete ssg2[`__vsk_ssr_promises_${freshToken}`];
55495
- delete ssg2[`__vsk_ssr_failures_${freshToken}`];
55496
- if (ssg2.__vsk_ssr_token === freshToken) delete ssg2.__vsk_ssr_token;
55938
+ const token = freshToken;
55939
+ dropSsrSlot2(token);
55940
+ delete ssg2[`__vsk_ssr_data_${token}`];
55941
+ delete ssg2[`__vsk_ssr_promises_${token}`];
55942
+ delete ssg2[`__vsk_ssr_failures_${token}`];
55497
55943
  delete ssg2.__vsk_ssr_data;
55498
55944
  }
55499
55945
  }
55500
55946
  function renderSSRStream() {
55501
55947
  const sst = globalThis;
55502
- const streamToken = Math.random().toString(36).slice(2);
55503
- sst.__vsk_ssr_token = streamToken;
55504
55948
  sst.__vsk_ssr_data = {};
55505
55949
  async function* raw2() {
55506
55950
  const chain = cleanChain;
@@ -55564,18 +56008,22 @@ ${prettifyHtml(body)}
55564
56008
  yield ' <script type="module" src="/_vesk/client.js"></script>\n</body>\n</html>';
55565
56009
  }
55566
56010
  const gen = raw2();
56011
+ const streamStore = createSsrStore3();
55567
56012
  async function* scoped() {
55568
56013
  try {
55569
56014
  let result;
55570
56015
  do {
55571
- result = await withSsrStore2(() => gen.next());
56016
+ result = await withSsrStoreOf2(streamStore, () => gen.next());
55572
56017
  if (!result.done) yield result.value;
55573
56018
  } while (!result.done);
55574
56019
  } finally {
55575
- delete sst[`__vsk_ssr_data_${streamToken}`];
55576
- delete sst[`__vsk_ssr_promises_${streamToken}`];
55577
- delete sst[`__vsk_ssr_failures_${streamToken}`];
55578
- if (sst.__vsk_ssr_token === streamToken) delete sst.__vsk_ssr_token;
56020
+ const token = streamStore.token;
56021
+ dropSsrSlot2(token);
56022
+ if (token) {
56023
+ delete sst[`__vsk_ssr_data_${token}`];
56024
+ delete sst[`__vsk_ssr_promises_${token}`];
56025
+ delete sst[`__vsk_ssr_failures_${token}`];
56026
+ }
55579
56027
  delete sst.__vsk_ssr_data;
55580
56028
  }
55581
56029
  }
@@ -55794,7 +56242,6 @@ ${prettifyHtml(body)}
55794
56242
  LOG.info(`${projectDir}`);
55795
56243
  LOG.info(`${pageCount} page${pageCount === 1 ? "" : "s"}: ${routes.join(", ") || "(none)"}`);
55796
56244
  if (apiCount > 0) LOG.info(`${apiCount} api route${apiCount === 1 ? "" : "s"} (app/api)`);
55797
- LOG.info("hmr enabled \u2014 edit app/ to hot reload");
55798
56245
  });
55799
56246
  updateSourceMapping();
55800
56247
  const hmrClients = /* @__PURE__ */ new Set();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vesk/vesk-cli",
3
- "version": "0.2.43",
3
+ "version": "0.2.44",
4
4
  "type": "module",
5
5
  "description": "Vesk — compiler-first framework for the post-VDOM web. vesk dev, vesk build.",
6
6
  "bin": {
@@ -8,10 +8,10 @@
8
8
  },
9
9
  "main": "./dist/cli.js",
10
10
  "dependencies": {
11
- "@vesk/adapter": "^0.2.43",
12
- "@vesk/compiler": "^0.2.43",
13
- "@vesk/plugin-tailwind": "^0.2.43",
14
- "@vesk/runtime": "^0.2.43",
11
+ "@vesk/adapter": "^0.2.44",
12
+ "@vesk/compiler": "^0.2.44",
13
+ "@vesk/plugin-tailwind": "^0.2.44",
14
+ "@vesk/runtime": "^0.2.44",
15
15
  "esrap": "^2.3.0",
16
16
  "typescript": "^5.8.0",
17
17
  "ws": "^8.18.0",