@vesk/vesk-cli 0.2.69 → 0.2.70

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 +104 -40
  2. package/package.json +5 -5
package/dist/cli.js CHANGED
@@ -49909,11 +49909,26 @@ function resolveCssUrls(opts) {
49909
49909
 
49910
49910
  // ../adapter/src/ssr-function.ts
49911
49911
  init_default_pages();
49912
- function resolveErrorFile(sourceDir, appDir) {
49912
+ function resolveBoundaryFile(sourceDir, appDir, file) {
49913
49913
  const rel = relative(appDir, sourceDir).split("/").filter(Boolean);
49914
49914
  for (let depth = rel.length; depth >= 0; depth--) {
49915
49915
  const dir = depth === 0 ? appDir : join5(appDir, ...rel.slice(0, depth));
49916
- const p = join5(dir, "error.vsk");
49916
+ const p = join5(dir, file);
49917
+ if (existsSync8(p)) return p;
49918
+ }
49919
+ return null;
49920
+ }
49921
+ function resolveErrorFile(sourceDir, appDir) {
49922
+ return resolveBoundaryFile(sourceDir, appDir, "error.vsk");
49923
+ }
49924
+ function resolveNotFoundFile(sourceDir, appDir) {
49925
+ return resolveBoundaryFile(sourceDir, appDir, "not-found.vsk");
49926
+ }
49927
+ function resolveBoundaryFileForUrl(pathname, appDir, file) {
49928
+ const segments = pathname.split("/").filter(Boolean);
49929
+ for (let depth = segments.length; depth >= 0; depth--) {
49930
+ const dir = join5(appDir, ...segments.slice(0, depth));
49931
+ const p = join5(dir, file);
49917
49932
  if (existsSync8(p)) return p;
49918
49933
  }
49919
49934
  return null;
@@ -50053,6 +50068,12 @@ function generateSsrFunction(routeNode, appDir, outDir, componentMap, options) {
50053
50068
  const errorVars = errorPath ? `const _errorComp = ${JSON.stringify(errorComp)};
50054
50069
  const _errorCompiled = ${hydrateInvoke(precompilePlan(errorPath))};
50055
50070
  ` : "const _errorComp = null;\nconst _errorCompiled = null;\n";
50071
+ const notFoundPath = resolveNotFoundFile(routeNode.sourceDir, appDir);
50072
+ const notFoundSrc = notFoundPath ? readFileSync7(notFoundPath, "utf-8") : null;
50073
+ const notFoundComp = notFoundPath ? extractCompName(notFoundSrc) || "NotFound" : null;
50074
+ const notFoundVars = notFoundPath ? `const _notFoundComp = ${JSON.stringify(notFoundComp)};
50075
+ const _notFoundCompiled = ${hydrateInvoke(precompilePlan(notFoundPath))};
50076
+ ` : "const _notFoundComp = null;\nconst _notFoundCompiled = null;\n";
50056
50077
  let src2 = "";
50057
50078
  if (layoutDecls.length > 0) {
50058
50079
  src2 = `const _pageComp = ${JSON.stringify(pageComp)};
@@ -50065,13 +50086,13 @@ const _errorCompiled = ${hydrateInvoke(precompilePlan(errorPath))};
50065
50086
  ${layoutDecls.map((d) => " " + d.compiled + ",").join("\n")}
50066
50087
  ];
50067
50088
  `;
50068
- src2 += errorVars;
50089
+ src2 += errorVars + notFoundVars;
50069
50090
  } else {
50070
50091
  src2 = `const _comp = ${JSON.stringify(pageComp)};
50071
50092
  `;
50072
50093
  src2 += `const _compiled = (() => { try { return hydratePrecompile(${pagePlan}); } catch { return null; } })();
50073
50094
  `;
50074
- src2 += errorVars;
50095
+ src2 += errorVars + notFoundVars;
50075
50096
  }
50076
50097
  const urlParts = routeNode.fullPath.split("/").filter(Boolean);
50077
50098
  const paramExprs = buildParamExtraction(routeNode, urlParts);
@@ -50113,30 +50134,66 @@ ${layoutDecls.map((d) => " " + d.compiled + ",").join("\n")}
50113
50134
  " }",
50114
50135
  "}",
50115
50136
  "",
50137
+ // Returns null rather than throwing when there is no boundary, so the
50138
+ // caller can re-throw the original NotFoundError and let the server answer
50139
+ // — a page with no not-found.vsk anywhere must still get a 404.
50140
+ "async function __renderNotFoundBody(props) {",
50141
+ " if (!_notFoundComp) return null;",
50142
+ " try {",
50143
+ " const result = await renderPage('', _notFoundComp, props, __componentRegistry, { hydrate: true, cached: _notFoundCompiled });",
50144
+ " return result.body;",
50145
+ " } catch {",
50146
+ " return null;",
50147
+ " }",
50148
+ "}",
50149
+ "",
50150
+ // The layout wrap, shared by the page / error / not-found paths. Extracted
50151
+ // because three call sites that each re-implemented it is three chances to
50152
+ // forget the head merge.
50153
+ "async function __wrapLayouts(params, body, head) {",
50154
+ " let _b = body;",
50155
+ ' let _h = head || "";',
50156
+ " for (let _i = _layoutCompList.length - 1; _i > 0; _i--) {",
50157
+ " const _inner = await renderPage('', _layoutCompList[_i], { params, children: _b }, __componentRegistry, { hydrate: true, cached: _layoutCompiledList[_i] });",
50158
+ " _b = _inner.body;",
50159
+ " if (_inner.head) _h = _inner.head + _h;",
50160
+ " }",
50161
+ " return renderFullPage('', _layoutCompList[0], { params, children: _b }, __componentRegistry, { hydrate: true, cached: _layoutCompiledList[0]" + bakedOptions + clientScriptOption + dataScriptOption + ", pageHead: _h });",
50162
+ "}",
50163
+ "",
50116
50164
  "async function __renderHtml(params, requestUrl) {",
50117
50165
  " return withSsrStore(async () => {",
50118
50166
  " resetPersistedStoreKeys();",
50119
50167
  " let page;",
50120
50168
  " let caughtError = null;",
50169
+ " let notFound = false;",
50121
50170
  " const __expose = process.env.NODE_ENV !== 'production';",
50122
50171
  " try {",
50123
50172
  " page = await renderPage('', _pageComp, { params }, __componentRegistry, { hydrate: true, cached: _pageCompiled });",
50124
50173
  " } catch (err) {",
50125
- " if (err && (err.name === 'NotFoundError' || err.name === 'Redirect')) throw err;",
50126
- " caughtError = err;",
50127
- " const message = err && typeof err === 'object' && 'message' in err ? String(err.message) : String(err);",
50128
- " const stack = err && typeof err === 'object' && 'stack' in err ? String(err.stack) : '';",
50129
- " page = { body: await __renderErrorBody({ params, statusCode: 500, error: __expose ? message : 'Internal Server Error', stack: __expose ? stack : '', url: requestUrl || '' }), head: '' };",
50174
+ " if (err && err.name === 'Redirect') throw err;",
50175
+ " if (err && err.name === 'NotFoundError') {",
50176
+ // The boundary is chosen per-route at build time (resolveNotFoundFile walks
50177
+ // this route's own directory up to the app root), so a nested
50178
+ // app/admin/not-found.vsk answers /admin/* and the root one answers the
50179
+ // rest — matching the client router's deepest-first chain walk.
50180
+ " const nfBody = await __renderNotFoundBody({ params, url: requestUrl || '' });",
50181
+ " if (nfBody !== null) {",
50182
+ " notFound = true;",
50183
+ " page = { body: nfBody, head: '' };",
50184
+ " } else {",
50185
+ " throw err;",
50186
+ " }",
50187
+ " } else {",
50188
+ " caughtError = err;",
50189
+ " const message = err && typeof err === 'object' && 'message' in err ? String(err.message) : String(err);",
50190
+ " const stack = err && typeof err === 'object' && 'stack' in err ? String(err.stack) : '';",
50191
+ " page = { body: await __renderErrorBody({ params, statusCode: 500, error: __expose ? message : 'Internal Server Error', stack: __expose ? stack : '', url: requestUrl || '' }), head: '' };",
50192
+ " }",
50130
50193
  " }",
50131
50194
  " let _body = (caughtError ? '<!--vesk-ssr-error:' + (caughtError && typeof caughtError === 'object' && 'message' in caughtError ? encodeURIComponent(__expose ? String(caughtError.message) : 'Internal Server Error') : '') + '-->' : '') + page.body;",
50132
- " let _head = page.head || '';",
50133
- " for (let _i = _layoutCompList.length - 1; _i > 0; _i--) {",
50134
- " const _inner = await renderPage('', _layoutCompList[_i], { params, children: _body }, __componentRegistry, { hydrate: true, cached: _layoutCompiledList[_i] });",
50135
- " _body = _inner.body;",
50136
- " if (_inner.head) _head = _inner.head + _head;",
50137
- " }",
50138
- " const html = await renderFullPage('', _layoutCompList[0], { params, children: _body }, __componentRegistry, { hydrate: true, cached: _layoutCompiledList[0]" + bakedOptions + clientScriptOption + dataScriptOption + ", pageHead: _head });",
50139
- " return new Response(__injectStoreInit(html), { headers: { 'Content-Type': 'text/html' }, status: caughtError ? 500 : 200 });",
50195
+ " const html = await __wrapLayouts(params, _body, page.head || '');",
50196
+ " return new Response(__injectStoreInit(html), { headers: { 'Content-Type': 'text/html' }, status: notFound ? 404 : (caughtError ? 500 : 200) });",
50140
50197
  " });",
50141
50198
  "}",
50142
50199
  ""
@@ -52505,6 +52562,7 @@ function generateManifest(routes, ssrRoutes, apiRoutes, staticRoutes, middleware
52505
52562
  if (r._isrTags) entry.tags = r._isrTags;
52506
52563
  routeEntries.push(entry);
52507
52564
  }
52565
+ routeEntries.sort((a, b) => compareSpecificity(a.path, b.path));
52508
52566
  const apiEntries = [];
52509
52567
  for (const r of apiRoutes) {
52510
52568
  const urlParts = r.fullPath.split("/").filter(Boolean);
@@ -53430,17 +53488,29 @@ async function startProdServer(outDir, options) {
53430
53488
  const pageCssUrls = resolveCssUrls({
53431
53489
  enabled: hasBuiltGlobalCss(outDir)
53432
53490
  });
53433
- const nfPath = resolve14(appDir, "not-found.vsk");
53434
- let notFoundHtml = null;
53435
- if (existsSync16(nfPath)) {
53436
- try {
53437
- const runtimePath = resolve14(outDir, "server", "runtime.js");
53438
- const { renderFullPage: renderFullPage2, storeDataScriptGlobal } = await import(runtimePath);
53439
- const src2 = readFileSync18(nfPath, "utf-8");
53440
- const compName = resolveComponentName(src2) || "NotFound";
53441
- notFoundHtml = await renderFullPage2(src2, compName, { params: {}, url: url.pathname }, /* @__PURE__ */ new Map(), { hydrate: true, cssUrls: pageCssUrls, security: securityConfig?.security || {}, externalDataScript: storeDataScriptGlobal, sourcePath: nfPath, headExtra: bakedHeadExtra });
53442
- } catch {
53491
+ const boundaryCache = /* @__PURE__ */ new Map();
53492
+ async function renderBoundaryFile(file, pathname, props) {
53493
+ const boundaryPath = resolveBoundaryFileForUrl(pathname, appDir, file);
53494
+ if (!boundaryPath) return null;
53495
+ const key = `${file}:${boundaryPath}`;
53496
+ let html = boundaryCache.has(key) ? boundaryCache.get(key) : null;
53497
+ if (html === null) {
53498
+ try {
53499
+ const runtimePath = resolve14(outDir, "server", "runtime.js");
53500
+ const { renderFullPage: renderFullPage2, storeDataScriptGlobal } = await import(runtimePath);
53501
+ const src2 = readFileSync18(boundaryPath, "utf-8");
53502
+ const compName = resolveComponentName(src2) || (file === "error.vsk" ? "Error" : "NotFound");
53503
+ html = await renderFullPage2(src2, compName, props, /* @__PURE__ */ new Map(), { hydrate: true, cssUrls: pageCssUrls, clientScriptUrl: buildConfig?.assets?.client?.url || "/_vesk/static/client.js", clientScriptIntegrity: buildConfig?.assets?.client?.integrity, security: securityConfig?.security || {}, externalDataScript: storeDataScriptGlobal, sourcePath: boundaryPath, headExtra: bakedHeadExtra });
53504
+ } catch {
53505
+ return null;
53506
+ }
53443
53507
  }
53508
+ return html;
53509
+ }
53510
+ let notFoundHtml = null;
53511
+ {
53512
+ const rendered = await renderBoundaryFile("not-found.vsk", url.pathname, { params: {}, url: url.pathname });
53513
+ notFoundHtml = rendered;
53444
53514
  }
53445
53515
  for (const route of buildConfig.routes) {
53446
53516
  if (route.type === "ssr") {
@@ -53510,19 +53580,13 @@ async function startProdServer(outDir, options) {
53510
53580
  if (eventsMod) {
53511
53581
  await executeError(err, { url: url.href, routePath: route.path, target: "node", status: 500 }, eventsMod);
53512
53582
  }
53513
- const errPath = resolve14(appDir, "error.vsk");
53514
- let errorHtml = null;
53515
- if (existsSync16(errPath)) {
53516
- try {
53517
- const runtimePath = resolve14(outDir, "server", "runtime.js");
53518
- const { renderFullPage: renderFullPage2, storeDataScriptGlobal } = await import(runtimePath);
53519
- const src2 = readFileSync18(errPath, "utf-8");
53520
- const compName = resolveComponentName(src2) || "Error";
53521
- const expose = process.env.NODE_ENV !== "production";
53522
- errorHtml = await renderFullPage2(src2, compName, { error: expose ? err.message : "Internal Server Error", stack: expose ? err.stack : "", statusCode: 500, url: url.pathname }, /* @__PURE__ */ new Map(), { hydrate: true, cssUrls: pageCssUrls, clientScriptUrl: buildConfig?.assets?.client?.url || "/_vesk/static/client.js", clientScriptIntegrity: buildConfig?.assets?.client?.integrity, security: securityConfig?.security || {}, externalDataScript: storeDataScriptGlobal, sourcePath: errPath, headExtra: bakedHeadExtra });
53523
- } catch {
53524
- }
53525
- }
53583
+ const expose = process.env.NODE_ENV !== "production";
53584
+ const errorHtml = await renderBoundaryFile("error.vsk", url.pathname, {
53585
+ error: expose ? err.message : "Internal Server Error",
53586
+ stack: expose ? err.stack : "",
53587
+ statusCode: 500,
53588
+ url: url.pathname
53589
+ });
53526
53590
  res.writeHead(500, { "Content-Type": "text/html" });
53527
53591
  res.end(errorHtml || renderDefaultErrorPage({ message: process.env.NODE_ENV !== "production" ? err.message : void 0, url: url.pathname, headExtra: bakedHeadExtra }));
53528
53592
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vesk/vesk-cli",
3
- "version": "0.2.69",
3
+ "version": "0.2.70",
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.69",
12
- "@vesk/compiler": "^0.2.69",
13
- "@vesk/plugin-tailwind": "^0.2.69",
14
- "@vesk/runtime": "^0.2.69",
11
+ "@vesk/adapter": "^0.2.70",
12
+ "@vesk/compiler": "^0.2.70",
13
+ "@vesk/plugin-tailwind": "^0.2.70",
14
+ "@vesk/runtime": "^0.2.70",
15
15
  "esrap": "^2.3.0",
16
16
  "typescript": "^5.8.0",
17
17
  "ws": "^8.18.0",