@nuxt/nitro-server 4.3.1 → 4.4.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 +4 -4
- package/dist/THIRD-PARTY-LICENSES.md +0 -7
- package/dist/index.d.mts +2 -50462
- package/dist/index.mjs +180 -27
- package/dist/runtime/h3-compat.d.mts +4 -0
- package/dist/runtime/h3-compat.mjs +4 -0
- 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 +31 -32
- 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/cache.mjs +2 -2
- 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/build-files.d.mts +2 -2
- package/dist/runtime/utils/renderer/payload.mjs +23 -7
- package/package.json +37 -15
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { performance } from "node:perf_hooks";
|
|
1
2
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
2
3
|
import { existsSync, promises, readFileSync } from "node:fs";
|
|
3
4
|
import { cpus } from "node:os";
|
|
@@ -15,37 +16,78 @@ import { addPlugin, addTemplate, addVitePlugin, createIsIgnored, findPath, getDi
|
|
|
15
16
|
import escapeRE from "escape-string-regexp";
|
|
16
17
|
import { defu } from "defu";
|
|
17
18
|
import { defineEventHandler, dynamicEventHandler, handleCors, setHeader } from "h3";
|
|
18
|
-
import {
|
|
19
|
+
import { addDependency } from "nypm";
|
|
20
|
+
import { hasTTY, isCI, isWindows } from "std-env";
|
|
19
21
|
import { ImpoundPlugin } from "impound";
|
|
20
22
|
import { resolveModulePath } from "exsolve";
|
|
21
23
|
import { runtimeDependencies } from "nitropack/runtime/meta";
|
|
22
|
-
|
|
24
|
+
//#region package.json
|
|
25
|
+
var version = "4.4.2";
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/utils.ts
|
|
23
28
|
function toArray(value) {
|
|
24
29
|
return Array.isArray(value) ? value : [value];
|
|
25
30
|
}
|
|
26
31
|
let _distDir = dirname(fileURLToPath(import.meta.url));
|
|
27
32
|
if (/(?:chunks|shared)$/.test(_distDir)) _distDir = dirname(_distDir);
|
|
28
33
|
const distDir = _distDir;
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region ../ui-templates/dist/templates/spa-loading-icon.ts
|
|
29
36
|
const template = () => {
|
|
30
37
|
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>";
|
|
31
38
|
};
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region ../nuxt/src/core/plugins/import-protection.ts
|
|
32
41
|
function createImportProtectionPatterns(nuxt, options) {
|
|
33
42
|
const patterns = [];
|
|
34
43
|
const context = contextFlags[options.context];
|
|
35
|
-
patterns.push([
|
|
36
|
-
|
|
44
|
+
patterns.push([
|
|
45
|
+
/^(nuxt|nuxt3|nuxt-nightly)$/,
|
|
46
|
+
`\`nuxt\` or \`nuxt-nightly\` cannot be imported directly in ${context}.`,
|
|
47
|
+
options.context === "nuxt-app" ? ["Import runtime Nuxt composables from `#app` or `#imports` instead."] : ["Use `#app` or `#imports` for runtime composables in your Vue app code."]
|
|
48
|
+
]);
|
|
49
|
+
patterns.push([
|
|
50
|
+
/^((~|~~|@|@@)?\/)?nuxt\.config(\.|$)/,
|
|
51
|
+
"Importing directly from a `nuxt.config` file is not allowed.",
|
|
52
|
+
[
|
|
53
|
+
"Use `useRuntimeConfig()` to access runtime config in your app.",
|
|
54
|
+
"Use `useAppConfig()` to access config that doesn't need to be changed at runtime.",
|
|
55
|
+
"Use a Nuxt module to access build-time configuration."
|
|
56
|
+
]
|
|
57
|
+
]);
|
|
37
58
|
patterns.push([/(^|node_modules\/)@vue\/composition-api/]);
|
|
38
|
-
for (const mod of nuxt.options._installedModules) if (mod.entryPath) patterns.push([
|
|
59
|
+
for (const mod of nuxt.options._installedModules) if (mod.entryPath) patterns.push([
|
|
60
|
+
new RegExp(`^${escapeRE(mod.entryPath)}$`),
|
|
61
|
+
"Importing directly from module entry-points is not allowed.",
|
|
62
|
+
["Import from the module's runtime directory instead (e.g. `my-module/runtime/...`)."]
|
|
63
|
+
]);
|
|
39
64
|
for (const i of [
|
|
40
65
|
/(^|node_modules\/)@nuxt\/(cli|kit|test-utils)/,
|
|
41
66
|
/(^|node_modules\/)nuxi/,
|
|
42
67
|
/(^|node_modules\/)nitro(?:pack)?(?:-nightly)?(?:$|\/)(?!(?:dist\/)?(?:node_modules|presets|runtime|types))/,
|
|
43
68
|
/(^|node_modules\/)nuxt\/(config|kit|schema)/
|
|
44
|
-
]) patterns.push([
|
|
45
|
-
|
|
69
|
+
]) patterns.push([
|
|
70
|
+
i,
|
|
71
|
+
`This module cannot be imported in ${context}.`,
|
|
72
|
+
["These are build-time only packages and cannot be used at runtime."]
|
|
73
|
+
]);
|
|
74
|
+
if (options.context === "nitro-app" || options.context === "shared") for (const i of ["#app", /^#build(\/|$)/]) patterns.push([
|
|
75
|
+
i,
|
|
76
|
+
`Vue app aliases are not allowed in ${context}.`,
|
|
77
|
+
["Move this code to your Vue app directory or use a shared utility."]
|
|
78
|
+
]);
|
|
46
79
|
if (options.context === "nuxt-app" || options.context === "shared") {
|
|
47
|
-
|
|
48
|
-
patterns.push([
|
|
80
|
+
const serverRelative = escapeRE(relative(nuxt.options.rootDir, resolve(nuxt.options.srcDir, nuxt.options.serverDir || "server")));
|
|
81
|
+
patterns.push([
|
|
82
|
+
new RegExp("^" + serverRelative + "\\/(api|routes|middleware|plugins)\\/"),
|
|
83
|
+
`Importing from server is not allowed in ${context}.`,
|
|
84
|
+
["Use `$fetch()` or `useFetch()` to fetch data from server routes.", "Move shared logic to the `shared/` directory."]
|
|
85
|
+
]);
|
|
86
|
+
patterns.push([
|
|
87
|
+
/^#server(\/|$)/,
|
|
88
|
+
`Server aliases are not allowed in ${context}.`,
|
|
89
|
+
["Use `$fetch()` or `useFetch()` to call server endpoints.", "Move shared logic to the `shared/` directory."]
|
|
90
|
+
]);
|
|
49
91
|
}
|
|
50
92
|
return patterns;
|
|
51
93
|
}
|
|
@@ -54,6 +96,8 @@ const contextFlags = {
|
|
|
54
96
|
"nuxt-app": "the Vue part of your app",
|
|
55
97
|
"shared": "the #shared directory"
|
|
56
98
|
};
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region src/templates.ts
|
|
57
101
|
const nitroSchemaTemplate = {
|
|
58
102
|
filename: "types/nitro-nuxt.d.ts",
|
|
59
103
|
async getContents({ nuxt }) {
|
|
@@ -130,6 +174,8 @@ declare module 'nitropack/types' {
|
|
|
130
174
|
function renderReference(ref, baseDir) {
|
|
131
175
|
return `/// <reference ${"path" in ref ? `path="${isAbsolute(ref.path) ? relative(baseDir, ref.path) : ref.path}"` : `types="${ref.types}"`} />`;
|
|
132
176
|
}
|
|
177
|
+
//#endregion
|
|
178
|
+
//#region src/index.ts
|
|
133
179
|
const logLevelMapReverse = {
|
|
134
180
|
silent: 0,
|
|
135
181
|
info: 3,
|
|
@@ -269,7 +315,8 @@ async function bundle(nuxt) {
|
|
|
269
315
|
`export const NUXT_JSON_PAYLOADS = ${!!nuxt.options.experimental.renderJsonPayloads}`,
|
|
270
316
|
`export const NUXT_ASYNC_CONTEXT = ${!!nuxt.options.experimental.asyncContext}`,
|
|
271
317
|
`export const NUXT_SHARED_DATA = ${!!nuxt.options.experimental.sharedPrerenderData}`,
|
|
272
|
-
`export const NUXT_PAYLOAD_EXTRACTION = ${
|
|
318
|
+
`export const NUXT_PAYLOAD_EXTRACTION = ${nuxt.options.experimental.payloadExtraction !== false}`,
|
|
319
|
+
`export const NUXT_PAYLOAD_INLINE = ${nuxt.options.experimental.payloadExtraction !== true}`,
|
|
273
320
|
`export const NUXT_RUNTIME_PAYLOAD_EXTRACTION = ${hasCachedRoutes}`
|
|
274
321
|
].join("\n");
|
|
275
322
|
}
|
|
@@ -374,7 +421,8 @@ async function bundle(nuxt) {
|
|
|
374
421
|
"appLayout",
|
|
375
422
|
"cache",
|
|
376
423
|
"isr",
|
|
377
|
-
"swr"
|
|
424
|
+
"swr",
|
|
425
|
+
"ssr"
|
|
378
426
|
];
|
|
379
427
|
function getRouteRulesRouter() {
|
|
380
428
|
const routeRulesRouter = createRouter();
|
|
@@ -435,6 +483,7 @@ async function bundle(nuxt) {
|
|
|
435
483
|
nuxt.hook("nitro:init", (nitro) => {
|
|
436
484
|
nitro.hooks.hook("build:before", (nitro) => {
|
|
437
485
|
for (const [route, value] of Object.entries(nitro.options.routeRules)) if (!route.endsWith("*") && !route.endsWith("/_payload.json")) {
|
|
486
|
+
if (value.ssr === false) continue;
|
|
438
487
|
if (value.isr || value.cache || value.prerender && nuxt.options.dev) {
|
|
439
488
|
const payloadKey = route + "/_payload.json";
|
|
440
489
|
const defaults = {};
|
|
@@ -510,6 +559,55 @@ async function bundle(nuxt) {
|
|
|
510
559
|
nitroConfig.virtual["#build/dist/server/styles.mjs"] = "export default {}";
|
|
511
560
|
if (process.platform === "win32") nitroConfig.virtual["#build/dist/server/styles.mjs".replace(FORWARD_SLASH_RE, "\\")] = "export default {}";
|
|
512
561
|
}
|
|
562
|
+
if (nuxt.options.experimental.decorators) {
|
|
563
|
+
const nitroDecoratorDeps = ["@rollup/plugin-babel", "@babel/plugin-proposal-decorators"];
|
|
564
|
+
let hasDeps = true;
|
|
565
|
+
for (const pkg of nitroDecoratorDeps) try {
|
|
566
|
+
await import(pkg);
|
|
567
|
+
} catch (_err) {
|
|
568
|
+
const err = _err;
|
|
569
|
+
if (err.code !== "ERR_MODULE_NOT_FOUND" && err.code !== "MODULE_NOT_FOUND") throw err;
|
|
570
|
+
if (!isCI && hasTTY) {
|
|
571
|
+
logger.info("Decorator support requires additional dependencies.");
|
|
572
|
+
if (await logger.prompt(`Install \`${nitroDecoratorDeps.join("` and `")}\`?`, {
|
|
573
|
+
type: "confirm",
|
|
574
|
+
initial: true
|
|
575
|
+
})) {
|
|
576
|
+
logger.start(`Installing ${nitroDecoratorDeps.map((d) => `\`${d}\``).join(" and ")}...`);
|
|
577
|
+
await addDependency(nitroDecoratorDeps, {
|
|
578
|
+
dev: true,
|
|
579
|
+
cwd: nuxt.options.rootDir,
|
|
580
|
+
silent: true
|
|
581
|
+
});
|
|
582
|
+
logger.info("Rerun Nuxt to enable decorator support.");
|
|
583
|
+
process.exit(1);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
logger.warn(`Cannot find \`${pkg}\`. Install \`${nitroDecoratorDeps.join("` and `")}\` to enable decorator support.`);
|
|
587
|
+
hasDeps = false;
|
|
588
|
+
break;
|
|
589
|
+
}
|
|
590
|
+
if (hasDeps) {
|
|
591
|
+
const { babel } = await import("@rollup/plugin-babel");
|
|
592
|
+
nitroConfig.rollupConfig.plugins = toArray(await nitroConfig.rollupConfig.plugins || []);
|
|
593
|
+
nitroConfig.rollupConfig.plugins.unshift(babel({
|
|
594
|
+
babelHelpers: "bundled",
|
|
595
|
+
configFile: false,
|
|
596
|
+
extensions: [
|
|
597
|
+
".ts",
|
|
598
|
+
".js",
|
|
599
|
+
".mjs",
|
|
600
|
+
".mts"
|
|
601
|
+
],
|
|
602
|
+
plugins: [["@babel/plugin-syntax-typescript", { isTSX: false }], ["@babel/plugin-proposal-decorators", { version: "2023-11" }]]
|
|
603
|
+
}), babel({
|
|
604
|
+
babelHelpers: "bundled",
|
|
605
|
+
configFile: false,
|
|
606
|
+
extensions: [".tsx", ".jsx"],
|
|
607
|
+
plugins: [["@babel/plugin-syntax-typescript", { isTSX: true }], ["@babel/plugin-proposal-decorators", { version: "2023-11" }]]
|
|
608
|
+
}));
|
|
609
|
+
}
|
|
610
|
+
}
|
|
513
611
|
nitroConfig.rollupConfig.plugins = await nitroConfig.rollupConfig.plugins || [];
|
|
514
612
|
nitroConfig.rollupConfig.plugins = toArray(nitroConfig.rollupConfig.plugins);
|
|
515
613
|
const sharedDir = withTrailingSlash(resolve(nuxt.options.rootDir, nuxt.options.dir.shared));
|
|
@@ -521,10 +619,12 @@ async function bundle(nuxt) {
|
|
|
521
619
|
];
|
|
522
620
|
nitroConfig.rollupConfig.plugins.push(ImpoundPlugin.rollup({
|
|
523
621
|
cwd: nuxt.options.rootDir,
|
|
622
|
+
trace: true,
|
|
524
623
|
include: sharedPatterns,
|
|
525
624
|
patterns: createImportProtectionPatterns(nuxt, { context: "shared" })
|
|
526
625
|
}), ImpoundPlugin.rollup({
|
|
527
626
|
cwd: nuxt.options.rootDir,
|
|
627
|
+
trace: true,
|
|
528
628
|
patterns: createImportProtectionPatterns(nuxt, { context: "nitro-app" }),
|
|
529
629
|
exclude: [/node_modules[\\/]nitro(?:pack)?(?:-nightly)?[\\/]|(packages|@nuxt)[\\/]nitro-server(?:-nightly)?[\\/](src|dist)[\\/]runtime[\\/]/, ...sharedPatterns]
|
|
530
630
|
}));
|
|
@@ -575,10 +675,12 @@ async function bundle(nuxt) {
|
|
|
575
675
|
tsConfig.compilerOptions.paths[alias] = [absolutePath];
|
|
576
676
|
if (isDirectory) tsConfig.compilerOptions.paths[`${alias}/*`] = [`${absolutePath}/*`];
|
|
577
677
|
}
|
|
678
|
+
nuxt._perf?.startPhase("nitro:createNitro");
|
|
578
679
|
const nitro = await createNitro(nitroConfig, {
|
|
579
680
|
compatibilityDate: nuxt.options.compatibilityDate,
|
|
580
681
|
dotenv: nuxt.options._loadOptions?.dotenv
|
|
581
682
|
});
|
|
683
|
+
nuxt._perf?.endPhase("nitro:createNitro");
|
|
582
684
|
if (nuxt.options.experimental.serverAppConfig === false && nitro.options.imports) {
|
|
583
685
|
nitro.options.imports.presets ||= [];
|
|
584
686
|
nitro.options.imports.presets = nitro.options.imports.presets.map((preset) => typeof preset === "string" || !("imports" in preset) ? preset : {
|
|
@@ -586,10 +688,7 @@ async function bundle(nuxt) {
|
|
|
586
688
|
imports: preset.imports.filter((i) => i !== "useAppConfig")
|
|
587
689
|
});
|
|
588
690
|
}
|
|
589
|
-
if (nitro.options.static && nuxt.options.experimental.payloadExtraction ===
|
|
590
|
-
logger.warn("Using experimental payload extraction for full-static output. You can opt-out by setting `experimental.payloadExtraction` to `false`.");
|
|
591
|
-
nuxt.options.experimental.payloadExtraction = true;
|
|
592
|
-
}
|
|
691
|
+
if (nitro.options.static && nuxt.options.experimental.payloadExtraction === false) logger.warn("Payload extraction is recommended for full-static output. You can enable it by setting `experimental.payloadExtraction` to `true` or `'client'`.");
|
|
593
692
|
const spaLoadingTemplateFilePath = await spaLoadingTemplatePath(nuxt);
|
|
594
693
|
nuxt.hook("builder:watch", async (_event, relativePath) => {
|
|
595
694
|
if (resolve(nuxt.options.srcDir, relativePath) === spaLoadingTemplateFilePath) await nitro.hooks.callHook("rollup:reload");
|
|
@@ -606,8 +705,52 @@ async function bundle(nuxt) {
|
|
|
606
705
|
} });
|
|
607
706
|
nuxt._nitro = nitro;
|
|
608
707
|
await nuxt.callHook("nitro:init", nitro);
|
|
708
|
+
if (nuxt._perf) nitro.hooks.hook("rollup:before", (_nitro, rollupConfig) => {
|
|
709
|
+
const plugins = rollupConfig.plugins || [];
|
|
710
|
+
for (const plugin of plugins) {
|
|
711
|
+
if (!plugin || !plugin.name) continue;
|
|
712
|
+
const pluginName = `nitro:${plugin.name}`;
|
|
713
|
+
for (const hookName of [
|
|
714
|
+
"transform",
|
|
715
|
+
"resolveId",
|
|
716
|
+
"load"
|
|
717
|
+
]) {
|
|
718
|
+
const original = plugin[hookName];
|
|
719
|
+
if (typeof original !== "function") continue;
|
|
720
|
+
plugin[hookName] = function(...args) {
|
|
721
|
+
const start = performance.now();
|
|
722
|
+
const record = () => nuxt._perf?.recordBundlerPluginHook(pluginName, hookName, performance.now() - start, start);
|
|
723
|
+
try {
|
|
724
|
+
const result = original.apply(this, args);
|
|
725
|
+
if (result && typeof result === "object" && "then" in result) return result.finally(record);
|
|
726
|
+
record();
|
|
727
|
+
return result;
|
|
728
|
+
} catch (err) {
|
|
729
|
+
record();
|
|
730
|
+
throw err;
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
});
|
|
609
736
|
nuxt["~runtimeDependencies"] ||= [];
|
|
610
737
|
nuxt["~runtimeDependencies"].push(...runtimeDependencies, "unhead", "@unhead/vue", "@nuxt/devalue", "unstorage", ...nitro.options.inlineDynamicImports ? ["vue", "@vue/server-renderer"] : []);
|
|
738
|
+
addVitePlugin({
|
|
739
|
+
name: "nuxt:nitro:ssr-conditions",
|
|
740
|
+
configEnvironment(name, config) {
|
|
741
|
+
if (name === "ssr") {
|
|
742
|
+
config.resolve ||= {};
|
|
743
|
+
config.resolve.conditions = [...nitro.options.exportConditions || [], "import"];
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
addVitePlugin({
|
|
748
|
+
name: "nuxt:nitro:vue-feature-flags",
|
|
749
|
+
applyToEnvironment: (environment) => environment.name === "ssr" && environment.config.isProduction,
|
|
750
|
+
configResolved(config) {
|
|
751
|
+
for (const key in config.define) if (key.startsWith("__VUE")) nitro.options.replace[key] = config.define[key];
|
|
752
|
+
}
|
|
753
|
+
});
|
|
611
754
|
nitro.vfs = nuxt.vfs = nitro.vfs || nuxt.vfs || {};
|
|
612
755
|
nuxt.hook("close", () => nitro.hooks.callHook("close"));
|
|
613
756
|
nitro.hooks.hook("prerender:routes", (routes) => {
|
|
@@ -701,16 +844,7 @@ async function bundle(nuxt) {
|
|
|
701
844
|
if (!existsSync(distDir)) await promises.symlink(nitro.options.output.publicDir, distDir, "junction").catch(() => {});
|
|
702
845
|
}
|
|
703
846
|
}
|
|
704
|
-
|
|
705
|
-
await nuxt.callHook("nitro:build:before", nitro);
|
|
706
|
-
await prepare(nitro);
|
|
707
|
-
if (nuxt.options.dev) return build(nitro);
|
|
708
|
-
await prerender(nitro);
|
|
709
|
-
logger.restoreAll();
|
|
710
|
-
await build(nitro);
|
|
711
|
-
logger.wrapAll();
|
|
712
|
-
await symlinkDist();
|
|
713
|
-
});
|
|
847
|
+
let waitUntilCompile;
|
|
714
848
|
if (nuxt.options.dev) {
|
|
715
849
|
for (const builder of ["webpack", "rspack"]) {
|
|
716
850
|
nuxt.hook(`${builder}:compile`, ({ name, compiler }) => {
|
|
@@ -736,9 +870,27 @@ async function bundle(nuxt) {
|
|
|
736
870
|
}));
|
|
737
871
|
});
|
|
738
872
|
nuxt.server = createDevServer(nitro);
|
|
739
|
-
|
|
740
|
-
nuxt.hook("build:done", () => waitUntilCompile);
|
|
873
|
+
waitUntilCompile = new Promise((resolve) => nitro.hooks.hook("compiled", () => resolve()));
|
|
741
874
|
}
|
|
875
|
+
nuxt.hook("build:done", async () => {
|
|
876
|
+
nuxt._perf?.startPhase("nitro:build");
|
|
877
|
+
try {
|
|
878
|
+
await nuxt.callHook("nitro:build:before", nitro);
|
|
879
|
+
await prepare(nitro);
|
|
880
|
+
if (nuxt.options.dev) {
|
|
881
|
+
await build(nitro);
|
|
882
|
+
await waitUntilCompile;
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
await prerender(nitro);
|
|
886
|
+
logger.restoreAll();
|
|
887
|
+
await build(nitro);
|
|
888
|
+
logger.wrapAll();
|
|
889
|
+
await symlinkDist();
|
|
890
|
+
} finally {
|
|
891
|
+
nuxt._perf?.endPhase("nitro:build");
|
|
892
|
+
}
|
|
893
|
+
});
|
|
742
894
|
}
|
|
743
895
|
const RELATIVE_RE = /^([^.])/;
|
|
744
896
|
function relativeWithDot(from, to) {
|
|
@@ -758,4 +910,5 @@ async function spaLoadingTemplate(nuxt) {
|
|
|
758
910
|
if (nuxt.options.spaLoadingTemplate) logger.warn(`Could not load custom \`spaLoadingTemplate\` path as it does not exist: \`${nuxt.options.spaLoadingTemplate}\`.`);
|
|
759
911
|
return "";
|
|
760
912
|
}
|
|
913
|
+
//#endregion
|
|
761
914
|
export { bundle };
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { EventHandler } from "h3";
|
|
2
|
+
declare const handler: EventHandler;
|
|
3
|
+
export default handler;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useNitroApp } from "nitropack/runtime";
|
|
2
2
|
import { destr } from "destr";
|
|
3
|
-
import { defineEventHandler, getQuery, readBody, setResponseHeaders } from "h3";
|
|
3
|
+
import { createError, defineEventHandler, getQuery, readBody, setResponseHeaders } from "h3";
|
|
4
4
|
import { resolveUnrefHeadInput } from "@unhead/vue";
|
|
5
5
|
import { getRequestDependencies } from "vue-bundle-renderer/runtime";
|
|
6
6
|
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",
|
|
@@ -95,26 +95,38 @@ export default defineEventHandler(async (event) => {
|
|
|
95
95
|
}
|
|
96
96
|
return islandResponse;
|
|
97
97
|
});
|
|
98
|
+
export default handler;
|
|
99
|
+
const ISLAND_PATH_PREFIX = "/__nuxt_island/";
|
|
100
|
+
const VALID_COMPONENT_NAME_RE = /^[a-z][\w.-]*$/i;
|
|
98
101
|
async function getIslandContext(event) {
|
|
99
|
-
// TODO: Strict validation for url
|
|
100
102
|
let url = event.path || "";
|
|
101
103
|
if (import.meta.prerender && event.path && await islandPropCache.hasItem(event.path)) {
|
|
102
104
|
// rehydrate props from cache so we can rerender island if cache does not have it any more
|
|
103
105
|
url = await islandPropCache.getItem(event.path);
|
|
104
106
|
}
|
|
105
|
-
|
|
107
|
+
if (!url.startsWith(ISLAND_PATH_PREFIX)) {
|
|
108
|
+
throw createError({
|
|
109
|
+
statusCode: 400,
|
|
110
|
+
statusMessage: "Invalid island request path"
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const componentParts = url.substring(ISLAND_PATH_PREFIX.length).replace(ISLAND_SUFFIX_RE, "").split("_");
|
|
106
114
|
const hashId = componentParts.length > 1 ? componentParts.pop() : undefined;
|
|
107
115
|
const componentName = componentParts.join("_");
|
|
108
|
-
|
|
116
|
+
if (!componentName || !VALID_COMPONENT_NAME_RE.test(componentName)) {
|
|
117
|
+
throw createError({
|
|
118
|
+
statusCode: 400,
|
|
119
|
+
statusMessage: "Invalid island component name"
|
|
120
|
+
});
|
|
121
|
+
}
|
|
109
122
|
const context = event.method === "GET" ? getQuery(event) : await readBody(event);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
123
|
+
// Only extract known context fields to prevent arbitrary data injection
|
|
124
|
+
return {
|
|
125
|
+
url: typeof context?.url === "string" ? context.url : "/",
|
|
113
126
|
id: hashId,
|
|
114
127
|
name: componentName,
|
|
115
128
|
props: destr(context.props) || {},
|
|
116
129
|
slots: {},
|
|
117
130
|
components: {}
|
|
118
131
|
};
|
|
119
|
-
return ctx;
|
|
120
132
|
}
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { EventHandler } from "h3";
|
|
2
|
+
declare const handler: EventHandler;
|
|
3
|
+
export default handler;
|
|
@@ -14,9 +14,9 @@ import { replaceIslandTeleports } from "../utils/renderer/islands.mjs";
|
|
|
14
14
|
// @ts-expect-error virtual file
|
|
15
15
|
import { renderSSRHeadOptions } from "#internal/unhead.config.mjs";
|
|
16
16
|
// @ts-expect-error virtual file
|
|
17
|
-
import { NUXT_ASYNC_CONTEXT, NUXT_EARLY_HINTS, NUXT_INLINE_STYLES, NUXT_JSON_PAYLOADS, NUXT_NO_SCRIPTS, NUXT_PAYLOAD_EXTRACTION, NUXT_RUNTIME_PAYLOAD_EXTRACTION, PARSE_ERROR_DATA } from "#internal/nuxt/nitro-config.mjs";
|
|
17
|
+
import { NUXT_ASYNC_CONTEXT, NUXT_EARLY_HINTS, NUXT_INLINE_STYLES, NUXT_JSON_PAYLOADS, NUXT_NO_SCRIPTS, NUXT_PAYLOAD_EXTRACTION, NUXT_PAYLOAD_INLINE, NUXT_RUNTIME_PAYLOAD_EXTRACTION, PARSE_ERROR_DATA } from "#internal/nuxt/nitro-config.mjs";
|
|
18
18
|
// @ts-expect-error virtual file
|
|
19
|
-
import { appHead, appTeleportAttrs, appTeleportTag, componentIslands
|
|
19
|
+
import { appHead, appTeleportAttrs, appTeleportTag, componentIslands } from "#internal/nuxt.config.mjs";
|
|
20
20
|
// @ts-expect-error virtual file
|
|
21
21
|
import entryIds from "#internal/nuxt/entry-ids.mjs";
|
|
22
22
|
// @ts-expect-error virtual file
|
|
@@ -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;
|
|
@@ -72,12 +72,16 @@ export default defineRenderHandler(async (event) => {
|
|
|
72
72
|
const routeOptions = getRouteRules(event);
|
|
73
73
|
// Whether we are prerendering route or using ISR/SWR caching
|
|
74
74
|
const _PAYLOAD_EXTRACTION = !ssrContext.noSSR && (import.meta.prerender && NUXT_PAYLOAD_EXTRACTION || NUXT_RUNTIME_PAYLOAD_EXTRACTION && (routeOptions.isr || routeOptions.cache));
|
|
75
|
+
// When NUXT_PAYLOAD_INLINE is true (payloadExtraction: 'client'), we inline the full payload
|
|
76
|
+
// in the HTML to avoid a separate _payload.json fetch on initial load (which would trigger a
|
|
77
|
+
// second render or lambda invocation). The _payload.json endpoint still works for client-side nav.
|
|
78
|
+
const _PAYLOAD_INLINE = !_PAYLOAD_EXTRACTION || NUXT_PAYLOAD_INLINE;
|
|
75
79
|
const isRenderingPayload = (_PAYLOAD_EXTRACTION || import.meta.dev && routeOptions.prerender) && PAYLOAD_URL_RE.test(ssrContext.url);
|
|
76
80
|
if (isRenderingPayload) {
|
|
77
81
|
const url = ssrContext.url.substring(0, ssrContext.url.lastIndexOf("/")) || "/";
|
|
78
82
|
ssrContext.url = url;
|
|
79
83
|
event._path = event.node.req.url = url;
|
|
80
|
-
if (
|
|
84
|
+
if (payloadCache && await payloadCache.hasItem(url)) {
|
|
81
85
|
return payloadCache.getItem(url);
|
|
82
86
|
}
|
|
83
87
|
}
|
|
@@ -128,16 +132,21 @@ export default defineRenderHandler(async (event) => {
|
|
|
128
132
|
// Directly render payload routes
|
|
129
133
|
if (isRenderingPayload) {
|
|
130
134
|
const response = renderPayloadResponse(ssrContext);
|
|
131
|
-
if (
|
|
135
|
+
if (payloadCache) {
|
|
132
136
|
await payloadCache.setItem(ssrContext.url, response);
|
|
133
137
|
}
|
|
134
138
|
return response;
|
|
135
139
|
}
|
|
136
|
-
if (_PAYLOAD_EXTRACTION
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
140
|
+
if (_PAYLOAD_EXTRACTION) {
|
|
141
|
+
if (import.meta.prerender) {
|
|
142
|
+
// Hint nitro to prerender payload for this route
|
|
143
|
+
appendResponseHeader(event, "x-nitro-prerender", joinURL(ssrContext.url.replace(/\?.*$/, ""), PAYLOAD_FILENAME));
|
|
144
|
+
}
|
|
145
|
+
// Cache payload from the current SSR context so _payload.json requests can be served
|
|
146
|
+
// without a full re-render (during prerender via LRU+FS, at runtime via in-memory TTL cache)
|
|
147
|
+
if (payloadCache) {
|
|
148
|
+
await payloadCache.setItem(ssrContext.url === "/" ? "/" : ssrContext.url.replace(/\/$/, ""), renderPayloadResponse(ssrContext));
|
|
149
|
+
}
|
|
141
150
|
}
|
|
142
151
|
const NO_SCRIPTS = NUXT_NO_SCRIPTS || routeOptions.noScripts;
|
|
143
152
|
// Setup head
|
|
@@ -167,7 +176,8 @@ export default defineRenderHandler(async (event) => {
|
|
|
167
176
|
}] }, headEntryOptions);
|
|
168
177
|
}
|
|
169
178
|
// 1. Preload payloads and app manifest
|
|
170
|
-
|
|
179
|
+
// Skip preload when inlining full payload in HTML (no separate fetch needed for initial load)
|
|
180
|
+
if (_PAYLOAD_EXTRACTION && !_PAYLOAD_INLINE && !NO_SCRIPTS) {
|
|
171
181
|
ssrContext.head.push({ link: [NUXT_JSON_PAYLOADS ? {
|
|
172
182
|
rel: "preload",
|
|
173
183
|
as: "fetch",
|
|
@@ -179,18 +189,6 @@ export default defineRenderHandler(async (event) => {
|
|
|
179
189
|
href: payloadURL
|
|
180
190
|
}] }, headEntryOptions);
|
|
181
191
|
}
|
|
182
|
-
if (isAppManifestEnabled && ssrContext["~preloadManifest"] && !NO_SCRIPTS) {
|
|
183
|
-
ssrContext.head.push({ link: [{
|
|
184
|
-
rel: "preload",
|
|
185
|
-
as: "fetch",
|
|
186
|
-
fetchpriority: "low",
|
|
187
|
-
crossorigin: "anonymous",
|
|
188
|
-
href: buildAssetsURL(`builds/meta/${ssrContext.runtimeConfig.app.buildId}.json`)
|
|
189
|
-
}] }, {
|
|
190
|
-
...headEntryOptions,
|
|
191
|
-
tagPriority: "low"
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
192
|
// 2. Styles
|
|
195
193
|
if (inlinedStyles.length) {
|
|
196
194
|
ssrContext.head.push({ style: inlinedStyles });
|
|
@@ -225,7 +223,14 @@ export default defineRenderHandler(async (event) => {
|
|
|
225
223
|
ssrContext.head.push({ link: getPreloadLinks(ssrContext, renderer.rendererContext) }, headEntryOptions);
|
|
226
224
|
ssrContext.head.push({ link: getPrefetchLinks(ssrContext, renderer.rendererContext) }, headEntryOptions);
|
|
227
225
|
// 5. Payloads
|
|
228
|
-
ssrContext.head.push({ script:
|
|
226
|
+
ssrContext.head.push({ script: _PAYLOAD_INLINE ? NUXT_JSON_PAYLOADS ? renderPayloadJsonScript({
|
|
227
|
+
ssrContext,
|
|
228
|
+
data: ssrContext.payload
|
|
229
|
+
}) : renderPayloadScript({
|
|
230
|
+
ssrContext,
|
|
231
|
+
data: ssrContext.payload,
|
|
232
|
+
routeOptions
|
|
233
|
+
}) : NUXT_JSON_PAYLOADS ? renderPayloadJsonScript({
|
|
229
234
|
ssrContext,
|
|
230
235
|
data: splitPayload(ssrContext).initial,
|
|
231
236
|
src: payloadURL
|
|
@@ -234,13 +239,6 @@ export default defineRenderHandler(async (event) => {
|
|
|
234
239
|
data: splitPayload(ssrContext).initial,
|
|
235
240
|
routeOptions,
|
|
236
241
|
src: payloadURL
|
|
237
|
-
}) : NUXT_JSON_PAYLOADS ? renderPayloadJsonScript({
|
|
238
|
-
ssrContext,
|
|
239
|
-
data: ssrContext.payload
|
|
240
|
-
}) : renderPayloadScript({
|
|
241
|
-
ssrContext,
|
|
242
|
-
data: ssrContext.payload,
|
|
243
|
-
routeOptions
|
|
244
242
|
}) }, {
|
|
245
243
|
...headEntryOptions,
|
|
246
244
|
tagPosition: "bodyClose",
|
|
@@ -249,7 +247,7 @@ export default defineRenderHandler(async (event) => {
|
|
|
249
247
|
}
|
|
250
248
|
// 6. Scripts
|
|
251
249
|
if (!routeOptions.noScripts) {
|
|
252
|
-
const tagPosition = _PAYLOAD_EXTRACTION && !NUXT_JSON_PAYLOADS ? "bodyClose" : "head";
|
|
250
|
+
const tagPosition = _PAYLOAD_EXTRACTION && !_PAYLOAD_INLINE && !NUXT_JSON_PAYLOADS ? "bodyClose" : "head";
|
|
253
251
|
ssrContext.head.push({ script: Object.values(scripts).map((resource) => ({
|
|
254
252
|
type: resource.module ? "module" : null,
|
|
255
253
|
src: renderer.rendererContext.buildAssetsURL(resource.file),
|
|
@@ -281,6 +279,7 @@ export default defineRenderHandler(async (event) => {
|
|
|
281
279
|
}
|
|
282
280
|
};
|
|
283
281
|
});
|
|
282
|
+
export default handler;
|
|
284
283
|
function normalizeChunks(chunks) {
|
|
285
284
|
const result = [];
|
|
286
285
|
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,7 +1,7 @@
|
|
|
1
1
|
import { useStorage } from "nitropack/runtime";
|
|
2
2
|
// @ts-expect-error virtual file
|
|
3
|
-
import { NUXT_SHARED_DATA } from "#internal/nuxt/nitro-config.mjs";
|
|
4
|
-
export const payloadCache = import.meta.prerender ? useStorage("internal:nuxt:prerender:payload") : null;
|
|
3
|
+
import { NUXT_RUNTIME_PAYLOAD_EXTRACTION, NUXT_SHARED_DATA } from "#internal/nuxt/nitro-config.mjs";
|
|
4
|
+
export const payloadCache = import.meta.prerender ? useStorage("internal:nuxt:prerender:payload") : NUXT_RUNTIME_PAYLOAD_EXTRACTION ? useStorage("cache:nuxt:payload") : null;
|
|
5
5
|
export const islandCache = import.meta.prerender ? useStorage("internal:nuxt:prerender:island") : null;
|
|
6
6
|
export const islandPropCache = import.meta.prerender ? useStorage("internal:nuxt:prerender:island-props") : null;
|
|
7
7
|
export const sharedPrerenderPromises = import.meta.prerender && NUXT_SHARED_DATA ? new Map() : null;
|
|
@@ -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
|
}
|