@vesk/adapter 0.2.26 → 0.2.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"client-bundle.d.ts","sourceRoot":"","sources":["../src/client-bundle.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,mBAAmB,EAAE,kBAAkB,EAAmF,MAAM,yBAAyB,CAAC;AAoCnL;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CA8BhE;AAoWD;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAkBxD;AAmCD,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,SAAS,EAAE,EACtB,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,kBAAkB,CAAC,CA4Z7B;AAMD,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA+B3D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAUlE;AAYD;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAuCrG"}
1
+ {"version":3,"file":"client-bundle.d.ts","sourceRoot":"","sources":["../src/client-bundle.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,mBAAmB,EAAE,kBAAkB,EAAmF,MAAM,yBAAyB,CAAC;AAoCnL;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CA8BhE;AAqVD;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAkBxD;AAmCD,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,SAAS,EAAE,EACtB,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,kBAAkB,CAAC,CAma7B;AAMD,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA+B3D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAUlE;AAYD;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAuCrG"}
@@ -4,10 +4,11 @@ import { fileURLToPath } from 'node:url';
4
4
  import { build } from './esbuild-fallback.js';
5
5
  import { stripCodeTypes } from '@vesk/compiler/src/strip-ts';
6
6
  import { parse } from '@vesk/compiler/src/parser';
7
- import { compileClient, compileClientBoth } from '@vesk/compiler/src/client-codegen';
7
+ import { compileClient, compileClientBoth, nameAllocFor } from '@vesk/compiler/src/client-codegen';
8
8
  import { resolveComponentName } from '@vesk/compiler/src/server-codegen';
9
9
  import { collectVskImportPaths, vskImportLines } from '@vesk/compiler/src/vsk-imports';
10
10
  import { inlineMdContentAttrs, guessProjectRoots } from '@vesk/compiler/src/md-inline';
11
+ import { resolveImportPath, findTsconfigPath } from '@vesk/compiler/src/module-imports';
11
12
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
13
  function fileUnchanged(filePath, cached) {
13
14
  try {
@@ -207,20 +208,6 @@ function demoteExports(code) {
207
208
  function chunkHasImports(headLen, mergeAborted) {
208
209
  return headLen > 0 || mergeAborted;
209
210
  }
210
- /**
211
- * Rewrites a compiled file's source specifier so esbuild can resolve it from
212
- * the temp bundle entry. Bare package specifiers (e.g. `lucide-vesk`) stay
213
- * as-is; relative specifiers are resolved to absolute filesystem paths with a
214
- * `.ts` extension appended so esbuild loads the module as TypeScript and
215
- * inlines its exported values. Offset-based on the parser AST — never a text
216
- * scan.
217
- */
218
- function resolveImportSource(src, filePath) {
219
- if (src.startsWith('./') || src.startsWith('../')) {
220
- return resolve(dirname(filePath), src) + '.ts';
221
- }
222
- return src;
223
- }
224
211
  /**
225
212
  * Accumulates every import a code-split chunk needs and rebuilds it as a
226
213
  * single deduped import block.
@@ -273,7 +260,7 @@ class ChunkImports {
273
260
  const src = node.source?.value;
274
261
  if (typeof src !== 'string')
275
262
  continue;
276
- const resolved = resolveImportSource(src, filePath);
263
+ const resolved = resolveImportPath(src, dirname(filePath));
277
264
  for (const spec of node.specifiers ?? []) {
278
265
  const s = spec;
279
266
  const local = s.local?.name;
@@ -793,6 +780,9 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
793
780
  const toBundle = `const __components = globalThis.__components || (globalThis.__components = {});\nconst __hydrators = globalThis.__hydrators || (globalThis.__hydrators = {});\n${entry.code}\n`;
794
781
  writeFileSync(tmpFile, toBundle);
795
782
  try {
783
+ // Pass the app tsconfig so esbuild resolves tsconfig `paths` aliases
784
+ // (`@/x`, `@app/x`) inside transitively bundled modules too.
785
+ const appTsconfig = findTsconfigPath(appDir);
796
786
  const result = await build({
797
787
  entryPoints: [tmpFile],
798
788
  bundle: true,
@@ -801,6 +791,7 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
801
791
  write: false,
802
792
  logLevel: 'silent',
803
793
  loader: { '.js': 'tsx' },
794
+ ...(appTsconfig ? { tsconfig: appTsconfig } : {}),
804
795
  });
805
796
  finalCode = result.outputFiles[0].text;
806
797
  }
@@ -863,8 +854,11 @@ export async function generateClientBundle(routeTree, appDir, componentMap, opti
863
854
  seen.add(filePath);
864
855
  const src = readFileSync(filePath, 'utf-8');
865
856
  resolveVskImports(filePath, (p, n) => compileFileMono(p, n || ''));
866
- const compCode = compileClient(src, null, { forceClient: true, sourcePath: filePath });
867
- const hydCode = compileClient(src, null, { hydrate: true, forceClient: true, includeTopLevel: false, sourcePath: filePath });
857
+ // Share one name allocator so the comp and hyd contributions (which are
858
+ // joined into a single scoped block below) never reuse `$n` names.
859
+ const monoAlloc = nameAllocFor(filePath);
860
+ const compCode = compileClient(src, null, { forceClient: true, sourcePath: filePath, nameAllocator: monoAlloc });
861
+ const hydCode = compileClient(src, null, { hydrate: true, forceClient: true, includeTopLevel: false, sourcePath: filePath, nameAllocator: monoAlloc });
868
862
  if (compCode)
869
863
  collectRuntimeImports(compCode);
870
864
  if (hydCode)
@@ -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;AAOjG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAI7C,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;AAuCD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAIzE;AAkBD,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;AA8CD,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwnBvG"}
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;AAOjG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAK7C,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;AAuCD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAIzE;AAkBD,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;AA8CD,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA2oBvG"}
@@ -8,6 +8,7 @@ import { build } from '@vesk/adapter/src/index';
8
8
  import { buildErrorPayload, createHmrServer } from './hmr';
9
9
  import * as hmrApi from './hmr';
10
10
  import { createDevApiRouter } from './dev-api';
11
+ import { resolveRuntimeErrorPayload, renderDevErrorPage } from './ssr-error';
11
12
  import { buildTreeShakenRuntime, runtimeExportNames } from '@vesk/adapter/src/client-bundle';
12
13
  import { resolveWithin, installMdReadHook } from '@vesk/adapter/src/paths';
13
14
  import { getPluginRecords } from './plugins';
@@ -359,6 +360,25 @@ export async function startDevServer(appDir, options) {
359
360
  let routeTree = [];
360
361
  let runtimeBundle = '';
361
362
  let pendingInitialError = null;
363
+ /**
364
+ * Render an SSR failure as the unified dev error page (same payload shape
365
+ * the HMR overlay shows): the `.vsk`/`.ts` source site, codeframe, tips,
366
+ * and the `VeskError` code when present — never a bare `500` page. Best
367
+ * effort: on any failure falls back to the old generic 500 HTML.
368
+ */
369
+ function renderSsrErrorPage(e, pathname) {
370
+ try {
371
+ const payload = resolveRuntimeErrorPayload(e, {
372
+ appDir,
373
+ projectDir: resolve(appDir, '..'),
374
+ componentMap,
375
+ });
376
+ return injectDevScripts(renderDevErrorPage(payload, { status: 500, url: pathname }));
377
+ }
378
+ catch {
379
+ return '<!DOCTYPE html><html><body><h1>500</h1><pre>Internal Server Error</pre></body></html>';
380
+ }
381
+ }
362
382
  let eventsMod = null;
363
383
  async function reloadEventsMod() {
364
384
  const eventsPath = resolve(devDir, 'server', 'events.js');
@@ -671,7 +691,7 @@ export async function startDevServer(appDir, options) {
671
691
  }
672
692
  catch (e) {
673
693
  res.writeHead(500, { 'Content-Type': 'text/html' });
674
- res.end('<!DOCTYPE html><html><body><h1>500</h1><pre>Internal Server Error</pre></body></html>');
694
+ res.end(renderSsrErrorPage(e, url.pathname));
675
695
  }
676
696
  return;
677
697
  }
@@ -698,7 +718,7 @@ export async function startDevServer(appDir, options) {
698
718
  }
699
719
  catch (e) {
700
720
  res.writeHead(500, { 'Content-Type': 'text/html' });
701
- res.end('<!DOCTYPE html><html><body><h1>500</h1><pre>Internal Server Error</pre></body></html>');
721
+ res.end(renderSsrErrorPage(e, url.pathname));
702
722
  }
703
723
  return;
704
724
  }
@@ -18,6 +18,8 @@ export interface ErroredLocation {
18
18
  message: string;
19
19
  stack: string | null;
20
20
  codeframe: Codeframe | null;
21
+ /** VeskError-style `.code` (e.g. `V0412`) when the thrown error carries one. */
22
+ code?: string;
21
23
  }
22
24
  export declare function parseCompilerError(err: unknown, file: string, src?: string): ErroredLocation | null;
23
25
  //# sourceMappingURL=error-codeframe.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"error-codeframe.d.ts","sourceRoot":"","sources":["../src/error-codeframe.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,QAAQ,EAAE,CAAC;CAClB;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,SAAI,GAAG,SAAS,GAAG,IAAI,CAcxG;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;CAC7B;AAuBD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAwFnG"}
1
+ {"version":3,"file":"error-codeframe.d.ts","sourceRoot":"","sources":["../src/error-codeframe.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,QAAQ,EAAE,CAAC;CAClB;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,SAAI,GAAG,SAAS,GAAG,IAAI,CAcxG;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAuBD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CA4FnG"}
@@ -116,6 +116,9 @@ export function parseCompilerError(err, file, src) {
116
116
  if (codeframe)
117
117
  codeframe.file = fileResolved;
118
118
  }
119
+ let code;
120
+ if (record && typeof record.code === 'string' && record.code)
121
+ code = record.code;
119
122
  return {
120
123
  file: fileResolved,
121
124
  line,
@@ -123,5 +126,6 @@ export function parseCompilerError(err, file, src) {
123
126
  message,
124
127
  stack,
125
128
  codeframe,
129
+ code,
126
130
  };
127
131
  }
package/dist/hmr.d.ts CHANGED
@@ -17,6 +17,8 @@ export interface HmrErrorPayload {
17
17
  suggestions?: string[];
18
18
  nextSteps?: string[];
19
19
  stack?: string;
20
+ /** Optional VeskError-style code (e.g. `V0412`) carried on the thrown error. */
21
+ code?: string;
20
22
  }
21
23
  export declare function getHmrState(): {
22
24
  status: 'up' | 'closed';
package/dist/hmr.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"hmr.d.ts","sourceRoot":"","sources":["../src/hmr.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAUxC,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAEvG,OAAO,KAAK,EAAE,SAAS,EAAkB,MAAM,yBAAyB,CAAC;AAIzE;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAaD,wBAAgB,WAAW,IAAI;IAC7B,MAAM,EAAE,IAAI,GAAG,QAAQ,CAAC;IACxB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAQA;AAED,MAAM,WAAW,wBAAwB;IACvC,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,wBAA6B,GAClC,eAAe,CA6FjB;AAwRD,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACjC;IACD,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAClE,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvH,WAAW,EAAE,MAAM,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;CACnD,CA6KA"}
1
+ {"version":3,"file":"hmr.d.ts","sourceRoot":"","sources":["../src/hmr.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAUxC,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAEvG,OAAO,KAAK,EAAE,SAAS,EAAkB,MAAM,yBAAyB,CAAC;AAIzE;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAaD,wBAAgB,WAAW,IAAI;IAC7B,MAAM,EAAE,IAAI,GAAG,QAAQ,CAAC;IACxB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAQA;AAED,MAAM,WAAW,wBAAwB;IACvC,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,wBAA6B,GAClC,eAAe,CAsGjB;AAwRD,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACjC;IACD,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAClE,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvH,WAAW,EAAE,MAAM,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;CACnD,CA6KA"}
package/dist/hmr.js CHANGED
@@ -103,6 +103,13 @@ export function buildErrorPayload(error, filename, opts = {}) {
103
103
  const filePath = resolvedAbsPath ?? (typeof opts.appDir === 'string'
104
104
  ? resolve(opts.appDir, file)
105
105
  : undefined);
106
+ // VeskError throws carry a stable `.code` (e.g. `V0412`) — surface it in the
107
+ // payload so the HMR overlay, SSR error page, and diagnostics can show it.
108
+ const errorCode = parsed && typeof parsed.code === 'string' && parsed.code
109
+ ? parsed.code
110
+ : error?.code
111
+ ? String(error.code)
112
+ : undefined;
106
113
  // The file header already shows the location; strip the redundant
107
114
  // " in <file>" suffix compilers append to messages (e.g. acorn's
108
115
  // "Unexpected token in /abs/app/page.vsk").
@@ -117,6 +124,8 @@ export function buildErrorPayload(error, filename, opts = {}) {
117
124
  column,
118
125
  message: cleanMessage,
119
126
  };
127
+ if (errorCode)
128
+ payload.code = errorCode;
120
129
  if (filePath)
121
130
  payload.filePath = filePath;
122
131
  if (codeframe)
@@ -0,0 +1,92 @@
1
+ import { type HmrErrorPayload } from './hmr';
2
+ export interface RuntimeErrorContext {
3
+ /** Absolute path of the app dir (`.../app`). Used to resolve sources for codeframes. */
4
+ appDir: string;
5
+ /** Absolute path of the project root (`.../`, parent of `app/`). Defaults to `resolve(appDir, '..')`. */
6
+ projectDir?: string;
7
+ /** Absolute `.vsk` files for the failing route (page first). Scanned for the failing identifier. */
8
+ routeFiles?: string[];
9
+ /** `componentName -> absolute .vsk path` for shared `components/` (from `scanComponents`). */
10
+ componentMap?: Map<string, string>;
11
+ /** `componentName -> absolute .vsk path` for route files (from `collectSources`). */
12
+ routeSources?: Map<string, string>;
13
+ }
14
+ export interface ClientErrorReport {
15
+ message?: unknown;
16
+ stack?: unknown;
17
+ /** Compiled-bundle URL/path from `window.onerror` — display fallback only, never the payload file. */
18
+ filename?: unknown;
19
+ line?: unknown;
20
+ column?: unknown;
21
+ /** VeskError-style code (e.g. `V0412`) when the client error carried one. */
22
+ code?: unknown;
23
+ }
24
+ /**
25
+ * Extract the undeclared identifier from `ReferenceError`-style messages:
26
+ * `Menu is not defined`, `ReferenceError: Menu is not defined`,
27
+ * `Cannot find name 'Menu'`, `Can't find variable: Menu`.
28
+ */
29
+ export declare function extractUndefinedName(message: string): string | null;
30
+ /**
31
+ * Component names from a compiled render stack, innermost frame first:
32
+ * `__components.SiteHeader`, `__components["SiteHeader"]`, `__components['Home']`.
33
+ */
34
+ export declare function extractComponentNames(stack: string): string[];
35
+ export interface StackFrame {
36
+ /** Path or URL as it appears in the stack frame (may be a bundle URL). */
37
+ path: string;
38
+ line: number;
39
+ column: number;
40
+ }
41
+ /**
42
+ * Parse V8-style stack lines into `path:line:col` frames (1-based column,
43
+ * matching the codeframe caret). Supports both parenthesized frames
44
+ * (`at fn (/abs/file.ts:12:5)`) and bare path frames (`at file.ts:12:5`).
45
+ */
46
+ export declare function extractSourceFrames(stack: string | undefined | null): StackFrame[];
47
+ /**
48
+ * First stack frame that names a real source file inside the project
49
+ * (`.ts`/`.tsx`/`.js`/`.vsk`, existing on disk, not node_modules/dist/.vesk
50
+ * or the dev bundle). Innermost (topmost) real frame is the throw site —
51
+ * e.g. `ReferenceError` inside `lib/helper.ts` imported by a `.vsk` page.
52
+ */
53
+ export declare function firstSourceFileFrame(stack: string | undefined | null, projectRoot: string): StackFrame | null;
54
+ /**
55
+ * First use of an identifier in a `.vsk` source file (1-based line/column).
56
+ * Import lines are skipped — an undeclared name's *use* site is the error site.
57
+ */
58
+ export declare function findIdentifierUsage(src: string, name: string): {
59
+ line: number;
60
+ column: number;
61
+ } | null;
62
+ /** Display path for a payload: app-relative (`page.vsk`), else project-relative (`components/X.vsk`). */
63
+ export declare function displayPath(absPath: string, ctx: RuntimeErrorContext): string;
64
+ /** Ordered absolute candidate files: stack-named components first, then route files. */
65
+ export declare function candidateFiles(message: string, stack: string | undefined, ctx: RuntimeErrorContext): string[];
66
+ /**
67
+ * Resolve a server-side throw to the HMR payload shape. Never throws — on any
68
+ * failure returns the message + stack + generic tips (the old behavior, but in
69
+ * the overlay-renderable shape).
70
+ */
71
+ export declare function resolveRuntimeErrorPayload(err: unknown, ctx: RuntimeErrorContext): HmrErrorPayload;
72
+ /**
73
+ * Resolve a client-side `{ message, stack }` report (compiled-bundle
74
+ * coordinates) to the HMR payload shape. Same core as the server path, but
75
+ * stack-named components outrank route files — the throw site is named by the
76
+ * `__components.Foo` frames, as in `ReferenceError: Menu is not defined` from
77
+ * `__components.SiteHeader` inside `__components.Home`.
78
+ */
79
+ export declare function resolveClientErrorReport(report: ClientErrorReport, ctx: RuntimeErrorContext): HmrErrorPayload;
80
+ /** `file:line:col` (or `file`, or `''`) for one-line terminal logs. */
81
+ export declare function formatErrorLocation(payload: HmrErrorPayload): string;
82
+ /**
83
+ * Standalone dev 500 page in the overlay's visual language (file header,
84
+ * message, codeframe with caret, TIPS/SUGGESTIONS/NEXT STEPS, collapsible
85
+ * stack). The HTML keeps a `</body>` so callers can run `injectDevScripts`
86
+ * over it — the HMR client then pops the same payload as an overlay on load.
87
+ */
88
+ export declare function renderDevErrorPage(payload: HmrErrorPayload, opts?: {
89
+ status?: number;
90
+ url?: string;
91
+ }): string;
92
+ //# sourceMappingURL=ssr-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssr-error.d.ts","sourceRoot":"","sources":["../src/ssr-error.ts"],"names":[],"mappings":"AAkCA,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,OAAO,CAAC;AAEhE,MAAM,WAAW,mBAAmB;IAClC,wFAAwF;IACxF,MAAM,EAAE,MAAM,CAAC;IACf,yGAAyG;IACzG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oGAAoG;IACpG,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,8FAA8F;IAC9F,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,qFAAqF;IACrF,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,sGAAsG;IACtG,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAkBD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAqBnE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA4B7D;AAED,MAAM,WAAW,UAAU;IACzB,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AA8BD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,UAAU,EAAE,CAiBlF;AAoBD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAChC,WAAW,EAAE,MAAM,GAClB,UAAU,GAAG,IAAI,CAiBnB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAmBtG;AAMD,yGAAyG;AACzG,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,mBAAmB,GAAG,MAAM,CAM7E;AA0BD,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,mBAAmB,GAAG,MAAM,EAAE,CAe7G;AAgBD;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,mBAAmB,GAAG,eAAe,CAsDlG;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,EAAE,mBAAmB,GAAG,eAAe,CAM7G;AAED,uEAAuE;AACvE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAMpE;AAUD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,eAAe,EACxB,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACvC,MAAM,CAuER"}
@@ -0,0 +1,482 @@
1
+ /**
2
+ * Dev-only runtime-error → HMR-payload resolver.
3
+ *
4
+ * HMR compile errors already report through the canonical `HmrErrorPayload`
5
+ * (file + line/column + codeframe + tips/suggestions/nextSteps + stack,
6
+ * rendered by the client's error overlay). SSR render failures and client
7
+ * runtime failures historically did NOT: the dev server swallowed them into a
8
+ * generic `<h1>500</h1>` page (or a raw `<pre>` stack dump), and the client
9
+ * overlay showed compiled-bundle coordinates (`page-index.js:833`) that point
10
+ * nowhere in user source.
11
+ *
12
+ * This module closes that gap. Given a thrown value (or a client-side
13
+ * `{ message, stack }` report) plus the dev server's knowledge of the app
14
+ * (route files, shared `components/`, route/component name maps), it resolves
15
+ * the failure back to a `.vsk` file/line and returns the SAME payload shape
16
+ * the HMR channel broadcasts — so SSR 500s and client `ReferenceError`s render
17
+ * in the same overlay with the same codeframe + tips.
18
+ *
19
+ * Resolution strategy (best-effort, never throws):
20
+ * 1. Errors that already carry a location (VeskError/acorn `.loc`,
21
+ * `.position`, `.line`) go straight through `buildErrorPayload`.
22
+ * 2. `X is not defined` (and the `Cannot find name 'X'` / Safari
23
+ * `Can't find variable: X` variants): the identifier plus the
24
+ * `__components.Foo` frames in the stack name the exact `.vsk` files to
25
+ * scan; the first use of the identifier in the best candidate yields the
26
+ * line/column for the codeframe.
27
+ * 3. Other errors with `__components.Foo` frames: the payload at least names
28
+ * the innermost `.vsk` file instead of the compiled bundle.
29
+ * 4. Anything else: message + stack + generic tips, same as before — but in
30
+ * the HMR payload shape so the overlay still renders it.
31
+ */
32
+ import { existsSync, readFileSync } from 'node:fs';
33
+ import { relative, resolve } from 'node:path';
34
+ import { buildCodeframe } from './error-codeframe';
35
+ import { buildErrorPayload } from './hmr';
36
+ function isIdentStart(ch) {
37
+ return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch === '_' || ch === '$';
38
+ }
39
+ function isIdentPart(ch) {
40
+ return isIdentStart(ch) || (ch >= '0' && ch <= '9');
41
+ }
42
+ function takeIdentifier(s) {
43
+ if (!s)
44
+ return null;
45
+ if (!isIdentStart(s.charAt(0)))
46
+ return null;
47
+ let i = 1;
48
+ while (i < s.length && isIdentPart(s.charAt(i)))
49
+ i++;
50
+ return s.slice(0, i);
51
+ }
52
+ /**
53
+ * Extract the undeclared identifier from `ReferenceError`-style messages:
54
+ * `Menu is not defined`, `ReferenceError: Menu is not defined`,
55
+ * `Cannot find name 'Menu'`, `Can't find variable: Menu`.
56
+ */
57
+ export function extractUndefinedName(message) {
58
+ if (typeof message !== 'string' || !message)
59
+ return null;
60
+ const notDefined = message.indexOf(' is not defined');
61
+ if (notDefined > 0) {
62
+ const head = message.slice(0, notDefined);
63
+ let i = head.length - 1;
64
+ while (i >= 0 && isIdentPart(head.charAt(i)))
65
+ i--;
66
+ return takeIdentifier(head.slice(i + 1));
67
+ }
68
+ const findName = message.indexOf("Cannot find name '");
69
+ if (findName >= 0) {
70
+ const rest = message.slice(findName + "Cannot find name '".length);
71
+ const end = rest.indexOf("'");
72
+ if (end > 0)
73
+ return takeIdentifier(rest.slice(0, end));
74
+ return null;
75
+ }
76
+ const findVar = message.indexOf("Can't find variable:");
77
+ if (findVar >= 0) {
78
+ return takeIdentifier(message.slice(findVar + "Can't find variable:".length).trim());
79
+ }
80
+ return null;
81
+ }
82
+ /**
83
+ * Component names from a compiled render stack, innermost frame first:
84
+ * `__components.SiteHeader`, `__components["SiteHeader"]`, `__components['Home']`.
85
+ */
86
+ export function extractComponentNames(stack) {
87
+ const names = [];
88
+ if (typeof stack !== 'string' || !stack)
89
+ return names;
90
+ const marker = '__components';
91
+ let i = 0;
92
+ while (i < stack.length) {
93
+ const at = stack.indexOf(marker, i);
94
+ if (at < 0)
95
+ break;
96
+ let j = at + marker.length;
97
+ let name = null;
98
+ if (stack.charAt(j) === '.') {
99
+ name = takeIdentifier(stack.slice(j + 1));
100
+ j += (name ? name.length : 0) + 1;
101
+ }
102
+ else if (stack.charAt(j) === '[') {
103
+ const quote = stack.charAt(j + 1);
104
+ if (quote === '"' || quote === "'") {
105
+ const end = stack.indexOf(quote, j + 2);
106
+ if (end > j + 2) {
107
+ const raw = stack.slice(j + 2, end);
108
+ name = takeIdentifier(raw) !== null && takeIdentifier(raw) === raw ? raw : null;
109
+ j = end + 1;
110
+ }
111
+ }
112
+ }
113
+ if (name && !names.includes(name))
114
+ names.push(name);
115
+ i = j > at ? j : at + 1;
116
+ }
117
+ return names;
118
+ }
119
+ const SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.mts', '.cts', '.js', '.mjs', '.cjs', '.vsk']);
120
+ function scanFrameLocation(seg) {
121
+ if (!seg)
122
+ return null;
123
+ let i = seg.length;
124
+ let column = 0;
125
+ let digits = 0;
126
+ while (i > 0 && seg.charCodeAt(i - 1) >= 48 && seg.charCodeAt(i - 1) <= 57) {
127
+ column = (seg.charCodeAt(i - 1) - 48) * Math.pow(10, digits) + column;
128
+ digits++;
129
+ i--;
130
+ }
131
+ if (digits === 0 || i === 0 || seg.charAt(i - 1) !== ':')
132
+ return null;
133
+ i--;
134
+ let line = 0;
135
+ digits = 0;
136
+ while (i > 0 && seg.charCodeAt(i - 1) >= 48 && seg.charCodeAt(i - 1) <= 57) {
137
+ line = (seg.charCodeAt(i - 1) - 48) * Math.pow(10, digits) + line;
138
+ digits++;
139
+ i--;
140
+ }
141
+ if (digits === 0 || i === 0 || seg.charAt(i - 1) !== ':')
142
+ return null;
143
+ i--;
144
+ const path = seg.slice(0, i);
145
+ if (!path)
146
+ return null;
147
+ return { path, line, column: column + 1 };
148
+ }
149
+ /**
150
+ * Parse V8-style stack lines into `path:line:col` frames (1-based column,
151
+ * matching the codeframe caret). Supports both parenthesized frames
152
+ * (`at fn (/abs/file.ts:12:5)`) and bare path frames (`at file.ts:12:5`).
153
+ */
154
+ export function extractSourceFrames(stack) {
155
+ const frames = [];
156
+ if (typeof stack !== 'string' || !stack)
157
+ return frames;
158
+ for (const raw of stack.split('\n')) {
159
+ const line = raw.trim();
160
+ if (!line.startsWith('at '))
161
+ continue;
162
+ let seg = line.slice(3).trim();
163
+ if (seg.startsWith('async '))
164
+ seg = seg.slice(6).trim();
165
+ const close = seg.lastIndexOf(')');
166
+ if (close >= 0) {
167
+ const open = seg.lastIndexOf('(', close);
168
+ if (open >= 0)
169
+ seg = seg.slice(open + 1, close);
170
+ }
171
+ const loc = scanFrameLocation(seg);
172
+ if (loc)
173
+ frames.push(loc);
174
+ }
175
+ return frames;
176
+ }
177
+ function isInsideRoot(absPath, root) {
178
+ if (!root)
179
+ return false;
180
+ const base = root.endsWith('/') ? root : root + '/';
181
+ if (absPath === root)
182
+ return true;
183
+ return absPath.startsWith(base);
184
+ }
185
+ const GENERATED_SEGMENTS = ['node_modules', '.vesk'];
186
+ function isGeneratedPath(absPath) {
187
+ for (const seg of GENERATED_SEGMENTS) {
188
+ if (absPath.split('/').includes(seg))
189
+ return true;
190
+ }
191
+ if (absPath.includes('/_vesk/static/'))
192
+ return true;
193
+ if (absPath.includes('/dist/'))
194
+ return true;
195
+ return false;
196
+ }
197
+ /**
198
+ * First stack frame that names a real source file inside the project
199
+ * (`.ts`/`.tsx`/`.js`/`.vsk`, existing on disk, not node_modules/dist/.vesk
200
+ * or the dev bundle). Innermost (topmost) real frame is the throw site —
201
+ * e.g. `ReferenceError` inside `lib/helper.ts` imported by a `.vsk` page.
202
+ */
203
+ export function firstSourceFileFrame(stack, projectRoot) {
204
+ for (const frame of extractSourceFrames(stack)) {
205
+ let p = frame.path;
206
+ if (p.startsWith('file://'))
207
+ p = p.slice('file://'.length);
208
+ if (!p.startsWith('/'))
209
+ continue; // bundle URLs (http://) and relative frames
210
+ const ext = p.slice(p.lastIndexOf('.')).split(':')[0].toLowerCase();
211
+ if (!SOURCE_EXTENSIONS.has(ext))
212
+ continue;
213
+ if (isGeneratedPath(p))
214
+ continue;
215
+ if (!isInsideRoot(p, projectRoot))
216
+ continue;
217
+ try {
218
+ if (!existsSync(p))
219
+ continue;
220
+ }
221
+ catch {
222
+ continue;
223
+ }
224
+ return { ...frame, path: p };
225
+ }
226
+ return null;
227
+ }
228
+ /**
229
+ * First use of an identifier in a `.vsk` source file (1-based line/column).
230
+ * Import lines are skipped — an undeclared name's *use* site is the error site.
231
+ */
232
+ export function findIdentifierUsage(src, name) {
233
+ if (typeof src !== 'string' || !src || typeof name !== 'string' || !takeIdentifier(name) || takeIdentifier(name) !== name) {
234
+ return null;
235
+ }
236
+ const lines = src.split('\n');
237
+ for (let ln = 0; ln < lines.length; ln++) {
238
+ const text = lines[ln];
239
+ if (/^\s*import[\s('"]/.test(text))
240
+ continue;
241
+ let idx = text.indexOf(name);
242
+ while (idx >= 0) {
243
+ const before = idx > 0 ? text.charAt(idx - 1) : '';
244
+ const after = idx + name.length < text.length ? text.charAt(idx + name.length) : '';
245
+ if (!isIdentPart(before) && !isIdentPart(after)) {
246
+ return { line: ln + 1, column: idx + 1 };
247
+ }
248
+ idx = text.indexOf(name, idx + 1);
249
+ }
250
+ }
251
+ return null;
252
+ }
253
+ function projectOf(ctx) {
254
+ return ctx.projectDir || resolve(ctx.appDir, '..');
255
+ }
256
+ /** Display path for a payload: app-relative (`page.vsk`), else project-relative (`components/X.vsk`). */
257
+ export function displayPath(absPath, ctx) {
258
+ const appRel = relative(ctx.appDir, absPath);
259
+ if (appRel && !appRel.startsWith('..'))
260
+ return appRel || absPath;
261
+ const projRel = relative(projectOf(ctx), absPath);
262
+ if (projRel && !projRel.startsWith('..'))
263
+ return projRel;
264
+ return absPath;
265
+ }
266
+ function readIfFile(absPath) {
267
+ try {
268
+ if (!existsSync(absPath))
269
+ return null;
270
+ return readFileSync(absPath, 'utf-8');
271
+ }
272
+ catch {
273
+ return null;
274
+ }
275
+ }
276
+ function resolveComponentFile(name, ctx) {
277
+ const fromRoutes = ctx.routeSources?.get(name);
278
+ if (fromRoutes) {
279
+ const abs = resolve(ctx.appDir, fromRoutes);
280
+ if (readIfFile(abs) !== null)
281
+ return abs;
282
+ }
283
+ const fromShared = ctx.componentMap?.get(name);
284
+ if (fromShared) {
285
+ const abs = resolve(projectOf(ctx), fromShared);
286
+ if (readIfFile(abs) !== null)
287
+ return abs;
288
+ if (readIfFile(fromShared) !== null)
289
+ return fromShared;
290
+ }
291
+ return null;
292
+ }
293
+ /** Ordered absolute candidate files: stack-named components first, then route files. */
294
+ export function candidateFiles(message, stack, ctx) {
295
+ const out = [];
296
+ const push = (abs) => {
297
+ if (!abs || out.includes(abs))
298
+ return;
299
+ if (readIfFile(abs) === null)
300
+ return;
301
+ out.push(abs);
302
+ };
303
+ if (stack) {
304
+ for (const name of extractComponentNames(stack)) {
305
+ const f = resolveComponentFile(name, ctx);
306
+ if (f)
307
+ push(f);
308
+ }
309
+ }
310
+ for (const f of ctx.routeFiles || [])
311
+ push(f);
312
+ return out.slice(0, 12);
313
+ }
314
+ function withCodeframe(payload, absPath, line, column) {
315
+ if (!payload.codeframe) {
316
+ const src = readIfFile(absPath);
317
+ if (src) {
318
+ const cf = buildCodeframe(src, line, column);
319
+ if (cf) {
320
+ cf.file = payload.file || cf.file;
321
+ payload.codeframe = cf;
322
+ }
323
+ }
324
+ }
325
+ return payload;
326
+ }
327
+ /**
328
+ * Resolve a server-side throw to the HMR payload shape. Never throws — on any
329
+ * failure returns the message + stack + generic tips (the old behavior, but in
330
+ * the overlay-renderable shape).
331
+ */
332
+ export function resolveRuntimeErrorPayload(err, ctx) {
333
+ const rec = (typeof err === 'object' && err !== null ? err : null);
334
+ const message = rec && typeof rec.message === 'string' && rec.message
335
+ ? rec.message
336
+ : err instanceof Error ? err.message : String(err ?? 'Unknown error');
337
+ const stack = rec && typeof rec.stack === 'string' && rec.stack
338
+ ? rec.stack
339
+ : err instanceof Error && err.stack ? err.stack : undefined;
340
+ const code = rec && typeof rec.code === 'string' && rec.code ? rec.code
341
+ : err instanceof Error && typeof err.code === 'string'
342
+ ? err.code
343
+ : undefined;
344
+ const candidates = candidateFiles(message, stack, ctx);
345
+ const firstFile = candidates.length > 0 ? displayPath(candidates[0], ctx) : 'app';
346
+ // Errors that already carry a location keep full fidelity.
347
+ const direct = buildErrorPayload(err, firstFile, { appDir: ctx.appDir });
348
+ if (direct.line !== null)
349
+ return direct;
350
+ // A stack frame naming a real source file (`.ts` imported into a `.vsk`,
351
+ // a `.ts` api/middleware module, etc.) is the exact throw site — point the
352
+ // payload there instead of scanning `__components` bundle coords.
353
+ const frame = firstSourceFileFrame(stack, projectOf(ctx));
354
+ if (frame) {
355
+ const payload = buildErrorPayload({ message: direct.message || message, stack, line: frame.line, column: frame.column, ...(code ? { code } : {}) }, displayPath(frame.path, ctx), { appDir: ctx.appDir });
356
+ return withCodeframe(payload, frame.path, frame.line, frame.column);
357
+ }
358
+ const name = extractUndefinedName(direct.message || message);
359
+ if (name) {
360
+ for (const abs of candidates) {
361
+ const src = readIfFile(abs);
362
+ if (!src)
363
+ continue;
364
+ const use = findIdentifierUsage(src, name);
365
+ if (!use)
366
+ continue;
367
+ const payload = buildErrorPayload({ message: direct.message || message, stack, line: use.line, column: use.column, ...(code ? { code } : {}) }, displayPath(abs, ctx), { appDir: ctx.appDir });
368
+ return withCodeframe(payload, abs, use.line, use.column);
369
+ }
370
+ }
371
+ // No pinpoint: still name the innermost .vsk file instead of the bundle.
372
+ if (candidates.length > 0) {
373
+ return buildErrorPayload(err, displayPath(candidates[0], ctx), { appDir: ctx.appDir });
374
+ }
375
+ return direct;
376
+ }
377
+ /**
378
+ * Resolve a client-side `{ message, stack }` report (compiled-bundle
379
+ * coordinates) to the HMR payload shape. Same core as the server path, but
380
+ * stack-named components outrank route files — the throw site is named by the
381
+ * `__components.Foo` frames, as in `ReferenceError: Menu is not defined` from
382
+ * `__components.SiteHeader` inside `__components.Home`.
383
+ */
384
+ export function resolveClientErrorReport(report, ctx) {
385
+ const raw = (report || {});
386
+ const message = typeof raw.message === 'string' && raw.message ? raw.message.slice(0, 5000) : 'Unknown error';
387
+ const stack = typeof raw.stack === 'string' ? raw.stack.slice(0, 20000) : undefined;
388
+ const code = typeof raw.code === 'string' && raw.code ? raw.code : undefined;
389
+ return resolveRuntimeErrorPayload({ message, stack, ...(code ? { code } : {}) }, ctx);
390
+ }
391
+ /** `file:line:col` (or `file`, or `''`) for one-line terminal logs. */
392
+ export function formatErrorLocation(payload) {
393
+ const file = payload.file || '';
394
+ if (payload.line != null) {
395
+ return `${file}:${payload.line}${payload.column != null ? `:${payload.column}` : ''}`;
396
+ }
397
+ return file;
398
+ }
399
+ function escapeHtml(s) {
400
+ return String(s ?? '')
401
+ .replace(/&/g, '&amp;')
402
+ .replace(/</g, '&lt;')
403
+ .replace(/>/g, '&gt;')
404
+ .replace(/"/g, '&quot;');
405
+ }
406
+ /**
407
+ * Standalone dev 500 page in the overlay's visual language (file header,
408
+ * message, codeframe with caret, TIPS/SUGGESTIONS/NEXT STEPS, collapsible
409
+ * stack). The HTML keeps a `</body>` so callers can run `injectDevScripts`
410
+ * over it — the HMR client then pops the same payload as an overlay on load.
411
+ */
412
+ export function renderDevErrorPage(payload, opts) {
413
+ const status = opts?.status ?? 500;
414
+ const url = opts?.url ? escapeHtml(opts.url) : '';
415
+ const file = payload.file ? escapeHtml(payload.file) : 'unknown file';
416
+ const code = payload.code ? escapeHtml(payload.code) : '';
417
+ const loc = payload.line != null
418
+ ? `:${payload.line}${payload.column != null ? `:${payload.column}` : ''}`
419
+ : '';
420
+ const message = escapeHtml(payload.message || 'Unknown error');
421
+ let codeHtml = '';
422
+ const frame = payload.codeframe;
423
+ if (frame && Array.isArray(frame.code) && frame.code.length > 0) {
424
+ let maxNo = 0;
425
+ for (const l of frame.code)
426
+ if (l.no > maxNo)
427
+ maxNo = l.no;
428
+ const gutterW = String(maxNo).length;
429
+ const pad = (n) => {
430
+ let s = String(n);
431
+ while (s.length < gutterW)
432
+ s = ' ' + s;
433
+ return s;
434
+ };
435
+ const rows = [];
436
+ for (const l of frame.code) {
437
+ const marker = l.isError ? '&gt;' : '&nbsp;';
438
+ rows.push(`<div${l.isError ? ' class="err"' : ''}>${marker} <span class="ln">${pad(l.no)}</span> ${escapeHtml(l.text)}</div>`);
439
+ if (l.isError && frame.column != null && frame.column > 0) {
440
+ rows.push(`<div class="caret">${' '.repeat(gutterW + 3 + Math.max(0, frame.column - 1)).replace(/ /g, '&nbsp;')}^</div>`);
441
+ }
442
+ }
443
+ codeHtml = `<div class="code">${rows.join('\n')}</div>`;
444
+ }
445
+ const list = (title, items) => {
446
+ if (!items || items.length === 0)
447
+ return '';
448
+ return `<div class="sec"><div class="sec-t">&gt; ${escapeHtml(title)}</div>` +
449
+ items.slice(0, 6).map((t) => `<div class="item">${escapeHtml(t)}</div>`).join('') + '</div>';
450
+ };
451
+ const lists = list('TIPS', payload.tips) + list('SUGGESTIONS', payload.suggestions) + list('NEXT STEPS', payload.nextSteps);
452
+ const stack = payload.stack
453
+ ? `<details class="stack"><summary>Stack trace</summary><pre>${escapeHtml(payload.stack)}</pre></details>`
454
+ : '';
455
+ return '<!DOCTYPE html><html><head><meta charset="utf-8"><title>' + status + ' — Vesk dev</title>' +
456
+ '<style>' +
457
+ 'body{background:#000;color:#e8e8e8;font-family:ui-monospace,Menlo,Consolas,monospace;margin:0;padding:32px;}' +
458
+ '.panel{max-width:900px;margin:0 auto;border:1px solid #e8e8e8;}' +
459
+ '.head{border-bottom:1px solid #e8e8e8;padding:12px 16px;font-weight:700;letter-spacing:.08em;}' +
460
+ '.body{padding:16px;}' +
461
+ '.file{font-weight:700;margin-bottom:8px;}' +
462
+ '.msg{margin:8px 0 16px;white-space:pre-wrap;}' +
463
+ '.code{background:#0a0a0a;border:1px solid #333;padding:12px;white-space:pre;overflow-x:auto;margin-bottom:16px;}' +
464
+ '.code .ln{color:#888;}' +
465
+ '.code .err{background:#3d0a0a;}' +
466
+ '.code .caret{color:#ff5555;}' +
467
+ '.sec{margin:12px 0;}' +
468
+ '.sec-t{font-weight:700;margin-bottom:4px;}' +
469
+ '.item{padding-left:16px;color:#ccc;}' +
470
+ '.stack{margin-top:16px;color:#999;}' +
471
+ '.stack pre{white-space:pre-wrap;font-size:12px;}' +
472
+ '.foot{border-top:1px solid #e8e8e8;padding:8px 16px;color:#888;font-size:12px;letter-spacing:.08em;}' +
473
+ '</style></head><body>' +
474
+ '<div class="panel"><div class="head">&gt; SSR ERROR — ' + status + '</div>' +
475
+ '<div class="body">' +
476
+ `<div class="file">${code ? code + ' — ' : ''}${file}${loc}</div>` +
477
+ (url ? `<div class="file" style="font-weight:400;color:#888;">${url}</div>` : '') +
478
+ `<div class="msg">${message}</div>` +
479
+ codeHtml + lists + stack +
480
+ '</div><div class="foot">VESK DEV — FIX THE FILE AND SAVE TO RELOAD</div></div>' +
481
+ '</body></html>';
482
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vesk/adapter",
3
- "version": "0.2.26",
3
+ "version": "0.2.28",
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.26",
27
- "@vesk/runtime": "^0.2.26",
28
- "@vesk/types": "^0.2.26"
26
+ "@vesk/compiler": "^0.2.28",
27
+ "@vesk/runtime": "^0.2.28",
28
+ "@vesk/types": "^0.2.28"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/ws": "^8.18.1",