@webjsdev/server 0.8.41 → 0.8.43
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.
- package/package.json +1 -1
- package/src/actions.js +1 -1
- package/src/check.js +46 -7
- package/src/dev.js +16 -5
package/package.json
CHANGED
package/src/actions.js
CHANGED
|
@@ -267,7 +267,7 @@ export function serveServerOnlyStub(relPath) {
|
|
|
267
267
|
`This file is server-only (a .server.{js,ts} file with no 'use server' directive). ` +
|
|
268
268
|
`Either add 'use server' at the top of the file to expose its exports as RPC, ` +
|
|
269
269
|
`or wrap the server-only logic in a separate *.server.{js,ts} action and import that instead. ` +
|
|
270
|
-
`Docs: https://docs.webjs.
|
|
270
|
+
`Docs: https://docs.webjs.dev/docs/server-actions`;
|
|
271
271
|
return `// webjs: server-only module stub for ${relPath} (no 'use server' directive)
|
|
272
272
|
throw new Error(${JSON.stringify(msg)});
|
|
273
273
|
`;
|
package/src/check.js
CHANGED
|
@@ -1079,6 +1079,41 @@ export async function checkConventions(appDir) {
|
|
|
1079
1079
|
return violations;
|
|
1080
1080
|
}
|
|
1081
1081
|
|
|
1082
|
+
/**
|
|
1083
|
+
* BFS the module graph for the shortest import path from `from` to `to`,
|
|
1084
|
+
* returning every hop `[from, ..., to]` so the `no-server-import-in-browser-module`
|
|
1085
|
+
* message can print the FULL chain instead of an opaque `… ->` truncation (#804).
|
|
1086
|
+
* Falls back to `[from, to]` if no path is found (defensive; the caller only
|
|
1087
|
+
* calls this once `to` is known reachable).
|
|
1088
|
+
*
|
|
1089
|
+
* @param {Map<string, Set<string>>} graph
|
|
1090
|
+
* @param {string} from
|
|
1091
|
+
* @param {string} to
|
|
1092
|
+
* @returns {string[]}
|
|
1093
|
+
*/
|
|
1094
|
+
function findImportChain(graph, from, to) {
|
|
1095
|
+
const prev = new Map();
|
|
1096
|
+
const seen = new Set([from]);
|
|
1097
|
+
const queue = [from];
|
|
1098
|
+
while (queue.length) {
|
|
1099
|
+
const cur = queue.shift();
|
|
1100
|
+
const deps = graph.get(cur);
|
|
1101
|
+
if (!deps) continue;
|
|
1102
|
+
for (const dep of deps) {
|
|
1103
|
+
if (seen.has(dep)) continue;
|
|
1104
|
+
seen.add(dep);
|
|
1105
|
+
prev.set(dep, cur);
|
|
1106
|
+
if (dep === to) {
|
|
1107
|
+
const path = [];
|
|
1108
|
+
for (let n = to; n !== undefined; n = prev.get(n)) path.unshift(n);
|
|
1109
|
+
return path;
|
|
1110
|
+
}
|
|
1111
|
+
queue.push(dep);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
return [from, to];
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1082
1117
|
/**
|
|
1083
1118
|
* Implements `no-server-import-in-browser-module`. Factored into its own
|
|
1084
1119
|
* function (rather than an inline block) because it does the heavier
|
|
@@ -1241,11 +1276,12 @@ async function checkServerImportInBrowserModule(appDir, violations) {
|
|
|
1241
1276
|
// module, the chain is just the two; otherwise show one intermediate hop
|
|
1242
1277
|
// so the diagnostic points at where the edge enters (the full path is
|
|
1243
1278
|
// recoverable from the graph, but one hop is enough to locate it).
|
|
1244
|
-
const
|
|
1245
|
-
const
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1279
|
+
const chainFiles = findImportChain(moduleGraph, file, serverDep);
|
|
1280
|
+
const chain = chainFiles.map((f) => relative(appDir, f)).join(' -> ');
|
|
1281
|
+
// If the edge into the server file comes from a types-shaped module, the
|
|
1282
|
+
// idiomatic fix is to relocate that type to a browser-safe typedef (#804).
|
|
1283
|
+
const importer = chainFiles.length >= 2 ? chainFiles[chainFiles.length - 2] : null;
|
|
1284
|
+
const viaTypesModule = importer && /(^|\/)types(\.m?[jt]s$|\/)/.test(relative(appDir, importer));
|
|
1249
1285
|
|
|
1250
1286
|
// The "register the component in a layout so it elides again" remedy only
|
|
1251
1287
|
// applies to a page / layout, which CAN elide (it became browser-bound by
|
|
@@ -1253,9 +1289,12 @@ async function checkServerImportInBrowserModule(appDir, violations) {
|
|
|
1253
1289
|
// ship and are never elided, so offering them an "elides again" fix is
|
|
1254
1290
|
// wrong. Branch the fix text on whether the kind can elide.
|
|
1255
1291
|
const canElide = kind === 'page' || kind === 'layout';
|
|
1292
|
+
const typesHint = viaTypesModule
|
|
1293
|
+
? `The edge enters via a types-shaped module (${relative(appDir, importer)}); if it re-exports a runtime VALUE from a \`.server.{ts,js}\` file, relocate that to a browser-safe typedef (a plain \`interface\` / JSDoc, or an \`import type\` which the stripper erases) so the type is shared without pinning the module. `
|
|
1294
|
+
: '';
|
|
1256
1295
|
const fixText = canElide
|
|
1257
|
-
?
|
|
1258
|
-
:
|
|
1296
|
+
? `${typesHint}Keep the server call off this browser-shipped ${kind}. Options: (1) gate the route in \`middleware.ts\` (runs server-side, never ships); (2) move the server-only call behind a \`'use server'\` action in a \`.server.{ts,js}\` file and call it as an RPC; or (3) if this ${kind} only became browser-bound because it imports a component to register, register that component in a \`layout.{ts,js}\` instead so the ${kind} elides again and its server import is stripped.`
|
|
1297
|
+
: `${typesHint}Keep the server call off this browser-shipped ${kind} (it always ships and is never elided). Options: (1) gate the route in \`middleware.ts\` (runs server-side, never ships); or (2) move the server-only call behind a \`'use server'\` action in a \`.server.{ts,js}\` file and call it as an RPC.`;
|
|
1259
1298
|
|
|
1260
1299
|
violations.push({
|
|
1261
1300
|
rule: 'no-server-import-in-browser-module',
|
package/src/dev.js
CHANGED
|
@@ -1825,9 +1825,14 @@ async function handleCore(req, ctx) {
|
|
|
1825
1825
|
return invokeAction(state.actionIndex, actMatch[1], actMatch[2], req, onActionError, allowedOrigins);
|
|
1826
1826
|
}
|
|
1827
1827
|
|
|
1828
|
-
// Static: /public
|
|
1829
|
-
|
|
1830
|
-
|
|
1828
|
+
// Static: /public/*, plus a small set of ROOT assets that must serve at the
|
|
1829
|
+
// site root even though they live under public/. A service worker registered
|
|
1830
|
+
// at /sw.js scopes to the origin root, so it MUST serve at / (not
|
|
1831
|
+
// /public/sw.js), and so must its offline fallback. Same remap shape as the
|
|
1832
|
+
// /favicon.ico special-case below. (#830)
|
|
1833
|
+
const ROOT_ASSETS = { '/sw.js': '/public/sw.js', '/offline.html': '/public/offline.html' };
|
|
1834
|
+
if (path.startsWith('/public/') || path === '/favicon.ico' || path in ROOT_ASSETS) {
|
|
1835
|
+
const p = path === '/favicon.ico' ? '/public/favicon.ico' : (ROOT_ASSETS[path] || path);
|
|
1831
1836
|
const abs = join(appDir, p);
|
|
1832
1837
|
// Containment check. `join` normalises `..` segments, so a path
|
|
1833
1838
|
// like `/public/%2E%2E/secret/x.svg` decodes (after URL parsing,
|
|
@@ -1843,7 +1848,13 @@ async function handleCore(req, ctx) {
|
|
|
1843
1848
|
return new Response(null, { status: 404 });
|
|
1844
1849
|
}
|
|
1845
1850
|
// A `?v=<hash>` public asset is content-addressed -> immutable (#243).
|
|
1846
|
-
if (await exists(abs))
|
|
1851
|
+
if (await exists(abs)) {
|
|
1852
|
+
const res = await fileResponse(abs, { dev, immutable: versioned });
|
|
1853
|
+
// A worker served below its registration path only controls that subtree
|
|
1854
|
+
// unless the response opts it up to the root scope. (#830)
|
|
1855
|
+
if (path === '/sw.js') res.headers.set('Service-Worker-Allowed', '/');
|
|
1856
|
+
return res;
|
|
1857
|
+
}
|
|
1847
1858
|
}
|
|
1848
1859
|
|
|
1849
1860
|
// User source modules (served as ES modules, with action-file rewriting).
|
|
@@ -2369,7 +2380,7 @@ async function tsResponse(abs, dev, elideOpts, cache, immutable, reportDevError)
|
|
|
2369
2380
|
`Replace enum / namespace / parameter-property / legacy-decorator / ` +
|
|
2370
2381
|
`import = require constructs with their erasable equivalents. ` +
|
|
2371
2382
|
`Run \`webjs check\` for guidance (no-non-erasable-typescript rule). ` +
|
|
2372
|
-
`Docs: https://docs.webjs.
|
|
2383
|
+
`Docs: https://docs.webjs.dev/docs/typescript`
|
|
2373
2384
|
// Prod: terse, no path leak, no Node-message leak (Node's
|
|
2374
2385
|
// message can include source snippets). Operators get the
|
|
2375
2386
|
// detail in server logs above.
|