@vesk/adapter 0.2.92 → 0.2.93

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.
@@ -132,6 +132,10 @@ export declare function buildRuntimeCode(runtimeDir: string): string;
132
132
  /**
133
133
  * Names the client runtime actually exports, so the tree-shaken bundle only
134
134
  * emits the modules reachable from the used set.
135
+ *
136
+ * Parses index-client.js with the AST parser instead of regex so that direct
137
+ * declarations (export function locals() {}, export const x = 1, export class
138
+ * Foo {}) are captured alongside re-exports (export { X } from '...').
135
139
  */
136
140
  export declare function runtimeExportNames(runtimeDir: string): Set<string>;
137
141
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"client-bundle.d.ts","sourceRoot":"","sources":["../src/client-bundle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client-bundle.d.ts","sourceRoot":"","sources":["../src/client-bundle.ts"],"names":[],"mappings":"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"}
@@ -137,32 +137,41 @@ function isAnyImport(node) {
137
137
  }
138
138
  /**
139
139
  * Generated client-preamble nodes the bundle must not re-emit: the local
140
- * `const __components = {}` registry plus the `__cleanup` / `__place` helpers —
141
- * the chunk IIFEs already define their own `__components`, so a second
142
- * declaration in the same scope would be a syntax error.
140
+ * `const __components = {}` / `const __hydrators = {}` registries plus the
141
+ * `__cleanup` / `__place` helpers — the chunk IIFEs already define their own
142
+ * registries, so a second declaration in the same scope would shadow the live
143
+ * map (registrations would land on a detached object).
143
144
  */
144
145
  function isRuntimePreamble(node) {
145
146
  if (node.type === 'FunctionDeclaration' && (node.id?.name === '__cleanup' || node.id?.name === '__place')) {
146
147
  return true;
147
148
  }
148
149
  if (node.type === 'VariableDeclaration') {
149
- return node.declarations?.[0]?.id?.name === '__components';
150
+ const name = node.declarations?.[0]?.id?.name;
151
+ return name === '__components' || name === '__hydrators';
150
152
  }
151
153
  return false;
152
154
  }
153
155
  /**
154
156
  * `export default __components[<name>];` / `export const <name> = __components[<name>];`
155
- * aliases emitted by codegen. The bundle registers components on `__components`
156
- * directly, so these module-level re-exports must go.
157
+ * aliases emitted by codegen. Hydrate-mode codegen emits the same alias against
158
+ * `__hydrators`. The bundle registers components on those maps directly, so
159
+ * these module-level re-exports must go — two surviving `export default`s in
160
+ * one chunk are a `SyntaxError: Identifier '.default' has already been
161
+ * declared` that kills every page script.
157
162
  */
158
163
  function isComponentExport(node) {
164
+ const isRegistry = (ref) => {
165
+ const n = ref;
166
+ return !!n && n.type === 'MemberExpression' && n.object?.type === 'Identifier'
167
+ && (n.object.name === '__components' || n.object.name === '__hydrators');
168
+ };
159
169
  if (node.type === 'ExportDefaultDeclaration') {
160
- const d = node.declaration;
161
- return !!d && d.type === 'MemberExpression' && d.object?.type === 'Identifier' && d.object.name === '__components';
170
+ return isRegistry(node.declaration);
162
171
  }
163
172
  if (node.type === 'ExportNamedDeclaration') {
164
173
  const init = node.declaration?.type === 'VariableDeclaration' ? node.declaration.declarations?.[0]?.init : undefined;
165
- return !!init && init.type === 'MemberExpression' && init.object?.type === 'Identifier' && init.object.name === '__components';
174
+ return isRegistry(init);
166
175
  }
167
176
  return false;
168
177
  }
@@ -582,7 +591,7 @@ function removeRanges(code, ranges) {
582
591
  */
583
592
  function fallbackReplay(cached, filePath, imports) {
584
593
  const comp = cached.compCode ? imports.add(cached.compCode, filePath).replace(/^\n+/, '').replace(/\n+$/, '') : '';
585
- const hyd = cached.hydCode ? imports.add(cached.hydCode, filePath).replace(/__components/g, '__hydrators').replace(/^\n+/, '').replace(/\n+$/, '') : '';
594
+ const hyd = cached.hydCode ? imports.add(cached.hydCode, filePath).replace(/^\n+/, '').replace(/\n+$/, '') : '';
586
595
  return [comp, hyd].filter(Boolean).join('\n');
587
596
  }
588
597
  /**
@@ -929,7 +938,7 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
929
938
  const compSpecs = [];
930
939
  const hydSpecs = [];
931
940
  const compCode = compWithImports ? imports.add(compWithImports, filePath, compSpecs).replace(/^\n+/, '').replace(/\n+$/, '') : '';
932
- const hydCode = hydWithImports ? imports.add(hydWithImports, filePath, hydSpecs).replace(/__components/g, '__hydrators').replace(/^\n+/, '').replace(/\n+$/, '') : '';
941
+ const hydCode = hydWithImports ? imports.add(hydWithImports, filePath, hydSpecs).replace(/^\n+/, '').replace(/\n+$/, '') : '';
933
942
  if (rawComp)
934
943
  collectRuntimeImports(rawComp);
935
944
  if (rawHyd)
@@ -1236,7 +1245,7 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
1236
1245
  const shim = join(tmpBase, `shim-${i}.js`);
1237
1246
  const shimLines = [`const m = globalThis.__veskShared[${JSON.stringify(file)}];`];
1238
1247
  if (hasDefault)
1239
- shimLines.push(`export default m;`);
1248
+ shimLines.push(`export default m["default"];`);
1240
1249
  for (const name of [...named].sort()) {
1241
1250
  shimLines.push(`export const ${name} = m[${JSON.stringify(name)}];`);
1242
1251
  }
@@ -1304,30 +1313,53 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
1304
1313
  // Pass the app tsconfig so esbuild resolves tsconfig `paths` aliases
1305
1314
  // (`@/x`, `@app/x`) inside transitively bundled modules too.
1306
1315
  const appTsconfig = findTsconfigPath(appDir);
1307
- const result = await build({
1308
- entryPoints: [tmpFile],
1309
- bundle: true,
1310
- format: 'iife',
1311
- platform: 'browser',
1312
- write: false,
1313
- logLevel: 'silent',
1314
- loader: { '.js': 'tsx' },
1315
- // Bundled dependencies (lucide-vesk above all) carry a license
1316
- // banner per module, and a barrel import pulls in hundreds of
1317
- // them. Those banners were ~339 KB of a 350 KB chunk, which is
1318
- // what kept a barrel import from being usable at all. Attribution
1319
- // for a dependency stays in its own package; shipping every
1320
- // module's header inside our own chunk adds nothing.
1321
- legalComments: 'none',
1322
- // Shared app modules are NOT inlined here: the resolver below
1323
- // redirects them to a generated shim reading
1324
- // `globalThis.__veskShared`, so the single copy in
1325
- // shared-modules.js serves every route chunk. (esbuild's `alias`
1326
- // option only accepts bare package names, so this is a resolver
1327
- // rather than a map.)
1328
- ...(sharedRedirectPlugin ? { plugins: [sharedRedirectPlugin, serverOnlyGuard] } : { plugins: [serverOnlyGuard] }),
1329
- ...(appTsconfig ? { tsconfig: appTsconfig } : {}),
1330
- });
1316
+ // Build an alias map so esbuild can resolve bare specifiers
1317
+ // (e.g. "vesk-qr") from the app's node_modules. The entry code lives
1318
+ // in a temp directory, so esbuild's default resolver (walking up from
1319
+ // the entry file) can't find app-level node_modules. resolveDir
1320
+ // doesn't exist in this esbuild version, so we alias each bare
1321
+ // specifier to its resolved absolute path.
1322
+ const alias = {};
1323
+ for (const source of entry.importsTable.sources()) {
1324
+ if (source.startsWith('/') || source.startsWith('./') || source.startsWith('../'))
1325
+ continue;
1326
+ let resolved = resolveSsrModule(source, appDir);
1327
+ if (!resolved)
1328
+ resolved = resolveImportPath(source, appDir);
1329
+ if (resolved && !resolved.startsWith('\0builtin:')) {
1330
+ alias[source] = resolved;
1331
+ }
1332
+ }
1333
+ const result = await Promise.race([
1334
+ build({
1335
+ entryPoints: [tmpFile],
1336
+ bundle: true,
1337
+ format: 'iife',
1338
+ platform: 'browser',
1339
+ write: false,
1340
+ logLevel: 'silent',
1341
+ loader: { '.js': 'tsx' },
1342
+ // Bundled dependencies (lucide-vesk above all) carry a license
1343
+ // banner per module, and a barrel import pulls in hundreds of
1344
+ // them. Those banners were ~339 KB of a 350 KB chunk, which is
1345
+ // what kept a barrel import from being usable at all. Attribution
1346
+ // for a dependency stays in its own package; shipping every
1347
+ // module's header inside our own chunk adds nothing.
1348
+ legalComments: 'none',
1349
+ // Shared app modules are NOT inlined here: the resolver below
1350
+ // redirects them to a generated shim reading
1351
+ // `globalThis.__veskShared`, so the single copy in
1352
+ // shared-modules.js serves every route chunk. (esbuild's `alias`
1353
+ // option only accepts bare package names, so this is a resolver
1354
+ // rather than a map.)
1355
+ ...(sharedRedirectPlugin ? { plugins: [sharedRedirectPlugin, serverOnlyGuard] } : { plugins: [serverOnlyGuard] }),
1356
+ ...(appTsconfig ? { tsconfig: appTsconfig } : {}),
1357
+ // Alias bare specifiers to their resolved absolute paths so
1358
+ // esbuild can find them from the temp chunk dir.
1359
+ ...(Object.keys(alias).length ? { alias } : {}),
1360
+ }),
1361
+ new Promise((_, reject) => setTimeout(() => reject(new Error('chunk esbuild timeout')), 60000)),
1362
+ ]);
1331
1363
  finalCode = result.outputFiles[0].text;
1332
1364
  }
1333
1365
  catch (e) {
@@ -1337,22 +1369,22 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
1337
1369
  return x.text + at;
1338
1370
  }).join(' | ') || err?.message || String(e);
1339
1371
  console.error('[vesk] chunk bundle failed', entry.name, detail);
1340
- // Under --strict this must fail the build. The fallback below is a
1341
- // DEVELOPMENT convenience only: it strips the unresolvable import
1342
- // and leaves every name it bound dangling, so the chunk parses, the
1343
- // build exits 0, and the page dies at hydration with `X is not
1344
- // defined` — far from the import that caused it. A wrong-case deep
1345
- // import (`lucide-vesk/icons/terminal`) hit exactly this.
1346
- if (options?.strict) {
1347
- throw new Error(`[vesk] chunk bundle failed: ${entry.name} — ${detail}`);
1372
+ // A module-scope server-only use is diagnosed here rather than after
1373
+ // the try: the bundler itself throws when the transitive TCP driver
1374
+ // (pg → node:net) can't be browser-bundled, which would otherwise
1375
+ // shadow the real, actionable problem.
1376
+ if (eagerHits.length > 0) {
1377
+ const eagerDetail = eagerHits
1378
+ .map(h => `${h.importer} uses ${h.bindings.map(b => `"${b}"`).join(', ')} from "${h.specifier}" at module scope`)
1379
+ .join('; ');
1380
+ console.error('[vesk] server-only package used at module scope in client code', entry.name + ':', eagerDetail);
1381
+ throw new Error(`[vesk] ${entry.name}: ${eagerDetail}.\n` +
1382
+ 'A module-scope statement runs the moment the browser evaluates the chunk, so a ' +
1383
+ 'server-only package cannot be substituted there. Move that work into an API route ' +
1384
+ '(app/api/**/route.ts) or a server action — if the binding is only used from inside ' +
1385
+ 'a function, this build already handles it.');
1348
1386
  }
1349
- // Fall back to the historical behavior: strip every import so the
1350
- // classic script at least parses. Bindings imported from native-only
1351
- // `@vesk/*` libraries get throwing stubs (a readable degradation
1352
- // instead of a baffling `X is not defined`); every other stripped
1353
- // name stays dangling, which is the pre-existing failure mode.
1354
- const stripped = demoteExports(removeCompiledNodes(entry.code, isAnyImport));
1355
- finalCode = chunkIIFE(nativeLibraryImportStubs(entry.code) + stripped);
1387
+ throw new Error(`[vesk] chunk bundle failed: ${entry.name} — ${detail}`);
1356
1388
  }
1357
1389
  finally {
1358
1390
  try {
@@ -1360,25 +1392,6 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
1360
1392
  }
1361
1393
  catch { }
1362
1394
  }
1363
- // Checked OUTSIDE the try above, deliberately. A TCP driver cannot be
1364
- // stubbed into a browser bundle — a page that looked fine and failed on
1365
- // its first query would be worse than a build error — and the catch
1366
- // block degrades every failure into "strip the imports and carry on",
1367
- // which is exactly the wrong outcome for this one.
1368
- if (eagerHits.length > 0) {
1369
- // A stub would throw while the chunk is still evaluating, so the page
1370
- // would die before a component existed. That is the worst place to
1371
- // learn about it: refuse the build instead.
1372
- const detail = eagerHits
1373
- .map(h => `${h.importer} uses ${h.bindings.map(b => `"${b}"`).join(', ')} from "${h.specifier}" at module scope`)
1374
- .join('; ');
1375
- console.error('[vesk] server-only package used at module scope in client code', entry.name + ':', detail);
1376
- throw new Error(`[vesk] ${entry.name}: ${detail}.\n` +
1377
- 'A module-scope statement runs the moment the browser evaluates the chunk, so a ' +
1378
- 'server-only package cannot be substituted there. Move that work into an API route ' +
1379
- '(app/api/**/route.ts) or a server action — if the binding is only used from inside ' +
1380
- 'a function, this build already handles it.');
1381
- }
1382
1395
  if (driverHits.length > 0) {
1383
1396
  const detail = [...new Set(driverHits.map(h => `${h.specifier} (from ${h.importer})`))].join(', ');
1384
1397
  console.error('[vesk] chunk bundle contains a TCP-only driver', entry.name + ':', detail);
@@ -1494,6 +1507,13 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
1494
1507
  let hydratorLines = [];
1495
1508
  let aliasLines = [];
1496
1509
  let hydratorAliasLines = [];
1510
+ // Mono builds bundle every file into one module scope. External-package
1511
+ // imports (e.g. vesk-qr) are not stripped by stripVskImports/stripRuntimeImport,
1512
+ // so multiple files importing the same package would emit duplicate
1513
+ // `import { X } from "pkg"` declarations — a SyntaxError. Fold them into
1514
+ // one deduped import block instead (mirrors what ChunkImports does for
1515
+ // code-split chunks).
1516
+ const monoImports = new ChunkImports();
1497
1517
  function compileFileMono(filePath, resolvedName) {
1498
1518
  if (seen.has(filePath))
1499
1519
  return;
@@ -1512,8 +1532,15 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
1512
1532
  if (hydCode)
1513
1533
  collectRuntimeImports(hydCode);
1514
1534
  // Comp and hyd share the file's top-level bindings — scope together.
1515
- const strippedComp = compCode ? stripExports(stripVskImports(stripRuntimeImport(compCode))).replace(/^\n+/, '').replace(/\n+$/, '') : '';
1516
- const strippedHyd = hydCode ? stripExports(stripVskImports(stripRuntimeImport(hydCode))).replace(/__components/g, '__hydrators').replace(/^\n+/, '').replace(/\n+$/, '') : '';
1535
+ let strippedComp = compCode ? stripExports(stripVskImports(stripRuntimeImport(compCode))).replace(/^\n+/, '').replace(/\n+$/, '') : '';
1536
+ let strippedHyd = hydCode ? stripExports(stripVskImports(stripRuntimeImport(hydCode))).replace(/^\n+/, '').replace(/\n+$/, '') : '';
1537
+ // Fold remaining external-package imports into the shared dedup set and
1538
+ // strip them from each file's contribution, so the same package imported
1539
+ // by multiple files only emits one `import` statement.
1540
+ if (monoImports.ok) {
1541
+ strippedComp = strippedComp ? monoImports.add(strippedComp, filePath).replace(/^\n+/, '').replace(/\n+$/, '') : '';
1542
+ strippedHyd = strippedHyd ? monoImports.add(strippedHyd, filePath).replace(/^\n+/, '').replace(/\n+$/, '') : '';
1543
+ }
1517
1544
  const fileCode = [strippedComp, strippedHyd].filter(Boolean).join('\n');
1518
1545
  if (fileCode.trim())
1519
1546
  componentLines.push(scopeFileContribution(fileCode));
@@ -1554,9 +1581,73 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
1554
1581
  for (const [compName, compPath] of compMap) {
1555
1582
  compileFileMono(compPath, compName);
1556
1583
  }
1557
- const main = await buildMainBundle(routeTree, runtimeDir, false, {
1584
+ // Emit the deduplicated external-package import block at the top of the
1585
+ // bundle so every file can reference those bindings without duplicate
1586
+ // declarations. If the import merge aborted (conflicting bindings), log a
1587
+ // warning and leave imports in place (best-effort, matches historical behaviour).
1588
+ if (monoImports.ok) {
1589
+ const head = monoImports.render();
1590
+ if (head)
1591
+ componentLines.unshift(head);
1592
+ }
1593
+ else {
1594
+ console.error('[vesk] mono import merge aborted:', monoImports.reason);
1595
+ }
1596
+ let main = await buildMainBundle(routeTree, runtimeDir, false, {
1558
1597
  componentLines, hydratorLines, aliasLines, hydratorAliasLines,
1559
1598
  }, !!options?.hmr, !!options?.importRuntime, runtimeImportNames, options?.routeDataCache);
1599
+ // Bundle the monolithic output through esbuild so bare external-package
1600
+ // imports (e.g. vesk-qr) are inlined instead of shipped as filesystem
1601
+ // paths the browser cannot load. Without this, client.js contains
1602
+ // `import ... from "/absolute/path/to/node_modules/…"` — the dev server
1603
+ // answers that with its SPA fallback (text/html), the module script dies,
1604
+ // the router never starts and hydration never completes.
1605
+ const appTsconfig = findTsconfigPath(appDir);
1606
+ const tmpBase = mkdtempSync(join(resolve(appDir, '..'), 'tmp-vesk-mono-'));
1607
+ try {
1608
+ const tmpFile = join(tmpBase, 'entry.js');
1609
+ writeFileSync(tmpFile, main, 'utf-8');
1610
+ const result = await build({
1611
+ entryPoints: [tmpFile],
1612
+ bundle: true,
1613
+ format: 'iife',
1614
+ platform: 'browser',
1615
+ write: false,
1616
+ logLevel: 'silent',
1617
+ loader: { '.js': 'tsx' },
1618
+ legalComments: 'none',
1619
+ ...(appTsconfig ? { tsconfig: appTsconfig } : {}),
1620
+ // Stub optional CJS deps (qrcode-generator via vesk-qr, canvas,
1621
+ // @zxing/*) so esbuild never bundles their CJS source into the client
1622
+ // bundle — that would emit a `__require` shim that throws in the
1623
+ // browser. vesk-qr now lazy-loads them via dynamic import; stubbing
1624
+ // keeps the bundle valid ESM without shipping dead code.
1625
+ plugins: [
1626
+ {
1627
+ name: 'stub-browser-packages',
1628
+ setup(b) {
1629
+ b.onResolve({ filter: /^(qrcode-generator|canvas|@zxing\/(browser|node))(\/|$)/ }, (args) => ({ path: args.path, namespace: 'browser-stub' }));
1630
+ b.onLoad({ filter: /.*/, namespace: 'browser-stub' }, () => ({
1631
+ contents: 'export {}\n',
1632
+ loader: 'js',
1633
+ }));
1634
+ },
1635
+ },
1636
+ ],
1637
+ });
1638
+ main = result.outputFiles[0].text;
1639
+ }
1640
+ catch (e) {
1641
+ const detail = e?.errors?.map((x) => x.text).join(' | ') || e.message || String(e);
1642
+ console.error('[vesk] client bundle failed', detail);
1643
+ throw new Error(`[vesk] client bundle failed — ${detail}`);
1644
+ }
1645
+ finally {
1646
+ try {
1647
+ rmSync(tmpBase, { recursive: true, force: true });
1648
+ }
1649
+ catch { }
1650
+ }
1560
1651
  // Mono (non-codeSplit) builds are the production path — no incremental cache.
1561
1652
  componentOwners.report('client bundle');
1562
1653
  return { main: await applyTransformPlugins(main, 'client.js'), chunks: [] };
@@ -1600,15 +1691,46 @@ export function buildRuntimeCode(runtimeDir) {
1600
1691
  /**
1601
1692
  * Names the client runtime actually exports, so the tree-shaken bundle only
1602
1693
  * emits the modules reachable from the used set.
1694
+ *
1695
+ * Parses index-client.js with the AST parser instead of regex so that direct
1696
+ * declarations (export function locals() {}, export const x = 1, export class
1697
+ * Foo {}) are captured alongside re-exports (export { X } from '...').
1603
1698
  */
1604
1699
  export function runtimeExportNames(runtimeDir) {
1605
1700
  const indexSrc = readFileSync(join(runtimeDir, 'index-client.js'), 'utf-8');
1606
1701
  const names = new Set();
1607
- for (const m of indexSrc.matchAll(/export\s*\{([^}]+)\}\s*from/g)) {
1608
- for (const raw of m[1].split(',')) {
1609
- const n = raw.trim().split(/\s+as\s+/).pop().trim();
1610
- if (n)
1611
- names.add(n);
1702
+ const ast = parse(indexSrc, { filename: join(runtimeDir, 'index-client.js') });
1703
+ for (const node of ast.body) {
1704
+ const n = node;
1705
+ if (n.type === 'ExportNamedDeclaration') {
1706
+ if (n.declaration) {
1707
+ // Direct declaration: export function foo() {}, export const x = 1,
1708
+ // export class Bar {}
1709
+ const decl = n.declaration;
1710
+ if (decl.type === 'FunctionDeclaration' && decl.id) {
1711
+ names.add(String(decl.id.name));
1712
+ }
1713
+ else if (decl.type === 'ClassDeclaration' && decl.id) {
1714
+ names.add(String(decl.id.name));
1715
+ }
1716
+ else if (decl.type === 'VariableDeclaration') {
1717
+ for (const d of decl.declarations) {
1718
+ if (d.id.type === 'Identifier') {
1719
+ names.add(String(d.id.name));
1720
+ }
1721
+ }
1722
+ }
1723
+ }
1724
+ else if (n.source) {
1725
+ // Re-export: export { X } from '...' or export { X as Y } from '...'
1726
+ for (const spec of n.specifiers) {
1727
+ if (spec.type === 'ExportSpecifier') {
1728
+ const exported = spec.exported.name;
1729
+ if (exported)
1730
+ names.add(String(exported));
1731
+ }
1732
+ }
1733
+ }
1612
1734
  }
1613
1735
  }
1614
1736
  return names;
@@ -1700,25 +1822,28 @@ async function bundleRuntimeNames(runtimeDir, names, strict = false) {
1700
1822
  }
1701
1823
  catch { /* not present yet */ }
1702
1824
  writeFileSync(entry, `export { ${unique.join(', ')} } from ${JSON.stringify(indexClient)};\n`);
1703
- const result = await build({
1704
- entryPoints: [esbuildPath(entry)],
1705
- bundle: true,
1706
- format: 'iife',
1707
- globalName: '__veskRuntime',
1708
- platform: 'browser',
1709
- target: ['es2022'],
1710
- treeShaking: true,
1711
- // Do not let esbuild rename identifiers here. This bundle is one large
1712
- // IIFE built from many runtime modules, and the renaming pass of some
1713
- // esbuild versions (observed on 0.25.12) emits a free reference to an
1714
- // undefined minified name (ReferenceError: m is not defined) that kills
1715
- // client hydration for the whole app. Whitespace-only output keeps the
1716
- // runtime deterministic at a modest size cost; tree-shaking still prunes
1717
- // unreachable modules, so names coming out of the IIFE are untouched.
1718
- minify: false,
1719
- write: false,
1720
- logLevel: 'silent',
1721
- });
1825
+ const result = await Promise.race([
1826
+ build({
1827
+ entryPoints: [esbuildPath(entry)],
1828
+ bundle: true,
1829
+ format: 'iife',
1830
+ globalName: '__veskRuntime',
1831
+ platform: 'browser',
1832
+ target: ['es2022'],
1833
+ treeShaking: true,
1834
+ // Do not let esbuild rename identifiers here. This bundle is one large
1835
+ // IIFE built from many runtime modules, and the renaming pass of some
1836
+ // esbuild versions (observed on 0.25.12) emits a free reference to an
1837
+ // undefined minified name (ReferenceError: m is not defined) that kills
1838
+ // client hydration for the whole app. Whitespace-only output keeps the
1839
+ // runtime deterministic at a modest size cost; tree-shaking still prunes
1840
+ // unreachable modules, so names coming out of the IIFE are untouched.
1841
+ minify: false,
1842
+ write: false,
1843
+ logLevel: 'silent',
1844
+ }),
1845
+ new Promise((_, reject) => setTimeout(() => reject(new Error('runtime esbuild timeout')), 60000)),
1846
+ ]);
1722
1847
  const bundle = result.outputFiles[0].text;
1723
1848
  return `${bundle}\nconst { ${unique.join(', ')} } = __veskRuntime;\nexport { ${unique.join(', ')} };\n`;
1724
1849
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../src/dev-server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,MAAM,EAA6C,MAAM,WAAW,CAAC;AASjG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAO7C,OAAO,KAAK,EAAa,gBAAgB,EAAwB,MAAM,yBAAyB,CAAC;AAgBjG;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,IAAI,GAAG,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;IAC9C,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB;+EAC2E;IAC3E,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,mEAAmE;AACnE,wBAAgB,kBAAkB,IAAI,WAAW,CAEhD;AAwDD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,mHAAmH;IACnH,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC7B;;8BAE0B;IAC1B,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,UAAU,GAAG,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;IACrE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,WAAW,CAAC;IAChC,8EAA8E;IAC9E,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,wBAAwB,GAAG;IACvE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;CAChH,CAUA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAa3C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIrD;AAUD,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAorBvG"}
1
+ {"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../src/dev-server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,MAAM,EAA6C,MAAM,WAAW,CAAC;AASjG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAO7C,OAAO,KAAK,EAAa,gBAAgB,EAAwB,MAAM,yBAAyB,CAAC;AAgBjG;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,IAAI,GAAG,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;IAC9C,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB;+EAC2E;IAC3E,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,mEAAmE;AACnE,wBAAgB,kBAAkB,IAAI,WAAW,CAEhD;AAwDD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,mHAAmH;IACnH,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC7B;;8BAE0B;IAC1B,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,UAAU,GAAG,YAAY,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;IACrE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,WAAW,CAAC;IAChC,8EAA8E;IAC9E,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,wBAAwB,GAAG;IACvE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;CAChH,CAUA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAa3C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQrD;AAUD,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA4sBvG"}
@@ -150,7 +150,10 @@ export function devBootstrapScript() {
150
150
  export function injectDevScripts(html) {
151
151
  if (!html.includes('</body>'))
152
152
  return html;
153
- const scripts = '\t<script type="module" src="/_vesk/hmr.js"></script>\n' + devBootstrapScript() + '\n';
153
+ const scripts = '\t<script type="module" src="/_vesk/client.js"></script>\n' +
154
+ '\t<script type="module" src="/_vesk/hmr.js"></script>\n' +
155
+ devBootstrapScript() +
156
+ '\n';
154
157
  return html.replace('</body>', scripts + '</body>');
155
158
  }
156
159
  const MIME = {
@@ -404,7 +407,12 @@ export async function startDevServer(appDir, options) {
404
407
  }
405
408
  }
406
409
  }
407
- await doBuild().catch(() => { });
410
+ await doBuild();
411
+ if (pendingInitialError) {
412
+ console.error(`vesk dev: initial build failed — ${pendingInitialError.message}`);
413
+ console.error(`vesk dev: cannot start server with a broken build. Fix the errors above and restart.`);
414
+ process.exit(1);
415
+ }
408
416
  const server = createServer(async (req, res) => {
409
417
  const url = new URL(req.url || '/', `http://localhost:${port}`);
410
418
  // SSR fetches may run while concurrent sub-requests (e.g. the page's own
@@ -507,6 +515,25 @@ export async function startDevServer(appDir, options) {
507
515
  res.end(runtimeBundle);
508
516
  return;
509
517
  }
518
+ // Serve the dev client bundle at /_vesk/client.js. The file is written to
519
+ // devDir/static/client.js by the build step. The SSR-injected script tag
520
+ // references this exact URL.
521
+ if (url.pathname === '/_vesk/client.js') {
522
+ const clientPath = resolve(devDir, 'static', 'client.js');
523
+ try {
524
+ if (existsSync(clientPath)) {
525
+ res.writeHead(200, { 'Content-Type': 'application/javascript' });
526
+ res.end(readFileSync(clientPath, 'utf-8'));
527
+ return;
528
+ }
529
+ }
530
+ catch {
531
+ /* fall through to 404 below */
532
+ }
533
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
534
+ res.end('client.js not built yet');
535
+ return;
536
+ }
510
537
  // External data scripts (/ssr-data.js?t=...) are emitted by
511
538
  // storeDataScriptGlobal once a render serializes SSR data. Without this
512
539
  // route the dev SPA fallback served the HTML document as the script's
@@ -1 +1 @@
1
- {"version":3,"file":"esbuild-fallback.d.ts","sourceRoot":"","sources":["../src/esbuild-fallback.ts"],"names":[],"mappings":"AAkCA,wBAAsB,KAAK,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAiBtD"}
1
+ {"version":3,"file":"esbuild-fallback.d.ts","sourceRoot":"","sources":["../src/esbuild-fallback.ts"],"names":[],"mappings":"AAkCA,wBAAsB,KAAK,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAoBtD"}
@@ -4,7 +4,7 @@
4
4
  // the compiler's dependency-free `stripCodeTypes` (acorn-based), so no
5
5
  // transformSync path exists anymore.
6
6
  let _nativeBuild = null;
7
- let _nativeDead = false;
7
+ let _nativeDead = process.env.VESK_NO_NATIVE_ESBUILD === 'true';
8
8
  let _wasm = null;
9
9
  let _wasmReady = null;
10
10
  async function loadNative() {
@@ -25,22 +25,25 @@ async function getWasm() {
25
25
  _wasmReady = import('esbuild-wasm').then(m => {
26
26
  _wasm = m;
27
27
  return m;
28
- }).catch(() => {
28
+ }).catch((e) => {
29
29
  _wasmReady = null;
30
- throw new Error('esbuild-wasm not available — install it with: npm install esbuild-wasm');
30
+ throw new Error(`esbuild-wasm not available — install it with: npm install esbuild-wasm (underlying: ${e instanceof Error ? e.message : String(e)})`);
31
31
  });
32
32
  }
33
33
  return _wasmReady;
34
34
  }
35
35
  export async function build(options) {
36
36
  await loadNative();
37
- if (_nativeBuild) {
37
+ if (_nativeBuild && !_nativeDead) {
38
38
  try {
39
- return await _nativeBuild(options);
39
+ return await Promise.race([
40
+ _nativeBuild(options),
41
+ new Promise((_, reject) => setTimeout(() => reject(new Error('esbuild native timeout')), 120000)),
42
+ ]);
40
43
  }
41
44
  catch (e) {
42
45
  const msg = e instanceof Error ? e.message : String(e);
43
- if (msg.includes('SIGILL') || msg.includes('illegal hardware instruction') || msg.includes('cannot execute binary file')) {
46
+ if (msg.includes('SIGILL') || msg.includes('illegal hardware instruction') || msg.includes('cannot execute binary file') || msg.includes('esbuild native timeout')) {
44
47
  console.warn('vesk: native esbuild failed, falling back to esbuild-wasm:', msg);
45
48
  _nativeDead = true;
46
49
  }
@@ -1 +1 @@
1
- {"version":3,"file":"platform-handler.d.ts","sourceRoot":"","sources":["../src/platform-handler.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AASvE,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,SAAS,EAAE,CAAC;IACvB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iFAAiF;IACjF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAMpD;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEtC;AAgBD;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CA4PjF;AAED,MAAM,WAAW,qBAAqB;IACpC,kEAAkE;IAClE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4GzF;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,SAAS,EAAE,CAAC;IACvB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB"}
1
+ {"version":3,"file":"platform-handler.d.ts","sourceRoot":"","sources":["../src/platform-handler.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AASvE,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,SAAS,EAAE,CAAC;IACvB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iFAAiF;IACjF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAMpD;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEtC;AAgBD;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CA4PjF;AAED,MAAM,WAAW,qBAAqB;IACpC,kEAAkE;IAClE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmJzF;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,SAAS,EAAE,CAAC;IACvB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB"}
@@ -337,17 +337,42 @@ export const sep = '/';
337
337
  export const delimiter = ':';
338
338
  export const spawnSync = () => ({ status: 0 });
339
339
  export const execSync = () => '';
340
- export const randomBytes = () => ({});
341
- export const createHash = () => ({ update: () => {}, digest: () => '' });
342
340
  `;
343
- return {
344
- contents,
345
- loader: 'js',
346
- };
341
+ return { contents, loader: 'js' };
347
342
  });
348
343
  },
349
344
  });
350
345
  }
346
+ // Edge/neutral targets bundle everything into ESM. CJS dependencies (e.g.
347
+ // qrcode-generator via vesk-qr) can't be bundled as ESM — esbuild rewrites
348
+ // their require() calls into `__require` shims that throw in ESM output.
349
+ // esbuild 0.28's string `external` option does not externalize resolvable
350
+ // bare specifiers (it emits `__require` shims instead), so resolve these
351
+ // packages to empty stub modules. Nothing from them is bundled, so no
352
+ // `__require` definition or call leaks into the edge bundle. vesk-qr and its
353
+ // optional CJS deps (qrcode-generator, canvas, @zxing/*) are optional at
354
+ // runtime on edge (they fail gracefully), so stubbing them at build time is
355
+ // safe and keeps the bundle valid ESM.
356
+ plugins.push({
357
+ name: 'stub-edge-packages',
358
+ setup(build) {
359
+ build.onResolve({ filter: /^(vesk-qr|qrcode-generator|canvas|@zxing\/(browser|node))(\/|$)/ }, (args) => ({ path: args.path, namespace: 'vesk-edge-stub' }));
360
+ build.onLoad({ filter: /.*/, namespace: 'vesk-edge-stub' }, () => ({
361
+ contents: [
362
+ 'export const CofoundrMatrix = undefined;\n',
363
+ 'export const generate = undefined;\n',
364
+ 'export const Qr = undefined;\n',
365
+ 'export const RawHtml = undefined;\n',
366
+ 'export const CanvasUnavailableError = undefined;\n',
367
+ 'export const InvalidConfigError = undefined;\n',
368
+ 'export const QREncodingError = undefined;\n',
369
+ 'export const ScanReliabilityError = undefined;\n',
370
+ 'export const QRError = undefined;\n',
371
+ ].join(''),
372
+ loader: 'js',
373
+ }));
374
+ },
375
+ });
351
376
  const result = await build({
352
377
  entryPoints: [entry],
353
378
  bundle: true,
@@ -377,7 +402,18 @@ export const createHash = () => ({ update: () => {}, digest: () => '' });
377
402
  'const require = createRequire(import.meta.url);\n',
378
403
  },
379
404
  }
380
- : {}),
405
+ : {
406
+ // Edge/neutral targets bundle everything into ESM. CJS dependencies
407
+ // (e.g. qrcode-generator via vesk-qr) can't be bundled as ESM —
408
+ // esbuild adds `__require` shims that throw in ESM output. Mark them
409
+ // external so they're left as bare imports instead of `__require`
410
+ // shims. vesk-qr is ESM but internally requires() its CJS deps,
411
+ // which would generate __require shims — externalize it too so its
412
+ // code never enters the bundle. On edge hosts without node_modules
413
+ // these external imports fail at runtime (expected — CJS packages
414
+ // don't run on edge), but the bundle itself is valid ESM.
415
+ external: ['vesk-qr', 'qrcode-generator', 'canvas', '@zxing/browser', '@zxing/node'],
416
+ }),
381
417
  logOverride: { 'direct-eval': 'silent' },
382
418
  plugins,
383
419
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vesk/adapter",
3
- "version": "0.2.92",
3
+ "version": "0.2.93",
4
4
  "type": "module",
5
5
  "description": "Vesk adapter — builds deployable .vesk/ output for Deno and Node serverless",
6
6
  "main": "./dist/index.js",
@@ -23,9 +23,9 @@
23
23
  "sharp": "^0.35.3"
24
24
  },
25
25
  "dependencies": {
26
- "@vesk/compiler": "^0.2.92",
27
- "@vesk/runtime": "^0.2.92",
28
- "@vesk/types": "^0.2.92"
26
+ "@vesk/compiler": "^0.2.93",
27
+ "@vesk/runtime": "^0.2.93",
28
+ "@vesk/types": "^0.2.93"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/ws": "^8.18.1",