@nuxt/nitro-server 3.21.0 → 3.21.2
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/README.md +5 -5
- package/dist/THIRD-PARTY-LICENSES.md +11 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +62 -59
- package/dist/runtime/handlers/error.mjs +2 -2
- package/dist/runtime/handlers/island.d.mts +3 -2
- package/dist/runtime/handlers/island.mjs +21 -9
- package/dist/runtime/handlers/renderer.d.mts +3 -2
- package/dist/runtime/handlers/renderer.mjs +2 -1
- package/dist/runtime/middleware/no-ssr.d.mts +3 -2
- package/dist/runtime/middleware/no-ssr.mjs +2 -1
- package/dist/runtime/plugins/dev-server-logs.d.mts +2 -1
- package/dist/runtime/utils/cache.d.mts +11 -5
- package/dist/runtime/utils/config.d.mts +1 -1
- package/dist/runtime/utils/dev.mjs +1 -1
- package/dist/runtime/utils/error.d.mts +1 -1
- package/dist/runtime/utils/error.mjs +1 -1
- package/dist/runtime/utils/renderer/app.mjs +1 -2
- package/dist/runtime/utils/renderer/build-files.d.mts +2 -4
- package/dist/runtime/utils/renderer/payload.mjs +21 -4
- package/package.json +15 -15
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
<a href="https://nuxt.com"><img width="830" height="213" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/banner.svg" alt="Nuxt banner"></a>
|
|
2
2
|
|
|
3
3
|
# Nuxt
|
|
4
4
|
|
|
5
5
|
<p>
|
|
6
|
-
<a href="https://
|
|
7
|
-
<a href="https://
|
|
6
|
+
<a href="https://npmx.dev/package/nuxt"><img src="https://npmx.dev/api/registry/badge/version/nuxt" alt="Version"></a>
|
|
7
|
+
<a href="https://npmx.dev/package/nuxt"><img src="https://npmx.dev/api/registry/badge/downloads/nuxt" alt="Downloads"></a>
|
|
8
8
|
<a href="https://github.com/nuxt/nuxt/blob/main/LICENSE"><img src="https://img.shields.io/github/license/nuxt/nuxt.svg?style=flat&colorA=18181B&colorB=28CF8D" alt="License"></a>
|
|
9
9
|
<a href="https://nuxt.com/modules"><img src="https://img.shields.io/badge/dynamic/json?url=https://nuxt.com/api/v1/modules&query=$.stats.modules&label=Modules&style=flat&colorA=18181B&colorB=28CF8D" alt="Modules"></a>
|
|
10
10
|
<a href="https://nuxt.com"><img src="https://img.shields.io/badge/Nuxt%20Docs-18181B?logo=nuxt" alt="Website"></a>
|
|
11
11
|
<a href="https://chat.nuxt.dev"><img src="https://img.shields.io/badge/Nuxt%20Discord-18181B?logo=discord" alt="Discord"></a>
|
|
12
|
-
<a href="https://securityscorecards.dev/"><img src="https://api.securityscorecards.dev/projects/github.com/nuxt/nuxt/badge" alt="Nuxt openssf scorecard score"></a>
|
|
12
|
+
<a href="https://securityscorecards.dev/viewer/?uri=github.com/nuxt/nuxt"><img src="https://api.securityscorecards.dev/projects/github.com/nuxt/nuxt/badge" alt="Nuxt openssf scorecard score"></a>
|
|
13
13
|
<a href="https://deepwiki.com/nuxt/nuxt"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
|
|
14
14
|
</p>
|
|
15
15
|
|
|
@@ -111,7 +111,7 @@ Follow the docs to [Set Up Your Local Development Environment](https://nuxt.com/
|
|
|
111
111
|
## <a name="follow-us">🔗 Follow Us</a>
|
|
112
112
|
|
|
113
113
|
<p valign="center">
|
|
114
|
-
<a href="https://go.nuxt.com/discord"><img width="
|
|
114
|
+
<a href="https://go.nuxt.com/discord"><img width="20" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/discord.svg" alt="Discord"></a> <a href="https://go.nuxt.com/x"><img width="20" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/twitter.svg" alt="Twitter"></a> <a href="https://go.nuxt.com/github"><img width="20" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/github.svg" alt="GitHub"></a> <a href="https://go.nuxt.com/bluesky"><img width="20" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/bluesky.svg" alt="Bluesky"></a>
|
|
115
115
|
</p>
|
|
116
116
|
|
|
117
117
|
## <a name="license">⚖️ License</a>
|
package/dist/index.d.mts
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -19,10 +19,8 @@ import { isWindows } from "std-env";
|
|
|
19
19
|
import { ImpoundPlugin } from "impound";
|
|
20
20
|
import { resolveModulePath } from "exsolve";
|
|
21
21
|
import { runtimeDependencies } from "nitropack/runtime/meta";
|
|
22
|
-
|
|
23
22
|
//#region package.json
|
|
24
|
-
var version = "3.21.
|
|
25
|
-
|
|
23
|
+
var version = "3.21.2";
|
|
26
24
|
//#endregion
|
|
27
25
|
//#region src/utils.ts
|
|
28
26
|
function toArray(value) {
|
|
@@ -31,13 +29,11 @@ function toArray(value) {
|
|
|
31
29
|
let _distDir = dirname(fileURLToPath(import.meta.url));
|
|
32
30
|
if (/(?:chunks|shared)$/.test(_distDir)) _distDir = dirname(_distDir);
|
|
33
31
|
const distDir = _distDir;
|
|
34
|
-
|
|
35
32
|
//#endregion
|
|
36
33
|
//#region ../ui-templates/dist/templates/spa-loading-icon.ts
|
|
37
34
|
const template = () => {
|
|
38
35
|
return "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"80\" fill=\"none\" class=\"nuxt-spa-loading\" viewBox=\"0 0 37 25\"><path d=\"M24.236 22.006h10.742L25.563 5.822l-8.979 14.31a4 4 0 0 1-3.388 1.874H2.978l11.631-20 5.897 10.567\"/></svg><style>.nuxt-spa-loading{left:50%;position:fixed;top:50%;transform:translate(-50%,-50%)}.nuxt-spa-loading>path{animation:nuxt-spa-loading-move 3s linear infinite;fill:none;stroke:#00dc82;stroke-dasharray:128;stroke-dashoffset:128;stroke-linecap:round;stroke-linejoin:round;stroke-width:4px}@keyframes nuxt-spa-loading-move{to{stroke-dashoffset:-128}}</style>";
|
|
39
36
|
};
|
|
40
|
-
|
|
41
37
|
//#endregion
|
|
42
38
|
//#region ../nuxt/src/core/plugins/import-protection.ts
|
|
43
39
|
function createImportProtectionPatterns(nuxt, options) {
|
|
@@ -46,7 +42,7 @@ function createImportProtectionPatterns(nuxt, options) {
|
|
|
46
42
|
patterns.push([/^(nuxt|nuxt3|nuxt-nightly)$/, `\`nuxt\`, or \`nuxt-nightly\` cannot be imported directly in ${context}.` + (options.context === "nuxt-app" ? " Instead, import runtime Nuxt composables from `#app` or `#imports`." : "")]);
|
|
47
43
|
patterns.push([/^((~|~~|@|@@)?\/)?nuxt\.config(\.|$)/, "Importing directly from a `nuxt.config` file is not allowed. Instead, use runtime config or a module."]);
|
|
48
44
|
patterns.push([/(^|node_modules\/)@vue\/composition-api/]);
|
|
49
|
-
for (const mod of nuxt.options._installedModules) if (mod.entryPath) patterns.push([
|
|
45
|
+
for (const mod of nuxt.options._installedModules) if (mod.entryPath) patterns.push([new RegExp(`^${escapeRE(mod.entryPath)}$`), "Importing directly from module entry-points is not allowed."]);
|
|
50
46
|
for (const i of [
|
|
51
47
|
/(^|node_modules\/)@nuxt\/(cli|kit|test-utils)/,
|
|
52
48
|
/(^|node_modules\/)nuxi/,
|
|
@@ -55,7 +51,7 @@ function createImportProtectionPatterns(nuxt, options) {
|
|
|
55
51
|
]) patterns.push([i, `This module cannot be imported in ${context}.`]);
|
|
56
52
|
if (options.context === "nitro-app" || options.context === "shared") for (const i of ["#app", /^#build(\/|$)/]) patterns.push([i, `Vue app aliases are not allowed in ${context}.`]);
|
|
57
53
|
if (options.context === "nuxt-app" || options.context === "shared") {
|
|
58
|
-
patterns.push([
|
|
54
|
+
patterns.push([new RegExp(escapeRE(relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, nuxt.options.serverDir || "server"))) + "\\/(api|routes|middleware|plugins)\\/"), `Importing from server is not allowed in ${context}.`]);
|
|
59
55
|
patterns.push([/^#server(\/|$)/, `Server aliases are not allowed in ${context}.`]);
|
|
60
56
|
}
|
|
61
57
|
return patterns;
|
|
@@ -65,7 +61,6 @@ const contextFlags = {
|
|
|
65
61
|
"nuxt-app": "the Vue part of your app",
|
|
66
62
|
"shared": "the #shared directory"
|
|
67
63
|
};
|
|
68
|
-
|
|
69
64
|
//#endregion
|
|
70
65
|
//#region src/templates.ts
|
|
71
66
|
const nitroSchemaTemplate = {
|
|
@@ -77,12 +72,9 @@ const nitroSchemaTemplate = {
|
|
|
77
72
|
references,
|
|
78
73
|
declarations
|
|
79
74
|
});
|
|
80
|
-
const
|
|
75
|
+
const typesDir = join(nuxt.options.buildDir, "types");
|
|
81
76
|
return `
|
|
82
|
-
${[...references.map((ref) =>
|
|
83
|
-
if ("path" in ref && isAbsolute(ref.path)) ref.path = relative(sourceDir, ref.path);
|
|
84
|
-
return `/// <reference ${renderAttrs(ref)} />`;
|
|
85
|
-
}), ...declarations].join("\n")}
|
|
77
|
+
${[...references.map((ref) => renderReference(ref, typesDir)), ...declarations].join("\n")}
|
|
86
78
|
/// <reference path="./schema.d.ts" />
|
|
87
79
|
|
|
88
80
|
import type { RuntimeConfig } from 'nuxt/schema'
|
|
@@ -119,15 +111,9 @@ declare module 'nitropack' {
|
|
|
119
111
|
`;
|
|
120
112
|
}
|
|
121
113
|
};
|
|
122
|
-
function
|
|
123
|
-
return
|
|
124
|
-
}
|
|
125
|
-
function renderAttrs(obj) {
|
|
126
|
-
const attrs = [];
|
|
127
|
-
for (const key in obj) attrs.push(renderAttr(key, obj[key]));
|
|
128
|
-
return attrs.join(" ");
|
|
114
|
+
function renderReference(ref, baseDir) {
|
|
115
|
+
return `/// <reference ${"path" in ref ? `path="${isAbsolute(ref.path) ? relative(baseDir, ref.path) : ref.path}"` : `types="${ref.types}"`} />`;
|
|
129
116
|
}
|
|
130
|
-
|
|
131
117
|
//#endregion
|
|
132
118
|
//#region src/index.ts
|
|
133
119
|
const logLevelMapReverse = {
|
|
@@ -146,7 +132,7 @@ async function bundle(nuxt) {
|
|
|
146
132
|
}
|
|
147
133
|
const layerPublicAssetsDirs = [];
|
|
148
134
|
for (const dirs of layerDirs) if (existsSync(dirs.public)) layerPublicAssetsDirs.push({ dir: dirs.public });
|
|
149
|
-
const excludePattern = excludePaths.length ? [
|
|
135
|
+
const excludePattern = excludePaths.length ? [new RegExp(`node_modules\\/(?!${excludePaths.join("|")})`)] : [/node_modules/];
|
|
150
136
|
const rootDirWithSlash = withTrailingSlash(nuxt.options.rootDir);
|
|
151
137
|
const moduleEntryPaths = [];
|
|
152
138
|
for (const m of nuxt.options._installedModules) {
|
|
@@ -376,7 +362,8 @@ async function bundle(nuxt) {
|
|
|
376
362
|
"appLayout",
|
|
377
363
|
"cache",
|
|
378
364
|
"isr",
|
|
379
|
-
"swr"
|
|
365
|
+
"swr",
|
|
366
|
+
"ssr"
|
|
380
367
|
];
|
|
381
368
|
function getRouteRulesRouter() {
|
|
382
369
|
const routeRulesRouter = createRouter();
|
|
@@ -425,18 +412,19 @@ async function bundle(nuxt) {
|
|
|
425
412
|
}
|
|
426
413
|
});
|
|
427
414
|
if (nuxt.options.experimental.payloadExtraction) {
|
|
428
|
-
if (nuxt.options.dev) nuxt.hook("nitro:config", (nitroConfig
|
|
429
|
-
nitroConfig
|
|
430
|
-
nitroConfig
|
|
431
|
-
nitroConfig
|
|
432
|
-
for (const route of nitroConfig
|
|
415
|
+
if (nuxt.options.dev) nuxt.hook("nitro:config", (nitroConfig) => {
|
|
416
|
+
nitroConfig.prerender ||= {};
|
|
417
|
+
nitroConfig.prerender.routes ||= [];
|
|
418
|
+
nitroConfig.routeRules ||= {};
|
|
419
|
+
for (const route of nitroConfig.prerender.routes) {
|
|
433
420
|
if (!route) continue;
|
|
434
|
-
nitroConfig
|
|
421
|
+
nitroConfig.routeRules[route] = defu(nitroConfig.routeRules[route], { prerender: true });
|
|
435
422
|
}
|
|
436
423
|
});
|
|
437
|
-
nuxt.hook("nitro:init", (nitro
|
|
438
|
-
nitro
|
|
439
|
-
for (const [route, value] of Object.entries(nitro
|
|
424
|
+
nuxt.hook("nitro:init", (nitro) => {
|
|
425
|
+
nitro.hooks.hook("build:before", (nitro) => {
|
|
426
|
+
for (const [route, value] of Object.entries(nitro.options.routeRules)) if (!route.endsWith("*") && !route.endsWith("/_payload.json")) {
|
|
427
|
+
if (value.ssr === false) continue;
|
|
440
428
|
if (value.isr || value.cache || value.prerender && nuxt.options.dev) {
|
|
441
429
|
const payloadKey = route + "/_payload.json";
|
|
442
430
|
const defaults = {};
|
|
@@ -445,7 +433,7 @@ async function bundle(nuxt) {
|
|
|
445
433
|
"cache",
|
|
446
434
|
...nuxt.options.dev ? ["prerender"] : []
|
|
447
435
|
]) if (key in value) defaults[key] = value[key];
|
|
448
|
-
nitro
|
|
436
|
+
nitro.options.routeRules[payloadKey] = defu(nitro.options.routeRules[payloadKey], defaults);
|
|
449
437
|
}
|
|
450
438
|
}
|
|
451
439
|
});
|
|
@@ -483,13 +471,13 @@ async function bundle(nuxt) {
|
|
|
483
471
|
config.alias ||= {};
|
|
484
472
|
config.alias["#app-manifest"] = join(tempDir, `meta/${buildId}.json`);
|
|
485
473
|
});
|
|
486
|
-
nuxt.hook("nitro:init", (nitro
|
|
487
|
-
nitro
|
|
474
|
+
nuxt.hook("nitro:init", (nitro) => {
|
|
475
|
+
nitro.hooks.hook("rollup:before", async (nitro) => {
|
|
488
476
|
const prerenderedRoutes = /* @__PURE__ */ new Set();
|
|
489
477
|
const routeRulesMatcher = getRouteRulesRouter();
|
|
490
|
-
if (nitro
|
|
478
|
+
if (nitro._prerenderedRoutes?.length) {
|
|
491
479
|
const payloadSuffix = nuxt.options.experimental.renderJsonPayloads ? "/_payload.json" : "/_payload.js";
|
|
492
|
-
for (const route of nitro
|
|
480
|
+
for (const route of nitro._prerenderedRoutes) if (!route.error && route.route.endsWith(payloadSuffix)) {
|
|
493
481
|
const url = route.route.slice(0, -payloadSuffix.length) || "/";
|
|
494
482
|
if (!defu({}, ...findAllRoutes(routeRulesMatcher, void 0, url).reverse()).prerender) prerenderedRoutes.add(url);
|
|
495
483
|
}
|
|
@@ -531,8 +519,8 @@ async function bundle(nuxt) {
|
|
|
531
519
|
const relativeSharedDir = withTrailingSlash(relative(nuxt.options.rootDir, resolve(nuxt.options.rootDir, nuxt.options.dir.shared)));
|
|
532
520
|
const sharedPatterns = [
|
|
533
521
|
/^#shared\//,
|
|
534
|
-
|
|
535
|
-
|
|
522
|
+
new RegExp("^" + escapeRE(sharedDir)),
|
|
523
|
+
new RegExp("^" + escapeRE(relativeSharedDir))
|
|
536
524
|
];
|
|
537
525
|
nitroConfig.rollupConfig.plugins.push(ImpoundPlugin.rollup({
|
|
538
526
|
cwd: nuxt.options.rootDir,
|
|
@@ -623,6 +611,22 @@ async function bundle(nuxt) {
|
|
|
623
611
|
await nuxt.callHook("nitro:init", nitro);
|
|
624
612
|
nuxt["~runtimeDependencies"] ||= [];
|
|
625
613
|
nuxt["~runtimeDependencies"].push(...runtimeDependencies, "unhead", "@unhead/vue", "@nuxt/devalue", "unstorage", ...nitro.options.inlineDynamicImports ? ["vue", "@vue/server-renderer"] : []);
|
|
614
|
+
addVitePlugin({
|
|
615
|
+
name: "nuxt:nitro:ssr-conditions",
|
|
616
|
+
configEnvironment(name, config) {
|
|
617
|
+
if (name === "ssr") {
|
|
618
|
+
config.resolve ||= {};
|
|
619
|
+
config.resolve.conditions = [...nitro.options.exportConditions || [], "import"];
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
addVitePlugin({
|
|
624
|
+
name: "nuxt:nitro:vue-feature-flags",
|
|
625
|
+
applyToEnvironment: (environment) => environment.name === "ssr" && environment.config.isProduction,
|
|
626
|
+
configResolved(config) {
|
|
627
|
+
for (const key in config.define) if (key.startsWith("__VUE")) nitro.options.replace[key] = config.define[key];
|
|
628
|
+
}
|
|
629
|
+
});
|
|
626
630
|
nitro.vfs = nuxt.vfs = nitro.vfs || nuxt.vfs || {};
|
|
627
631
|
nuxt.hook("close", () => nitro.hooks.callHook("close"));
|
|
628
632
|
nitro.hooks.hook("prerender:routes", (routes) => {
|
|
@@ -656,12 +660,12 @@ async function bundle(nuxt) {
|
|
|
656
660
|
handler: resolve(distDir, "runtime/handlers/renderer")
|
|
657
661
|
});
|
|
658
662
|
if (nuxt.options.experimental.chromeDevtoolsProjectSettings) {
|
|
659
|
-
const cacheDir
|
|
660
|
-
let projectConfiguration = await readFile(join(cacheDir
|
|
663
|
+
const cacheDir = resolve(nuxt.options.rootDir, "node_modules/.cache/nuxt");
|
|
664
|
+
let projectConfiguration = await readFile(join(cacheDir, "chrome-workspace.json"), "utf-8").then((r) => JSON.parse(r)).catch(() => null);
|
|
661
665
|
if (!projectConfiguration) {
|
|
662
666
|
projectConfiguration = { uuid: randomUUID() };
|
|
663
|
-
await mkdir(cacheDir
|
|
664
|
-
await writeFile(join(cacheDir
|
|
667
|
+
await mkdir(cacheDir, { recursive: true });
|
|
668
|
+
await writeFile(join(cacheDir, "chrome-workspace.json"), JSON.stringify(projectConfiguration), "utf-8");
|
|
665
669
|
}
|
|
666
670
|
nitro.options.devHandlers.push({
|
|
667
671
|
route: "/.well-known/appspecific/com.chrome.devtools.json",
|
|
@@ -671,12 +675,12 @@ async function bundle(nuxt) {
|
|
|
671
675
|
} }))
|
|
672
676
|
});
|
|
673
677
|
}
|
|
674
|
-
if (!nuxt.options.dev && nuxt.options.experimental.noVueServer) nitro.hooks.hook("rollup:before", (nitro
|
|
675
|
-
if (nitro
|
|
676
|
-
const nuxtErrorHandler = nitro
|
|
677
|
-
if (nuxtErrorHandler >= 0) nitro
|
|
678
|
-
nitro
|
|
679
|
-
nitro
|
|
678
|
+
if (!nuxt.options.dev && nuxt.options.experimental.noVueServer) nitro.hooks.hook("rollup:before", (nitro) => {
|
|
679
|
+
if (nitro.options.preset === "nitro-prerender") return;
|
|
680
|
+
const nuxtErrorHandler = nitro.options.handlers.findIndex((h) => h.route === "/__nuxt_error");
|
|
681
|
+
if (nuxtErrorHandler >= 0) nitro.options.handlers.splice(nuxtErrorHandler, 1);
|
|
682
|
+
nitro.options.renderer = void 0;
|
|
683
|
+
nitro.options.errorHandler = "nitropack/runtime/error";
|
|
680
684
|
});
|
|
681
685
|
nuxt.hook("prepare:types", async (opts) => {
|
|
682
686
|
if (!nuxt.options.dev) {
|
|
@@ -691,14 +695,14 @@ async function bundle(nuxt) {
|
|
|
691
695
|
for (const route of ["/200.html", "/404.html"]) routes.add(route);
|
|
692
696
|
if (!nuxt.options.ssr) routes.add("/index.html");
|
|
693
697
|
});
|
|
694
|
-
if (!nuxt.options.dev) nitro.hooks.hook("rollup:before", async (nitro
|
|
695
|
-
await copyPublicAssets(nitro
|
|
696
|
-
await nuxt.callHook("nitro:build:public-assets", nitro
|
|
698
|
+
if (!nuxt.options.dev) nitro.hooks.hook("rollup:before", async (nitro) => {
|
|
699
|
+
await copyPublicAssets(nitro);
|
|
700
|
+
await nuxt.callHook("nitro:build:public-assets", nitro);
|
|
697
701
|
});
|
|
698
702
|
async function symlinkDist() {
|
|
699
703
|
if (nitro.options.static) {
|
|
700
|
-
const distDir
|
|
701
|
-
if (!existsSync(distDir
|
|
704
|
+
const distDir = resolve(nuxt.options.rootDir, "dist");
|
|
705
|
+
if (!existsSync(distDir)) await promises.symlink(nitro.options.output.publicDir, distDir, "junction").catch(() => {});
|
|
702
706
|
}
|
|
703
707
|
}
|
|
704
708
|
nuxt.hook("build:done", async () => {
|
|
@@ -736,7 +740,7 @@ async function bundle(nuxt) {
|
|
|
736
740
|
}));
|
|
737
741
|
});
|
|
738
742
|
nuxt.server = createDevServer(nitro);
|
|
739
|
-
const waitUntilCompile = new Promise((resolve
|
|
743
|
+
const waitUntilCompile = new Promise((resolve) => nitro.hooks.hook("compiled", () => resolve()));
|
|
740
744
|
nuxt.hook("build:done", () => waitUntilCompile);
|
|
741
745
|
}
|
|
742
746
|
}
|
|
@@ -750,14 +754,13 @@ async function spaLoadingTemplatePath(nuxt) {
|
|
|
750
754
|
}
|
|
751
755
|
async function spaLoadingTemplate(nuxt) {
|
|
752
756
|
if (nuxt.options.spaLoadingTemplate === false) return "";
|
|
753
|
-
const spaLoadingTemplate
|
|
757
|
+
const spaLoadingTemplate = await spaLoadingTemplatePath(nuxt);
|
|
754
758
|
try {
|
|
755
|
-
if (existsSync(spaLoadingTemplate
|
|
759
|
+
if (existsSync(spaLoadingTemplate)) return readFileSync(spaLoadingTemplate, "utf-8").trim();
|
|
756
760
|
} catch {}
|
|
757
761
|
if (nuxt.options.spaLoadingTemplate === true) return template();
|
|
758
762
|
if (nuxt.options.spaLoadingTemplate) logger.warn(`Could not load custom \`spaLoadingTemplate\` path as it does not exist: \`${nuxt.options.spaLoadingTemplate}\`.`);
|
|
759
763
|
return "";
|
|
760
764
|
}
|
|
761
|
-
|
|
762
765
|
//#endregion
|
|
763
|
-
export { bundle };
|
|
766
|
+
export { bundle };
|
|
@@ -26,8 +26,8 @@ export default (async function errorhandler(error, event, { defaultHandler }) {
|
|
|
26
26
|
// remove proto/hostname/port from URL
|
|
27
27
|
const url = new URL(errorObject.url);
|
|
28
28
|
errorObject.url = withoutBase(url.pathname, useRuntimeConfig(event).app.baseURL) + url.search + url.hash;
|
|
29
|
-
// add default server message
|
|
30
|
-
errorObject.message
|
|
29
|
+
// add default server message (keep sanitized for unhandled errors)
|
|
30
|
+
errorObject.message = error.unhandled ? errorObject.message || "Server Error" : error.message || errorObject.message || "Server Error";
|
|
31
31
|
// we will be rendering this error internally so we can pass along the error.data safely
|
|
32
32
|
errorObject.data ||= error.data;
|
|
33
33
|
errorObject.statusText ||= error.statusText || error.statusMessage;
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { EventHandler } from "h3";
|
|
2
|
+
declare const handler: EventHandler;
|
|
3
|
+
export default handler;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { destr } from "destr";
|
|
2
|
-
import { defineEventHandler, getQuery, readBody, setResponseHeaders } from "h3";
|
|
2
|
+
import { createError, defineEventHandler, getQuery, readBody, setResponseHeaders } from "h3";
|
|
3
3
|
import { resolveUnrefHeadInput } from "@unhead/vue";
|
|
4
4
|
import { getRequestDependencies } from "vue-bundle-renderer/runtime";
|
|
5
5
|
import { getQuery as getURLQuery } from "ufo";
|
|
@@ -10,7 +10,7 @@ import { getSSRRenderer } from "../utils/renderer/build-files.mjs";
|
|
|
10
10
|
import { renderInlineStyles } from "../utils/renderer/inline-styles.mjs";
|
|
11
11
|
import { getClientIslandResponse, getServerComponentHTML, getSlotIslandResponse } from "../utils/renderer/islands.mjs";
|
|
12
12
|
const ISLAND_SUFFIX_RE = /\.json(?:\?.*)?$/;
|
|
13
|
-
|
|
13
|
+
const handler = defineEventHandler(async (event) => {
|
|
14
14
|
const nitroApp = useNitroApp();
|
|
15
15
|
setResponseHeaders(event, {
|
|
16
16
|
"content-type": "application/json;charset=utf-8",
|
|
@@ -98,26 +98,38 @@ export default defineEventHandler(async (event) => {
|
|
|
98
98
|
}
|
|
99
99
|
return islandResponse;
|
|
100
100
|
});
|
|
101
|
+
export default handler;
|
|
102
|
+
const ISLAND_PATH_PREFIX = "/__nuxt_island/";
|
|
103
|
+
const VALID_COMPONENT_NAME_RE = /^[a-z][\w.-]*$/i;
|
|
101
104
|
async function getIslandContext(event) {
|
|
102
|
-
// TODO: Strict validation for url
|
|
103
105
|
let url = event.path || "";
|
|
104
106
|
if (import.meta.prerender && event.path && await islandPropCache.hasItem(event.path)) {
|
|
105
107
|
// rehydrate props from cache so we can rerender island if cache does not have it any more
|
|
106
108
|
url = await islandPropCache.getItem(event.path);
|
|
107
109
|
}
|
|
108
|
-
|
|
110
|
+
if (!url.startsWith(ISLAND_PATH_PREFIX)) {
|
|
111
|
+
throw createError({
|
|
112
|
+
statusCode: 400,
|
|
113
|
+
statusMessage: "Invalid island request path"
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const componentParts = url.substring(ISLAND_PATH_PREFIX.length).replace(ISLAND_SUFFIX_RE, "").split("_");
|
|
109
117
|
const hashId = componentParts.length > 1 ? componentParts.pop() : undefined;
|
|
110
118
|
const componentName = componentParts.join("_");
|
|
111
|
-
|
|
119
|
+
if (!componentName || !VALID_COMPONENT_NAME_RE.test(componentName)) {
|
|
120
|
+
throw createError({
|
|
121
|
+
statusCode: 400,
|
|
122
|
+
statusMessage: "Invalid island component name"
|
|
123
|
+
});
|
|
124
|
+
}
|
|
112
125
|
const context = event.method === "GET" ? getQuery(event) : await readBody(event);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
126
|
+
// Only extract known context fields to prevent arbitrary data injection
|
|
127
|
+
return {
|
|
128
|
+
url: typeof context?.url === "string" ? context.url : "/",
|
|
116
129
|
id: hashId,
|
|
117
130
|
name: componentName,
|
|
118
131
|
props: destr(context.props) || {},
|
|
119
132
|
slots: {},
|
|
120
133
|
components: {}
|
|
121
134
|
};
|
|
122
|
-
return ctx;
|
|
123
135
|
}
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { EventHandler } from "h3";
|
|
2
|
+
declare const handler: EventHandler;
|
|
3
|
+
export default handler;
|
|
@@ -38,7 +38,7 @@ const APP_TELEPORT_CLOSE_TAG = HAS_APP_TELEPORTS ? `</${appTeleportTag}>` : "";
|
|
|
38
38
|
const PAYLOAD_URL_RE = NUXT_JSON_PAYLOADS ? /^[^?]*\/_payload.json(?:\?.*)?$/ : /^[^?]*\/_payload.js(?:\?.*)?$/;
|
|
39
39
|
const PAYLOAD_FILENAME = NUXT_JSON_PAYLOADS ? "_payload.json" : "_payload.js";
|
|
40
40
|
let entryPath;
|
|
41
|
-
|
|
41
|
+
const handler = defineRenderHandler(async (event) => {
|
|
42
42
|
const nitroApp = useNitroApp();
|
|
43
43
|
// Whether we're rendering an error page
|
|
44
44
|
const ssrError = event.path.startsWith("/__nuxt_error") ? getQuery(event) : null;
|
|
@@ -282,6 +282,7 @@ export default defineRenderHandler(async (event) => {
|
|
|
282
282
|
}
|
|
283
283
|
};
|
|
284
284
|
});
|
|
285
|
+
export default handler;
|
|
285
286
|
function normalizeChunks(chunks) {
|
|
286
287
|
const result = [];
|
|
287
288
|
for (const _chunk of chunks) {
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { EventHandler } from "h3";
|
|
2
|
+
declare const handler: EventHandler;
|
|
3
|
+
export default handler;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { defineEventHandler, getRequestHeader } from "h3";
|
|
2
|
-
|
|
2
|
+
const handler = defineEventHandler((event) => {
|
|
3
3
|
if (getRequestHeader(event, "x-nuxt-no-ssr")) {
|
|
4
4
|
event.context.nuxt ||= {};
|
|
5
5
|
event.context.nuxt.noSSR = true;
|
|
6
6
|
}
|
|
7
7
|
});
|
|
8
|
+
export default handler;
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const
|
|
3
|
-
export declare const
|
|
4
|
-
export declare const
|
|
5
|
-
export declare const
|
|
1
|
+
import type { Storage } from "unstorage";
|
|
2
|
+
export declare const payloadCache: Storage | null;
|
|
3
|
+
export declare const islandCache: Storage | null;
|
|
4
|
+
export declare const islandPropCache: Storage | null;
|
|
5
|
+
export declare const sharedPrerenderPromises: Map<string, Promise<any>> | null;
|
|
6
|
+
interface SharedPrerenderCache {
|
|
7
|
+
get<T = unknown>(key: string): Promise<T> | undefined;
|
|
8
|
+
set<T>(key: string, value: Promise<T>): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export declare const sharedPrerenderCache: SharedPrerenderCache | null;
|
|
11
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const defineAppConfig:
|
|
1
|
+
export declare const defineAppConfig: (config: any) => any;
|
|
@@ -357,7 +357,7 @@ function webComponentScript(base64HTML, startMinimized) {
|
|
|
357
357
|
iframe.id = 'frame';
|
|
358
358
|
iframe.src = 'data:text/html;base64,${base64HTML}';
|
|
359
359
|
iframe.title = 'Detailed error stack trace';
|
|
360
|
-
iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');
|
|
360
|
+
iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-top-navigation-by-user-activation');
|
|
361
361
|
|
|
362
362
|
const preview = el('div');
|
|
363
363
|
preview.id = 'preview';
|
|
@@ -3,4 +3,4 @@ import type { H3Event } from "h3";
|
|
|
3
3
|
* Nitro internal functions extracted from https://github.com/nitrojs/nitro/blob/v2/src/runtime/internal/utils.ts
|
|
4
4
|
*/
|
|
5
5
|
export declare function isJsonRequest(event: H3Event): boolean;
|
|
6
|
-
export declare function hasReqHeader(event: H3Event, name: string, includes: string);
|
|
6
|
+
export declare function hasReqHeader(event: H3Event, name: string, includes: string): boolean;
|
|
@@ -11,5 +11,5 @@ export function isJsonRequest(event) {
|
|
|
11
11
|
}
|
|
12
12
|
export function hasReqHeader(event, name, includes) {
|
|
13
13
|
const value = getRequestHeader(event, name);
|
|
14
|
-
return value && typeof value === "string" && value.toLowerCase().includes(includes);
|
|
14
|
+
return !!(value && typeof value === "string" && value.toLowerCase().includes(includes));
|
|
15
15
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { decodePath } from "ufo";
|
|
2
1
|
import { useRuntimeConfig } from "nitropack/runtime";
|
|
3
2
|
import { createHead } from "@unhead/vue/server";
|
|
4
3
|
import { sharedPrerenderCache } from "../cache.mjs";
|
|
@@ -13,7 +12,7 @@ const PRERENDER_NO_SSR_ROUTES = new Set([
|
|
|
13
12
|
]);
|
|
14
13
|
export function createSSRContext(event) {
|
|
15
14
|
const ssrContext = {
|
|
16
|
-
url:
|
|
15
|
+
url: event.path,
|
|
17
16
|
event,
|
|
18
17
|
runtimeConfig: useRuntimeConfig(event),
|
|
19
18
|
noSSR: !!NUXT_NO_SSR || event.context.nuxt?.noSSR || (import.meta.prerender ? PRERENDER_NO_SSR_ROUTES.has(event.path) : false),
|
|
@@ -10,9 +10,7 @@ interface Renderer {
|
|
|
10
10
|
renderScripts: () => string;
|
|
11
11
|
}>;
|
|
12
12
|
}
|
|
13
|
-
|
|
14
|
-
export declare const getSSRRenderer: unknown;
|
|
13
|
+
export declare const getSSRRenderer: () => Promise<Renderer>;
|
|
15
14
|
export declare function getRenderer(ssrContext: NuxtSSRContext): Promise<Renderer>;
|
|
16
|
-
|
|
17
|
-
export declare const getSSRStyles: unknown;
|
|
15
|
+
export declare const getSSRStyles: () => Promise<Record<string, () => Promise<string[]>>>;
|
|
18
16
|
export {};
|
|
@@ -7,7 +7,7 @@ import { appId, multiApp } from "#internal/nuxt.config.mjs";
|
|
|
7
7
|
import { NUXT_JSON_PAYLOADS, NUXT_NO_SSR, NUXT_PAYLOAD_EXTRACTION, NUXT_RUNTIME_PAYLOAD_EXTRACTION } from "#internal/nuxt/nitro-config.mjs";
|
|
8
8
|
export function renderPayloadResponse(ssrContext) {
|
|
9
9
|
return {
|
|
10
|
-
body: NUXT_JSON_PAYLOADS ? stringify(splitPayload(ssrContext).payload, ssrContext["~payloadReducers"]) : `export default ${devalue(splitPayload(ssrContext).payload)}`,
|
|
10
|
+
body: NUXT_JSON_PAYLOADS ? encodeForwardSlashes(stringify(splitPayload(ssrContext).payload, ssrContext["~payloadReducers"])) : `export default ${devalue(splitPayload(ssrContext).payload)}`,
|
|
11
11
|
statusCode: getResponseStatus(ssrContext.event),
|
|
12
12
|
statusMessage: getResponseStatusText(ssrContext.event),
|
|
13
13
|
headers: {
|
|
@@ -17,7 +17,7 @@ export function renderPayloadResponse(ssrContext) {
|
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
export function renderPayloadJsonScript(opts) {
|
|
20
|
-
const contents = opts.data ? stringify(opts.data, opts.ssrContext["~payloadReducers"]) : "";
|
|
20
|
+
const contents = opts.data ? encodeForwardSlashes(stringify(opts.data, opts.ssrContext["~payloadReducers"])) : "";
|
|
21
21
|
const payload = {
|
|
22
22
|
"type": "application/json",
|
|
23
23
|
"innerHTML": contents,
|
|
@@ -33,13 +33,30 @@ export function renderPayloadJsonScript(opts) {
|
|
|
33
33
|
const config = uneval(opts.ssrContext.config);
|
|
34
34
|
return [payload, { innerHTML: multiApp ? `window.__NUXT__=window.__NUXT__||{};window.__NUXT__[${JSON.stringify(appId)}]={config:${config}}` : `window.__NUXT__={};window.__NUXT__.config=${config}` }];
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Encode forward slashes as unicode escape sequences to prevent
|
|
38
|
+
* Google from treating them as internal links and trying to crawl them.
|
|
39
|
+
* @see https://github.com/nuxt/nuxt/issues/24175
|
|
40
|
+
*/
|
|
41
|
+
function encodeForwardSlashes(str) {
|
|
42
|
+
return str.replaceAll("/", "\\u002F");
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Escape a string for safe interpolation inside a double-quoted JavaScript string literal.
|
|
46
|
+
* Prevents XSS when user-controlled URLs are embedded in inline `<script>` tags.
|
|
47
|
+
*/
|
|
48
|
+
function escapeJsString(str) {
|
|
49
|
+
return str.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"").replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll("/", "\\u002F").replaceAll("<", "\\u003C");
|
|
50
|
+
}
|
|
36
51
|
export function renderPayloadScript(opts) {
|
|
37
52
|
opts.data.config = opts.ssrContext.config;
|
|
38
53
|
const _PAYLOAD_EXTRACTION = !opts.ssrContext.noSSR && (import.meta.prerender && NUXT_PAYLOAD_EXTRACTION || NUXT_RUNTIME_PAYLOAD_EXTRACTION && (opts.routeOptions.isr || opts.routeOptions.cache));
|
|
39
54
|
const nuxtData = devalue(opts.data);
|
|
40
55
|
if (_PAYLOAD_EXTRACTION) {
|
|
41
|
-
|
|
42
|
-
const
|
|
56
|
+
// Escape the URL to prevent XSS when interpolated into a JS string literal
|
|
57
|
+
const escapedSrc = escapeJsString(opts.src);
|
|
58
|
+
const singleAppPayload = `import p from "${escapedSrc}";window.__NUXT__={...p,...(${nuxtData})}`;
|
|
59
|
+
const multiAppPayload = `import p from "${escapedSrc}";window.__NUXT__=window.__NUXT__||{};window.__NUXT__[${JSON.stringify(appId)}]={...p,...(${nuxtData})}`;
|
|
43
60
|
return [{
|
|
44
61
|
type: "module",
|
|
45
62
|
innerHTML: multiApp ? multiAppPayload : singleAppPayload
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuxt/nitro-server",
|
|
3
|
-
"version": "3.21.
|
|
3
|
+
"version": "3.21.2",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/nuxt/nuxt.git",
|
|
@@ -19,41 +19,41 @@
|
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@nuxt/devalue": "^2.0.2",
|
|
22
|
-
"@unhead/vue": "^2.1.
|
|
23
|
-
"@vue/shared": "^3.5.
|
|
22
|
+
"@unhead/vue": "^2.1.12",
|
|
23
|
+
"@vue/shared": "^3.5.30",
|
|
24
24
|
"consola": "^3.4.2",
|
|
25
25
|
"defu": "^6.1.4",
|
|
26
26
|
"destr": "^2.0.5",
|
|
27
|
-
"devalue": "^5.6.
|
|
27
|
+
"devalue": "^5.6.3",
|
|
28
28
|
"errx": "^0.1.0",
|
|
29
29
|
"escape-string-regexp": "^5.0.0",
|
|
30
30
|
"exsolve": "^1.0.8",
|
|
31
|
-
"h3": "^1.15.
|
|
32
|
-
"impound": "^1.
|
|
31
|
+
"h3": "^1.15.6",
|
|
32
|
+
"impound": "^1.1.5",
|
|
33
33
|
"klona": "^2.0.6",
|
|
34
34
|
"mocked-exports": "^0.1.1",
|
|
35
35
|
"nitropack": "^2.13.1",
|
|
36
36
|
"ohash": "^2.0.11",
|
|
37
37
|
"pathe": "^2.0.3",
|
|
38
38
|
"pkg-types": "^2.3.0",
|
|
39
|
-
"rou3": "^0.
|
|
40
|
-
"std-env": "^
|
|
39
|
+
"rou3": "^0.8.1",
|
|
40
|
+
"std-env": "^4.0.0",
|
|
41
41
|
"ufo": "^1.6.3",
|
|
42
42
|
"unctx": "^2.5.0",
|
|
43
43
|
"unstorage": "^1.17.4",
|
|
44
|
-
"vue": "^3.5.
|
|
44
|
+
"vue": "^3.5.30",
|
|
45
45
|
"vue-bundle-renderer": "^2.2.0",
|
|
46
46
|
"vue-devtools-stub": "^0.1.0",
|
|
47
|
-
"@nuxt/kit": "3.21.
|
|
47
|
+
"@nuxt/kit": "3.21.2"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"nuxt": "^3.21.
|
|
50
|
+
"nuxt": "^3.21.2"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"obuild": "0.4.
|
|
54
|
-
"vitest": "
|
|
55
|
-
"nuxt": "3.21.
|
|
56
|
-
"
|
|
53
|
+
"obuild": "0.4.32",
|
|
54
|
+
"vitest": "4.0.18",
|
|
55
|
+
"@nuxt/schema": "3.21.2",
|
|
56
|
+
"nuxt": "3.21.2"
|
|
57
57
|
},
|
|
58
58
|
"engines": {
|
|
59
59
|
"node": "^20.19.0 || >=22.12.0"
|