@vesk/vesk-cli 0.2.58 → 0.2.59

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 +341 -15
  2. package/package.json +5 -5
package/dist/cli.js CHANGED
@@ -3369,6 +3369,17 @@ ${String(this.root.outerHTML).slice(0, 800)}` : ""),
3369
3369
  // callee's first claim and resets the deposit so later claims stay
3370
3370
  // positional against the advancing cursor. Consumed synchronously on the
3371
3371
  // same walker reference the call was made through.
3372
+ //
3373
+ // The deposit is ALSO consumed implicitly by the next claim made through this
3374
+ // walker when the caller passed no explicit skipK. A component written in plain
3375
+ // JS (lucide-vesk icons are the common case) never calls `takeSkipK`, so
3376
+ // before this the deposit was silently dropped: the icon's `nextElement("svg")`
3377
+ // claimed the slot still holding the STATIC residue in front of it
3378
+ // (`<button><span>Apply Now</span><svg/></button>`), the descriptor missed on
3379
+ // `<span>`, and the walker detached server-rendered content that no client
3380
+ // program had any intention of replacing. Implicit consumption keeps static
3381
+ // residue out of the way for plain-JS components exactly as it is for
3382
+ // compiled ones.
3372
3383
  inheritOffset = 0;
3373
3384
  injectSkipK(n) {
3374
3385
  this.inheritOffset = n;
@@ -3382,7 +3393,11 @@ ${String(this.root.outerHTML).slice(0, 800)}` : ""),
3382
3393
  if (globalThis.__vesk_hydrate_debug) {
3383
3394
  console.error("[hyd-dbg] claimAt", tag, "skip=", skipK, "idx=", this.idx, "els=", this.els.length);
3384
3395
  }
3385
- let target = this.advancePastSkippable(this.idx + (skipK || 0));
3396
+ let offset2 = skipK || 0;
3397
+ const inherited = this.inheritOffset;
3398
+ this.inheritOffset = 0;
3399
+ if (!offset2) offset2 = inherited;
3400
+ let target = this.advancePastSkippable(this.idx + offset2);
3386
3401
  if (target > this.els.length) target = this.idx;
3387
3402
  while (target < this.els.length) {
3388
3403
  const el = this.els[target];
@@ -21909,6 +21924,7 @@ function warnStaleConstBindings(comp, file) {
21909
21924
  for (const d of st.declarations || []) {
21910
21925
  if (d.id?.type !== "Identifier" || !d.init) continue;
21911
21926
  if (reactive.has(d.id.name)) continue;
21927
+ if (d.init.type === "ArrowFunctionExpression" || d.init.type === "FunctionExpression") continue;
21912
21928
  if (d.init.type === "Identifier" && reactive.has(d.init.name)) continue;
21913
21929
  const seen = /* @__PURE__ */ new Set();
21914
21930
  collectIdentifiers(d.init, seen);
@@ -30704,6 +30720,9 @@ function isReactiveExpression(node, tracked2) {
30704
30720
  if (!ast) return false;
30705
30721
  let reactive = false;
30706
30722
  walk4(ast, tracked2, {
30723
+ CallExpression() {
30724
+ reactive = true;
30725
+ },
30707
30726
  Identifier(n, context) {
30708
30727
  if (reactive) return;
30709
30728
  const parent = context.path.at(-1);
@@ -30831,7 +30850,7 @@ function emitComponentCall(ctx2, node, tracked2, effectsVar, parentVar, compPref
30831
30850
  ctx2.push(`return $f; })();`);
30832
30851
  propsEntries.push(`children: ${frag}`);
30833
30852
  }
30834
- if (ctx2.markerless && promptExpr && !plainTarget) {
30853
+ if (ctx2.markerless && promptExpr) {
30835
30854
  ctx2.push(`${walkerArg}.injectSkipK(${promptExpr});`);
30836
30855
  }
30837
30856
  ctx2.push(`let ${v} = undefined;`);
@@ -49528,6 +49547,34 @@ function demoteExports(code) {
49528
49547
  function chunkHasImports(headLen, mergeAborted) {
49529
49548
  return headLen > 0 || mergeAborted;
49530
49549
  }
49550
+ function runtimeImportsOf(filePath) {
49551
+ let text;
49552
+ try {
49553
+ text = readFileSync11(filePath, "utf8");
49554
+ } catch {
49555
+ return [];
49556
+ }
49557
+ const names = [];
49558
+ const pattern2 = /import\s+(type\s+)?\{([^}]*)\}\s*from\s*['"]@vesk\/runtime[^'"]*['"]/g;
49559
+ for (const match of text.matchAll(pattern2)) {
49560
+ if (match[1]) continue;
49561
+ for (const part of match[2].split(",")) {
49562
+ const cleaned = part.trim().replace(/^type\s+/, "");
49563
+ if (!cleaned) continue;
49564
+ const name = cleaned.split(/\s+as\s+/)[0].trim();
49565
+ if (name) names.push(name);
49566
+ }
49567
+ }
49568
+ return names;
49569
+ }
49570
+ function sharedRuntimeResolver(shimPath) {
49571
+ return {
49572
+ name: "vesk-shared-runtime",
49573
+ setup(build4) {
49574
+ build4.onResolve({ filter: /^@vesk\/runtime(\/.*)?$/ }, () => ({ path: shimPath }));
49575
+ }
49576
+ };
49577
+ }
49531
49578
  var ChunkImports = class {
49532
49579
  bySource = /* @__PURE__ */ new Map();
49533
49580
  aborted = false;
@@ -49625,6 +49672,20 @@ var ChunkImports = class {
49625
49672
  get reason() {
49626
49673
  return this.abortReason;
49627
49674
  }
49675
+ /** Every import source this chunk's merged block pulls in (absolute paths). */
49676
+ sources() {
49677
+ return this.aborted ? [] : [...this.bySource.keys()];
49678
+ }
49679
+ /**
49680
+ * The named bindings this chunk imports from `source`, so a shared-module
49681
+ * shim can re-export exactly what consumers use (re-exporting `*` from the
49682
+ * real module would drag it back into the chunk being built).
49683
+ */
49684
+ bindingsFor(source) {
49685
+ const group = this.bySource.get(source);
49686
+ if (!group) return { named: [], hasDefault: false };
49687
+ return { named: [...group.named.keys()].sort(), hasDefault: !!group.default };
49688
+ }
49628
49689
  /** Renders the deduped import block (empty string when there are no imports). */
49629
49690
  render() {
49630
49691
  if (this.aborted || this.bySource.size === 0) return "";
@@ -49760,6 +49821,7 @@ async function generateClientBundle(routeTree, appDir, componentMap, options) {
49760
49821
  let cachedFileHits = 0;
49761
49822
  let compiledFiles = 0;
49762
49823
  let mainFromCache = false;
49824
+ let sharedModuleBundle = "";
49763
49825
  function mustReuseWithoutStat(filePath) {
49764
49826
  return !!only && !only.has(filePath);
49765
49827
  }
@@ -49945,7 +50007,7 @@ async function generateClientBundle(routeTree, appDir, componentMap, options) {
49945
50007
  if (!chunkImports.ok) console.error("[vesk] chunk import merge aborted for", chunkName + ":", chunkImports.reason);
49946
50008
  const head = chunkImports.render();
49947
50009
  const code = (head ? head + "\n\n" : "") + chunkCode.join("\n\n");
49948
- chunkEntries.push({ name: chunkName, code, node, imports: chunkHasImports(head.length, !chunkImports.ok) });
50010
+ chunkEntries.push({ name: chunkName, code, node, importsTable: chunkImports, imports: chunkHasImports(head.length, !chunkImports.ok) });
49949
50011
  }
49950
50012
  walkSplit2(node.children || [], [..._chain, node]);
49951
50013
  }
@@ -49974,7 +50036,7 @@ async function generateClientBundle(routeTree, appDir, componentMap, options) {
49974
50036
  if (!sharedImports.ok) console.error("[vesk] chunk import merge aborted for shared.js:", sharedImports.reason);
49975
50037
  const head = sharedImports.render();
49976
50038
  const code = (head ? head + "\n\n" : "") + sharedCode.join("\n\n");
49977
- chunkEntries.push({ name: "shared.js", code, node: null, imports: chunkHasImports(head.length, !sharedImports.ok) });
50039
+ chunkEntries.push({ name: "shared.js", code, node: null, importsTable: sharedImports, imports: chunkHasImports(head.length, !sharedImports.ok) });
49978
50040
  }
49979
50041
  const chunkIIFE = (body) => `(()=>{
49980
50042
  const __components = globalThis.__components || (globalThis.__components = {});
@@ -49982,6 +50044,98 @@ const __hydrators = globalThis.__hydrators || (globalThis.__hydrators = {});
49982
50044
  ${body}
49983
50045
  })();
49984
50046
  `;
50047
+ const sharedUsage = /* @__PURE__ */ new Map();
50048
+ for (const entry of chunkEntries) {
50049
+ for (const source of new Set(entry.importsTable.sources())) {
50050
+ sharedUsage.set(source, (sharedUsage.get(source) ?? 0) + 1);
50051
+ }
50052
+ }
50053
+ const isAppLocal = (source) => !source.includes("node_modules") && source.startsWith(resolve10(appDir, ".."));
50054
+ const sharedSources = [...sharedUsage.entries()].filter(([source, uses]) => uses > 1 && isAppLocal(source)).map(([source]) => source);
50055
+ if (process.env.VESK_DEBUG_SHARED) {
50056
+ console.error("[shared] candidates:", [...sharedUsage.entries()].map(([s, n]) => `${n}x ${s}`).join("\n"));
50057
+ console.error("[shared] selected:", sharedSources.join(", "));
50058
+ }
50059
+ const chunkAlias = {};
50060
+ let sharedBundleCode = "";
50061
+ const sharedRuntimeNames = /* @__PURE__ */ new Set();
50062
+ if (sharedSources.length > 0) {
50063
+ const tmpBase = mkdtempSync(join7(resolve10(appDir, ".."), "tmp-vesk-shared-"));
50064
+ try {
50065
+ const sharedEntry = join7(tmpBase, "shared-entry.js");
50066
+ const lines = [];
50067
+ const members = [];
50068
+ sharedSources.forEach((source, i) => {
50069
+ lines.push(`import * as m${i} from ${JSON.stringify(source)};`);
50070
+ members.push(` ${JSON.stringify(source)}: m${i},`);
50071
+ });
50072
+ lines.push(`globalThis.__veskShared = { ${members.join("\n")} };`);
50073
+ writeFileSync4(sharedEntry, lines.join("\n"));
50074
+ const appTsconfig = findTsconfigPath(appDir);
50075
+ const runtimeShim = join7(tmpBase, "runtime-shim.js");
50076
+ const runtimeLines = [
50077
+ "// ONE runtime instance: the entry bundle publishes these on globalThis,",
50078
+ "// and route chunks import the same functions. A second bundled copy",
50079
+ "// would mean a second Cell class and a second currentEffect.",
50080
+ "const __r = (n) => (...args) => globalThis[n](...args);"
50081
+ ];
50082
+ for (const source of sharedSources) {
50083
+ for (const name of runtimeImportsOf(source)) sharedRuntimeNames.add(name);
50084
+ }
50085
+ for (const name of [...sharedRuntimeNames].sort()) {
50086
+ runtimeLines.push(`export const ${name} = __r(${JSON.stringify(name)});`);
50087
+ }
50088
+ writeFileSync4(runtimeShim, runtimeLines.join("\n") + "\n");
50089
+ const result = await build({
50090
+ entryPoints: [sharedEntry],
50091
+ bundle: true,
50092
+ format: "iife",
50093
+ platform: "browser",
50094
+ write: false,
50095
+ logLevel: "silent",
50096
+ loader: { ".js": "tsx" },
50097
+ legalComments: "none",
50098
+ // `@vesk/runtime` MUST stay the entry bundle's instance. Bundling a
50099
+ // second copy in here gives the app a second `Cell` class and a
50100
+ // second `currentEffect` variable: a store cell written from a click
50101
+ // notifies subscribers registered against the OTHER copy, so nothing
50102
+ // the store drives ever updates — writes land, no error, no repaint.
50103
+ plugins: [sharedRuntimeResolver(runtimeShim)],
50104
+ ...appTsconfig ? { tsconfig: appTsconfig } : {}
50105
+ });
50106
+ sharedBundleCode = result.outputFiles[0].text;
50107
+ sharedSources.forEach((source, i) => {
50108
+ const named = /* @__PURE__ */ new Set();
50109
+ let hasDefault = false;
50110
+ for (const entry of chunkEntries) {
50111
+ const bindings = entry.importsTable.bindingsFor(source);
50112
+ bindings.named.forEach((n) => named.add(n));
50113
+ hasDefault = hasDefault || bindings.hasDefault;
50114
+ }
50115
+ const shim = join7(tmpBase, `shim-${i}.js`);
50116
+ const shimLines = [`const m = globalThis.__veskShared[${JSON.stringify(source)}];`];
50117
+ if (hasDefault) shimLines.push(`export default m;`);
50118
+ for (const name of [...named].sort()) {
50119
+ shimLines.push(`export const ${name} = m[${JSON.stringify(name)}];`);
50120
+ }
50121
+ writeFileSync4(shim, shimLines.join("\n") + "\n");
50122
+ chunkAlias[source] = shim;
50123
+ });
50124
+ } catch (e) {
50125
+ console.error("[vesk] shared-module pass failed; falling back to per-chunk inlining:", e.message);
50126
+ sharedBundleCode = "";
50127
+ for (const key of Object.keys(chunkAlias)) delete chunkAlias[key];
50128
+ }
50129
+ }
50130
+ const sharedRedirectPlugin = Object.keys(chunkAlias).length > 0 ? {
50131
+ name: "vesk-shared-modules",
50132
+ setup(build4) {
50133
+ build4.onResolve({ filter: /.*/ }, (args2) => {
50134
+ const mapped = chunkAlias[args2.path];
50135
+ return mapped ? { path: mapped } : null;
50136
+ });
50137
+ }
50138
+ } : null;
49985
50139
  for (const entry of chunkEntries) {
49986
50140
  if (!entry.code.trim()) continue;
49987
50141
  let finalCode;
@@ -50010,6 +50164,13 @@ ${entry.code}
50010
50164
  // for a dependency stays in its own package; shipping every
50011
50165
  // module's header inside our own chunk adds nothing.
50012
50166
  legalComments: "none",
50167
+ // Shared app modules are NOT inlined here: the resolver below
50168
+ // redirects them to a generated shim reading
50169
+ // `globalThis.__veskShared`, so the single copy in
50170
+ // shared-modules.js serves every route chunk. (esbuild's `alias`
50171
+ // option only accepts bare package names, so this is a resolver
50172
+ // rather than a map.)
50173
+ ...sharedRedirectPlugin ? { plugins: [sharedRedirectPlugin] } : {},
50013
50174
  ...appTsconfig ? { tsconfig: appTsconfig } : {}
50014
50175
  });
50015
50176
  finalCode = result.outputFiles[0].text;
@@ -50037,13 +50198,15 @@ ${entry.code}
50037
50198
  chunks.push({ name: entry.name, code: finalCode });
50038
50199
  }
50039
50200
  annotate2(routeTree);
50040
- const mainKey = JSON.stringify(routeTree) + "|" + [...runtimeImportNames].sort().join(",") + `|${!!options?.hmr}|${!!options?.importRuntime}|${options?.routeDataCache ?? ""}`;
50201
+ sharedModuleBundle = sharedBundleCode;
50202
+ for (const name of sharedRuntimeNames) runtimeImportNames.add(name);
50203
+ const mainKey = JSON.stringify(routeTree) + "|" + [...runtimeImportNames].sort().join(",") + `|${!!options?.hmr}|${!!options?.importRuntime}|${options?.routeDataCache ?? ""}|${sharedModuleBundle.length}`;
50041
50204
  let main;
50042
50205
  if (cache2?.mainBundle && cache2.mainBundle.key === mainKey) {
50043
50206
  mainFromCache = true;
50044
50207
  main = cache2.mainBundle.code;
50045
50208
  } else {
50046
- main = await buildMainBundle(routeTree, runtimeDir, true, {}, !!options?.hmr, !!options?.importRuntime, runtimeImportNames, options?.routeDataCache);
50209
+ main = await buildMainBundle(routeTree, runtimeDir, true, {}, !!options?.hmr, !!options?.importRuntime, runtimeImportNames, options?.routeDataCache, sharedModuleBundle);
50047
50210
  if (cache2) cache2.mainBundle = { key: mainKey, code: main };
50048
50211
  }
50049
50212
  const transformedChunks = [];
@@ -50244,7 +50407,7 @@ export { ${unique.join(", ")} };
50244
50407
  function appendHmrGlobals(code) {
50245
50408
  return code + "globalThis.__vesk_hmr_eval = (code, n) => {\n if (!n || n !== globalThis.__vesk_hmr_nonce) throw new Error('vesk hmr: invalid nonce');\n return eval(code);\n};\n";
50246
50409
  }
50247
- async function buildMainBundle(routeTree, runtimeDir, codeSplit, mono, hmr, importRuntime, runtimeImportNames, routeDataCache) {
50410
+ async function buildMainBundle(routeTree, runtimeDir, codeSplit, mono, hmr, importRuntime, runtimeImportNames, routeDataCache, sharedModuleBundle = "") {
50248
50411
  const baseRuntimeImports = ["createFileRouter", "get", "set", "effect", "track", "destroy_block", "getActiveComponent", "setActiveComponent", "NavLink", "Link", "reactiveProps", "matchRoute", "ensureChunk", "toDomNode", "rerenderNode"];
50249
50412
  const allRuntimeImports = runtimeImportNames && runtimeImportNames.size > 0 ? [.../* @__PURE__ */ new Set([...baseRuntimeImports, ...runtimeImportNames])] : baseRuntimeImports;
50250
50413
  const runtimeGlobals = [
@@ -50313,7 +50476,11 @@ if (__pendChunks.length > 0 && typeof ensureChunk === 'function') {
50313
50476
  `).join("") + "globalThis.__runtime_comps = __runtime_comps;\nglobalThis.__cleanup = __cleanup;\nglobalThis.__place = __place;\n\n";
50314
50477
  const extraGlobals = [...runtimeImportNames || []].filter((n) => n && n !== "default").map((n) => `globalThis.${n} = ${n};
50315
50478
  `).join("");
50316
- const code2 = preamble + "const __components = globalThis.__components || (globalThis.__components = {});\nconst __hydrators = globalThis.__hydrators || (globalThis.__hydrators = {});\nconst __runtime_comps = __components;\n\n" + runtimeGlobals2 + extraGlobals + cleanupFn + placeFn + "globalThis.__components = __components;\n" + resolveNamesFn + updateComponentsFn + "const __routeTree = " + routeTreeJson + ";\n__resolveNames(__routeTree);\n" + pendCode + startRouterCode;
50479
+ const code2 = preamble + "const __components = globalThis.__components || (globalThis.__components = {});\nconst __hydrators = globalThis.__hydrators || (globalThis.__hydrators = {});\nconst __runtime_comps = __components;\n\n" + runtimeGlobals2 + extraGlobals + // Shared app modules run HERE: after the runtime bindings are published,
50480
+ // before anything can request a route chunk. Earlier (at the top of the
50481
+ // bundle) `globalThis.track` was still undefined, so every module-scope
50482
+ // cell in a store was created against a missing runtime.
50483
+ (sharedModuleBundle ? sharedModuleBundle + "\n" : "") + cleanupFn + placeFn + "globalThis.__components = __components;\n" + resolveNamesFn + updateComponentsFn + "const __routeTree = " + routeTreeJson + ";\n__resolveNames(__routeTree);\n" + pendCode + startRouterCode;
50317
50484
  return hmr ? appendHmrGlobals(code2) : code2;
50318
50485
  }
50319
50486
  const componentLines = mono?.componentLines || [];
@@ -52568,6 +52735,9 @@ function isReactiveExpression2(node, tracked2) {
52568
52735
  return false;
52569
52736
  let reactive = false;
52570
52737
  walk7(ast, tracked2, {
52738
+ CallExpression() {
52739
+ reactive = true;
52740
+ },
52571
52741
  Identifier(n, context) {
52572
52742
  if (reactive)
52573
52743
  return;
@@ -52713,7 +52883,7 @@ function emitComponentCall2(ctx2, node, tracked2, effectsVar, parentVar, compPre
52713
52883
  ctx2.push(`return $f; })();`);
52714
52884
  propsEntries.push(`children: ${frag}`);
52715
52885
  }
52716
- if (ctx2.markerless && promptExpr && !plainTarget) {
52886
+ if (ctx2.markerless && promptExpr) {
52717
52887
  ctx2.push(`${walkerArg}.injectSkipK(${promptExpr});`);
52718
52888
  }
52719
52889
  ctx2.push(`let ${v} = undefined;`);
@@ -55489,6 +55659,37 @@ function demoteExports2(code) {
55489
55659
  function chunkHasImports2(headLen, mergeAborted) {
55490
55660
  return headLen > 0 || mergeAborted;
55491
55661
  }
55662
+ function runtimeImportsOf2(filePath) {
55663
+ let text;
55664
+ try {
55665
+ text = readFileSync24(filePath, "utf8");
55666
+ } catch {
55667
+ return [];
55668
+ }
55669
+ const names = [];
55670
+ const pattern2 = /import\s+(type\s+)?\{([^}]*)\}\s*from\s*['"]@vesk\/runtime[^'"]*['"]/g;
55671
+ for (const match of text.matchAll(pattern2)) {
55672
+ if (match[1])
55673
+ continue;
55674
+ for (const part of match[2].split(",")) {
55675
+ const cleaned = part.trim().replace(/^type\s+/, "");
55676
+ if (!cleaned)
55677
+ continue;
55678
+ const name = cleaned.split(/\s+as\s+/)[0].trim();
55679
+ if (name)
55680
+ names.push(name);
55681
+ }
55682
+ }
55683
+ return names;
55684
+ }
55685
+ function sharedRuntimeResolver2(shimPath) {
55686
+ return {
55687
+ name: "vesk-shared-runtime",
55688
+ setup(build4) {
55689
+ build4.onResolve({ filter: /^@vesk\/runtime(\/.*)?$/ }, () => ({ path: shimPath }));
55690
+ }
55691
+ };
55692
+ }
55492
55693
  var ChunkImports2 = class {
55493
55694
  bySource = /* @__PURE__ */ new Map();
55494
55695
  aborted = false;
@@ -55596,6 +55797,21 @@ var ChunkImports2 = class {
55596
55797
  get reason() {
55597
55798
  return this.abortReason;
55598
55799
  }
55800
+ /** Every import source this chunk's merged block pulls in (absolute paths). */
55801
+ sources() {
55802
+ return this.aborted ? [] : [...this.bySource.keys()];
55803
+ }
55804
+ /**
55805
+ * The named bindings this chunk imports from `source`, so a shared-module
55806
+ * shim can re-export exactly what consumers use (re-exporting `*` from the
55807
+ * real module would drag it back into the chunk being built).
55808
+ */
55809
+ bindingsFor(source) {
55810
+ const group = this.bySource.get(source);
55811
+ if (!group)
55812
+ return { named: [], hasDefault: false };
55813
+ return { named: [...group.named.keys()].sort(), hasDefault: !!group.default };
55814
+ }
55599
55815
  /** Renders the deduped import block (empty string when there are no imports). */
55600
55816
  render() {
55601
55817
  if (this.aborted || this.bySource.size === 0)
@@ -55750,6 +55966,7 @@ async function generateClientBundle2(routeTree, appDir, componentMap, options) {
55750
55966
  let cachedFileHits = 0;
55751
55967
  let compiledFiles = 0;
55752
55968
  let mainFromCache = false;
55969
+ let sharedModuleBundle = "";
55753
55970
  function mustReuseWithoutStat(filePath) {
55754
55971
  return !!only && !only.has(filePath);
55755
55972
  }
@@ -55948,7 +56165,7 @@ async function generateClientBundle2(routeTree, appDir, componentMap, options) {
55948
56165
  console.error("[vesk] chunk import merge aborted for", chunkName + ":", chunkImports.reason);
55949
56166
  const head = chunkImports.render();
55950
56167
  const code = (head ? head + "\n\n" : "") + chunkCode.join("\n\n");
55951
- chunkEntries.push({ name: chunkName, code, node, imports: chunkHasImports2(head.length, !chunkImports.ok) });
56168
+ chunkEntries.push({ name: chunkName, code, node, importsTable: chunkImports, imports: chunkHasImports2(head.length, !chunkImports.ok) });
55952
56169
  }
55953
56170
  walkSplit2(node.children || [], [..._chain, node]);
55954
56171
  }
@@ -55978,7 +56195,7 @@ async function generateClientBundle2(routeTree, appDir, componentMap, options) {
55978
56195
  console.error("[vesk] chunk import merge aborted for shared.js:", sharedImports.reason);
55979
56196
  const head = sharedImports.render();
55980
56197
  const code = (head ? head + "\n\n" : "") + sharedCode.join("\n\n");
55981
- chunkEntries.push({ name: "shared.js", code, node: null, imports: chunkHasImports2(head.length, !sharedImports.ok) });
56198
+ chunkEntries.push({ name: "shared.js", code, node: null, importsTable: sharedImports, imports: chunkHasImports2(head.length, !sharedImports.ok) });
55982
56199
  }
55983
56200
  const chunkIIFE = (body) => `(()=>{
55984
56201
  const __components = globalThis.__components || (globalThis.__components = {});
@@ -55986,6 +56203,101 @@ const __hydrators = globalThis.__hydrators || (globalThis.__hydrators = {});
55986
56203
  ${body}
55987
56204
  })();
55988
56205
  `;
56206
+ const sharedUsage = /* @__PURE__ */ new Map();
56207
+ for (const entry of chunkEntries) {
56208
+ for (const source of new Set(entry.importsTable.sources())) {
56209
+ sharedUsage.set(source, (sharedUsage.get(source) ?? 0) + 1);
56210
+ }
56211
+ }
56212
+ const isAppLocal = (source) => !source.includes("node_modules") && source.startsWith(resolve24(appDir, ".."));
56213
+ const sharedSources = [...sharedUsage.entries()].filter(([source, uses]) => uses > 1 && isAppLocal(source)).map(([source]) => source);
56214
+ if (process.env.VESK_DEBUG_SHARED) {
56215
+ console.error("[shared] candidates:", [...sharedUsage.entries()].map(([s, n]) => `${n}x ${s}`).join("\n"));
56216
+ console.error("[shared] selected:", sharedSources.join(", "));
56217
+ }
56218
+ const chunkAlias = {};
56219
+ let sharedBundleCode = "";
56220
+ const sharedRuntimeNames = /* @__PURE__ */ new Set();
56221
+ if (sharedSources.length > 0) {
56222
+ const tmpBase = mkdtempSync2(join20(resolve24(appDir, ".."), "tmp-vesk-shared-"));
56223
+ try {
56224
+ const sharedEntry = join20(tmpBase, "shared-entry.js");
56225
+ const lines = [];
56226
+ const members = [];
56227
+ sharedSources.forEach((source, i) => {
56228
+ lines.push(`import * as m${i} from ${JSON.stringify(source)};`);
56229
+ members.push(` ${JSON.stringify(source)}: m${i},`);
56230
+ });
56231
+ lines.push(`globalThis.__veskShared = { ${members.join("\n")} };`);
56232
+ writeFileSync13(sharedEntry, lines.join("\n"));
56233
+ const appTsconfig = findTsconfigPath(appDir);
56234
+ const runtimeShim = join20(tmpBase, "runtime-shim.js");
56235
+ const runtimeLines = [
56236
+ "// ONE runtime instance: the entry bundle publishes these on globalThis,",
56237
+ "// and route chunks import the same functions. A second bundled copy",
56238
+ "// would mean a second Cell class and a second currentEffect.",
56239
+ "const __r = (n) => (...args) => globalThis[n](...args);"
56240
+ ];
56241
+ for (const source of sharedSources) {
56242
+ for (const name of runtimeImportsOf2(source))
56243
+ sharedRuntimeNames.add(name);
56244
+ }
56245
+ for (const name of [...sharedRuntimeNames].sort()) {
56246
+ runtimeLines.push(`export const ${name} = __r(${JSON.stringify(name)});`);
56247
+ }
56248
+ writeFileSync13(runtimeShim, runtimeLines.join("\n") + "\n");
56249
+ const result = await build3({
56250
+ entryPoints: [sharedEntry],
56251
+ bundle: true,
56252
+ format: "iife",
56253
+ platform: "browser",
56254
+ write: false,
56255
+ logLevel: "silent",
56256
+ loader: { ".js": "tsx" },
56257
+ legalComments: "none",
56258
+ // `@vesk/runtime` MUST stay the entry bundle's instance. Bundling a
56259
+ // second copy in here gives the app a second `Cell` class and a
56260
+ // second `currentEffect` variable: a store cell written from a click
56261
+ // notifies subscribers registered against the OTHER copy, so nothing
56262
+ // the store drives ever updates — writes land, no error, no repaint.
56263
+ plugins: [sharedRuntimeResolver2(runtimeShim)],
56264
+ ...appTsconfig ? { tsconfig: appTsconfig } : {}
56265
+ });
56266
+ sharedBundleCode = result.outputFiles[0].text;
56267
+ sharedSources.forEach((source, i) => {
56268
+ const named = /* @__PURE__ */ new Set();
56269
+ let hasDefault = false;
56270
+ for (const entry of chunkEntries) {
56271
+ const bindings = entry.importsTable.bindingsFor(source);
56272
+ bindings.named.forEach((n) => named.add(n));
56273
+ hasDefault = hasDefault || bindings.hasDefault;
56274
+ }
56275
+ const shim = join20(tmpBase, `shim-${i}.js`);
56276
+ const shimLines = [`const m = globalThis.__veskShared[${JSON.stringify(source)}];`];
56277
+ if (hasDefault)
56278
+ shimLines.push(`export default m;`);
56279
+ for (const name of [...named].sort()) {
56280
+ shimLines.push(`export const ${name} = m[${JSON.stringify(name)}];`);
56281
+ }
56282
+ writeFileSync13(shim, shimLines.join("\n") + "\n");
56283
+ chunkAlias[source] = shim;
56284
+ });
56285
+ } catch (e) {
56286
+ console.error("[vesk] shared-module pass failed; falling back to per-chunk inlining:", e.message);
56287
+ sharedBundleCode = "";
56288
+ for (const key of Object.keys(chunkAlias))
56289
+ delete chunkAlias[key];
56290
+ }
56291
+ }
56292
+ const sharedRedirectPlugin = Object.keys(chunkAlias).length > 0 ? {
56293
+ name: "vesk-shared-modules",
56294
+ setup(build4) {
56295
+ build4.onResolve({ filter: /.*/ }, (args2) => {
56296
+ const mapped = chunkAlias[args2.path];
56297
+ return mapped ? { path: mapped } : null;
56298
+ });
56299
+ }
56300
+ } : null;
55989
56301
  for (const entry of chunkEntries) {
55990
56302
  if (!entry.code.trim())
55991
56303
  continue;
@@ -56015,6 +56327,13 @@ ${entry.code}
56015
56327
  // for a dependency stays in its own package; shipping every
56016
56328
  // module's header inside our own chunk adds nothing.
56017
56329
  legalComments: "none",
56330
+ // Shared app modules are NOT inlined here: the resolver below
56331
+ // redirects them to a generated shim reading
56332
+ // `globalThis.__veskShared`, so the single copy in
56333
+ // shared-modules.js serves every route chunk. (esbuild's `alias`
56334
+ // option only accepts bare package names, so this is a resolver
56335
+ // rather than a map.)
56336
+ ...sharedRedirectPlugin ? { plugins: [sharedRedirectPlugin] } : {},
56018
56337
  ...appTsconfig ? { tsconfig: appTsconfig } : {}
56019
56338
  });
56020
56339
  finalCode = result.outputFiles[0].text;
@@ -56042,13 +56361,16 @@ ${entry.code}
56042
56361
  chunks.push({ name: entry.name, code: finalCode });
56043
56362
  }
56044
56363
  annotate2(routeTree);
56045
- const mainKey = JSON.stringify(routeTree) + "|" + [...runtimeImportNames].sort().join(",") + `|${!!options?.hmr}|${!!options?.importRuntime}|${options?.routeDataCache ?? ""}`;
56364
+ sharedModuleBundle = sharedBundleCode;
56365
+ for (const name of sharedRuntimeNames)
56366
+ runtimeImportNames.add(name);
56367
+ const mainKey = JSON.stringify(routeTree) + "|" + [...runtimeImportNames].sort().join(",") + `|${!!options?.hmr}|${!!options?.importRuntime}|${options?.routeDataCache ?? ""}|${sharedModuleBundle.length}`;
56046
56368
  let main;
56047
56369
  if (cache2?.mainBundle && cache2.mainBundle.key === mainKey) {
56048
56370
  mainFromCache = true;
56049
56371
  main = cache2.mainBundle.code;
56050
56372
  } else {
56051
- main = await buildMainBundle2(routeTree, runtimeDir, true, {}, !!options?.hmr, !!options?.importRuntime, runtimeImportNames, options?.routeDataCache);
56373
+ main = await buildMainBundle2(routeTree, runtimeDir, true, {}, !!options?.hmr, !!options?.importRuntime, runtimeImportNames, options?.routeDataCache, sharedModuleBundle);
56052
56374
  if (cache2)
56053
56375
  cache2.mainBundle = { key: mainKey, code: main };
56054
56376
  }
@@ -56265,7 +56587,7 @@ export { ${unique.join(", ")} };
56265
56587
  function appendHmrGlobals2(code) {
56266
56588
  return code + "globalThis.__vesk_hmr_eval = (code, n) => {\n if (!n || n !== globalThis.__vesk_hmr_nonce) throw new Error('vesk hmr: invalid nonce');\n return eval(code);\n};\n";
56267
56589
  }
56268
- async function buildMainBundle2(routeTree, runtimeDir, codeSplit, mono, hmr, importRuntime, runtimeImportNames, routeDataCache) {
56590
+ async function buildMainBundle2(routeTree, runtimeDir, codeSplit, mono, hmr, importRuntime, runtimeImportNames, routeDataCache, sharedModuleBundle = "") {
56269
56591
  const baseRuntimeImports = ["createFileRouter", "get", "set", "effect", "track", "destroy_block", "getActiveComponent", "setActiveComponent", "NavLink", "Link", "reactiveProps", "matchRoute", "ensureChunk", "toDomNode", "rerenderNode"];
56270
56592
  const allRuntimeImports = runtimeImportNames && runtimeImportNames.size > 0 ? [.../* @__PURE__ */ new Set([...baseRuntimeImports, ...runtimeImportNames])] : baseRuntimeImports;
56271
56593
  const runtimeGlobals = [
@@ -56334,7 +56656,11 @@ if (__pendChunks.length > 0 && typeof ensureChunk === 'function') {
56334
56656
  `).join("") + "globalThis.__runtime_comps = __runtime_comps;\nglobalThis.__cleanup = __cleanup;\nglobalThis.__place = __place;\n\n";
56335
56657
  const extraGlobals = [...runtimeImportNames || []].filter((n) => n && n !== "default").map((n) => `globalThis.${n} = ${n};
56336
56658
  `).join("");
56337
- const code2 = preamble + "const __components = globalThis.__components || (globalThis.__components = {});\nconst __hydrators = globalThis.__hydrators || (globalThis.__hydrators = {});\nconst __runtime_comps = __components;\n\n" + runtimeGlobals2 + extraGlobals + cleanupFn + placeFn + "globalThis.__components = __components;\n" + resolveNamesFn + updateComponentsFn + "const __routeTree = " + routeTreeJson + ";\n__resolveNames(__routeTree);\n" + pendCode + startRouterCode;
56659
+ const code2 = preamble + "const __components = globalThis.__components || (globalThis.__components = {});\nconst __hydrators = globalThis.__hydrators || (globalThis.__hydrators = {});\nconst __runtime_comps = __components;\n\n" + runtimeGlobals2 + extraGlobals + // Shared app modules run HERE: after the runtime bindings are published,
56660
+ // before anything can request a route chunk. Earlier (at the top of the
56661
+ // bundle) `globalThis.track` was still undefined, so every module-scope
56662
+ // cell in a store was created against a missing runtime.
56663
+ (sharedModuleBundle ? sharedModuleBundle + "\n" : "") + cleanupFn + placeFn + "globalThis.__components = __components;\n" + resolveNamesFn + updateComponentsFn + "const __routeTree = " + routeTreeJson + ";\n__resolveNames(__routeTree);\n" + pendCode + startRouterCode;
56338
56664
  return hmr ? appendHmrGlobals2(code2) : code2;
56339
56665
  }
56340
56666
  const componentLines = mono?.componentLines || [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vesk/vesk-cli",
3
- "version": "0.2.58",
3
+ "version": "0.2.59",
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.58",
12
- "@vesk/compiler": "^0.2.58",
13
- "@vesk/plugin-tailwind": "^0.2.58",
14
- "@vesk/runtime": "^0.2.58",
11
+ "@vesk/adapter": "^0.2.59",
12
+ "@vesk/compiler": "^0.2.59",
13
+ "@vesk/plugin-tailwind": "^0.2.59",
14
+ "@vesk/runtime": "^0.2.59",
15
15
  "esrap": "^2.3.0",
16
16
  "typescript": "^5.8.0",
17
17
  "ws": "^8.18.0",