Website Structure

This commit is contained in:
supalerk-ar66 2026-01-13 10:46:40 +07:00
parent 62812f2090
commit 71f0676a62
22365 changed files with 4265753 additions and 791 deletions

View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016-present - Nuxt Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,117 @@
[![Nuxt banner](https://github.com/nuxt/nuxt/blob/main/.github/assets/banner.svg)](https://nuxt.com)
# Nuxt
<p>
<a href="https://www.npmjs.com/package/nuxt"><img src="https://img.shields.io/npm/v/nuxt.svg?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a>
<a href="https://www.npmjs.com/package/nuxt"><img src="https://img.shields.io/npm/dm/nuxt.svg?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"></a>
<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>
<a href="https://nuxt.com"><img src="https://img.shields.io/badge/Nuxt%20Docs-18181B?logo=nuxt" alt="Website"></a>
<a href="https://chat.nuxt.dev"><img src="https://img.shields.io/badge/Nuxt%20Discord-18181B?logo=discord" alt="Discord"></a>
<a href="https://securityscorecards.dev/"><img src="https://api.securityscorecards.dev/projects/github.com/nuxt/nuxt/badge" alt="Nuxt openssf scorecard score"></a>
</p>
Nuxt is a free and open-source framework with an intuitive and extendable way to create type-safe, performant and production-grade full-stack web applications and websites with Vue.js.
It provides a number of features that make it easy to build fast, SEO-friendly, and scalable web applications, including:
- Server-side rendering, static site generation, hybrid rendering and edge-side rendering
- Automatic routing with code-splitting and pre-fetching
- Data fetching and state management
- Search engine optimization and defining meta tags
- Auto imports of components, composables and utils
- TypeScript with zero configuration
- Go full-stack with our server/ directory
- Extensible with [200+ modules](https://nuxt.com/modules)
- Deployment to a variety of [hosting platforms](https://nuxt.com/deploy)
- ...[and much more](https://nuxt.com) 🚀
### Table of Contents
- 🚀 [Getting Started](#getting-started)
- 💻 [ Vue Development](#vue-development)
- 📖 [Documentation](#documentation)
- 🧩 [Modules](#modules)
- ❤️ [Contribute](#contribute)
- 🏠 [Local Development](#local-development)
- 🛟 [Professional Support](#professional-support)
- 🔗 [Follow Us](#follow-us)
- ⚖️ [License](#license)
---
## <a name="getting-started">🚀 Getting Started</a>
Use the following command to create a new starter project. This will create a starter project with all the necessary files and dependencies:
```bash
npm create nuxt@latest <my-project>
```
> [!TIP]
> Discover also [nuxt.new](https://nuxt.new): Open a Nuxt starter on CodeSandbox, StackBlitz or locally to get up and running in a few seconds.
## <a name="vue-development">💻 Vue Development</a>
Simple, intuitive and powerful, Nuxt lets you write Vue components in a way that makes sense. Every repetitive task is automated, so you can focus on writing your full-stack Vue application with confidence.
Example of an `app.vue`:
```vue
<script setup lang="ts">
useSeoMeta({
title: 'Meet Nuxt',
description: 'The Intuitive Vue Framework.',
})
</script>
<template>
<div id="app">
<AppHeader />
<NuxtPage />
<AppFooter />
</div>
</template>
<style scoped>
#app {
background-color: #020420;
color: #00DC82;
}
</style>
```
## <a name="documentation">📖 Documentation</a>
We highly recommend you take a look at the [Nuxt documentation](https://nuxt.com/docs) to level up. Its a great resource for learning more about the framework. It covers everything from getting started to advanced topics.
## <a name="modules">🧩 Modules</a>
Discover our [list of modules](https://nuxt.com/modules) to supercharge your Nuxt project, created by the Nuxt team and community.
## <a name="contribute">❤️ Contribute</a>
We invite you to contribute and help improve Nuxt 💚
Here are a few ways you can get involved:
- **Reporting Bugs:** If you come across any bugs or issues, please check out the [reporting bugs guide](https://nuxt.com/docs/community/reporting-bugs) to learn how to submit a bug report.
- **Suggestions:** Have ideas to enhance Nuxt? We'd love to hear them! Check out the [contribution guide](https://nuxt.com/docs/community/contribution) to share your suggestions.
- **Questions:** If you have questions or need assistance, the [getting help guide](https://nuxt.com/docs/community/getting-help) provides resources to help you out.
## <a name="local-development">🏠 Local Development</a>
Follow the docs to [Set Up Your Local Development Environment](https://nuxt.com/docs/community/framework-contribution#setup) to contribute to the framework and documentation.
## <a name="professional-support">🛟 Professional Support</a>
- Technical audit & consulting: [Nuxt Experts](https://nuxt.com/enterprise/support)
- Custom development & more: [Nuxt Agencies Partners](https://nuxt.com/enterprise/agencies)
## <a name="follow-us">🔗 Follow Us</a>
<p valign="center">
<a href="https://go.nuxt.com/discord"><img width="20px" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/discord.svg" alt="Discord"></a>&nbsp;&nbsp;<a href="https://go.nuxt.com/x"><img width="20px" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/twitter.svg" alt="Twitter"></a>&nbsp;&nbsp;<a href="https://go.nuxt.com/github"><img width="20px" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/github.svg" alt="GitHub"></a>&nbsp;&nbsp;<a href="https://go.nuxt.com/bluesky"><img width="20px" src="https://github.com/nuxt/nuxt/blob/main/.github/assets/bluesky.svg" alt="Bluesky"></a>
</p>
## <a name="license">⚖️ License</a>
[MIT](https://github.com/nuxt/nuxt/blob/main/LICENSE)

View file

@ -0,0 +1,47 @@
import { Nuxt } from '@nuxt/schema';
import { Nitro } from 'nitropack/types';
import { H3Event } from 'h3';
import { LogObject } from 'consola';
import { NuxtRenderHTMLContext, NuxtIslandResponse, NuxtIslandContext } from 'nuxt/app';
import { RuntimeConfig } from 'nuxt/schema';
declare module 'nitropack' {
interface NitroRuntimeConfigApp {
buildAssetsDir: string;
cdnURL: string;
}
interface NitroRuntimeConfig extends RuntimeConfig {
}
interface NitroRouteConfig {
ssr?: boolean;
noScripts?: boolean;
/** @deprecated Use `noScripts` instead */
experimentalNoScripts?: boolean;
}
interface NitroRouteRules {
ssr?: boolean;
noScripts?: boolean;
/** @deprecated Use `noScripts` instead */
experimentalNoScripts?: boolean;
appMiddleware?: Record<string, boolean>;
}
interface NitroRuntimeHooks {
'dev:ssr-logs': (ctx: {
logs: LogObject[];
path: string;
}) => void | Promise<void>;
'render:html': (htmlContext: NuxtRenderHTMLContext, context: {
event: H3Event;
}) => void | Promise<void>;
'render:island': (islandResponse: NuxtIslandResponse, context: {
event: H3Event;
islandContext: NuxtIslandContext;
}) => void | Promise<void>;
}
}
declare function bundle(nuxt: Nuxt & {
_nitro?: Nitro;
}): Promise<void>;
export { bundle };

View file

@ -0,0 +1,47 @@
import { Nuxt } from '@nuxt/schema';
import { Nitro } from 'nitropack/types';
import { H3Event } from 'h3';
import { LogObject } from 'consola';
import { NuxtRenderHTMLContext, NuxtIslandResponse, NuxtIslandContext } from 'nuxt/app';
import { RuntimeConfig } from 'nuxt/schema';
declare module 'nitropack' {
interface NitroRuntimeConfigApp {
buildAssetsDir: string;
cdnURL: string;
}
interface NitroRuntimeConfig extends RuntimeConfig {
}
interface NitroRouteConfig {
ssr?: boolean;
noScripts?: boolean;
/** @deprecated Use `noScripts` instead */
experimentalNoScripts?: boolean;
}
interface NitroRouteRules {
ssr?: boolean;
noScripts?: boolean;
/** @deprecated Use `noScripts` instead */
experimentalNoScripts?: boolean;
appMiddleware?: Record<string, boolean>;
}
interface NitroRuntimeHooks {
'dev:ssr-logs': (ctx: {
logs: LogObject[];
path: string;
}) => void | Promise<void>;
'render:html': (htmlContext: NuxtRenderHTMLContext, context: {
event: H3Event;
}) => void | Promise<void>;
'render:island': (islandResponse: NuxtIslandResponse, context: {
event: H3Event;
islandContext: NuxtIslandContext;
}) => void | Promise<void>;
}
}
declare function bundle(nuxt: Nuxt & {
_nitro?: Nitro;
}): Promise<void>;
export { bundle };

View file

@ -0,0 +1,801 @@
import { fileURLToPath, pathToFileURL } from 'node:url';
import { existsSync, promises, readFileSync } from 'node:fs';
import { cpus } from 'node:os';
import process from 'node:process';
import { readFile, mkdir, writeFile } from 'node:fs/promises';
import { randomUUID } from 'node:crypto';
import { dirname, relative, resolve, join, isAbsolute } from 'pathe';
import { readPackageJSON } from 'pkg-types';
import { toRouteMatcher, createRouter, exportMatcher } from 'radix3';
import { withTrailingSlash, joinURL } from 'ufo';
import { createNitro, scanHandlers, writeTypes, copyPublicAssets, prepare, build, prerender, createDevServer } from 'nitropack';
import { getLayerDirectories, getDirectory, resolveNuxtModule, addTemplate, resolveAlias, addPlugin, resolveIgnorePatterns, createIsIgnored, addVitePlugin, logger, findPath } from '@nuxt/kit';
import escapeRE from 'escape-string-regexp';
import { defu } from 'defu';
import { dynamicEventHandler, defineEventHandler } from 'h3';
import { isWindows } from 'std-env';
import { ImpoundPlugin } from 'impound';
import { resolveModulePath } from 'exsolve';
const version = "3.20.2";
function toArray(value) {
return Array.isArray(value) ? value : [value];
}
let _distDir = dirname(fileURLToPath(import.meta.url));
if (/(?:chunks|shared)$/.test(_distDir)) {
_distDir = dirname(_distDir);
}
const distDir = _distDir;
const template = () => {
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{fill:none;stroke:#00dc82;stroke-width:4px;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:128;stroke-dashoffset:128;animation:nuxt-spa-loading-move 3s linear infinite}@keyframes nuxt-spa-loading-move{to{stroke-dashoffset:-128}}</style>';
};
function createImportProtectionPatterns(nuxt, options) {
const patterns = [];
const context = contextFlags[options.context];
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`." : "")
]);
patterns.push([
/^((~|~~|@|@@)?\/)?nuxt\.config(\.|$)/,
"Importing directly from a `nuxt.config` file is not allowed. Instead, use runtime config or a module."
]);
patterns.push([/(^|node_modules\/)@vue\/composition-api/]);
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."
]);
}
}
for (const i of [/(^|node_modules\/)@nuxt\/(cli|kit|test-utils)/, /(^|node_modules\/)nuxi/, /(^|node_modules\/)nitro(?:pack)?(?:-nightly)?(?:$|\/)(?!(?:dist\/)?(?:node_modules|presets|runtime|types))/, /(^|node_modules\/)nuxt\/(config|kit|schema)/]) {
patterns.push([i, `This module cannot be imported in ${context}.`]);
}
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}.`]);
}
}
if (options.context === "nuxt-app" || options.context === "shared") {
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}.`
]);
}
return patterns;
}
const contextFlags = {
"nitro-app": "server runtime",
"nuxt-app": "the Vue part of your app",
"shared": "the #shared directory"
};
const nitroSchemaTemplate = {
filename: "types/nitro-nuxt.d.ts",
async getContents({ nuxt }) {
const references = [];
const declarations = [];
await nuxt.callHook("nitro:prepare:types", { references, declarations });
const sourceDir = join(nuxt.options.buildDir, "types");
const lines = [
...references.map((ref) => {
if ("path" in ref && isAbsolute(ref.path)) {
ref.path = relative(sourceDir, ref.path);
}
return `/// <reference ${renderAttrs(ref)} />`;
}),
...declarations
];
return (
/* typescript */
`
${lines.join("\n")}
/// <reference path="./schema.d.ts" />
import type { RuntimeConfig } from 'nuxt/schema'
import type { H3Event } from 'h3'
import type { LogObject } from 'consola'
import type { NuxtIslandContext, NuxtIslandResponse, NuxtRenderHTMLContext } from 'nuxt/app'
declare module 'nitropack' {
interface NitroRuntimeConfigApp {
buildAssetsDir: string
cdnURL: string
}
interface NitroRuntimeConfig extends RuntimeConfig {}
interface NitroRouteConfig {
ssr?: boolean
noScripts?: boolean
/** @deprecated Use \`noScripts\` instead */
experimentalNoScripts?: boolean
}
interface NitroRouteRules {
ssr?: boolean
noScripts?: boolean
/** @deprecated Use \`noScripts\` instead */
experimentalNoScripts?: boolean
appMiddleware?: Record<string, boolean>
}
interface NitroRuntimeHooks {
'dev:ssr-logs': (ctx: { logs: LogObject[], path: string }) => void | Promise<void>
'render:html': (htmlContext: NuxtRenderHTMLContext, context: { event: H3Event }) => void | Promise<void>
'render:island': (islandResponse: NuxtIslandResponse, context: { event: H3Event, islandContext: NuxtIslandContext }) => void | Promise<void>
}
}
`
);
}
};
function renderAttr(key, value) {
return value ? `${key}="${value}"` : "";
}
function renderAttrs(obj) {
const attrs = [];
for (const key in obj) {
attrs.push(renderAttr(key, obj[key]));
}
return attrs.join(" ");
}
const logLevelMapReverse = {
silent: 0,
info: 3,
verbose: 3
};
const NODE_MODULES_RE = /(?<=\/)node_modules\/(.+)$/;
const PNPM_NODE_MODULES_RE = /\.pnpm\/.+\/node_modules\/(.+)$/;
async function bundle(nuxt) {
const layerDirs = getLayerDirectories(nuxt);
const excludePaths = [];
for (const dirs of layerDirs) {
const paths = [
dirs.root.match(NODE_MODULES_RE)?.[1]?.replace(/\/$/, ""),
dirs.root.match(PNPM_NODE_MODULES_RE)?.[1]?.replace(/\/$/, "")
];
for (const dir of paths) {
if (dir) {
excludePaths.push(escapeRE(dir));
}
}
}
const layerPublicAssetsDirs = [];
for (const dirs of layerDirs) {
if (existsSync(dirs.public)) {
layerPublicAssetsDirs.push({ dir: dirs.public });
}
}
const excludePattern = excludePaths.length ? [new RegExp(`node_modules\\/(?!${excludePaths.join("|")})`)] : [/node_modules/];
const rootDirWithSlash = withTrailingSlash(nuxt.options.rootDir);
const moduleEntryPaths = [];
for (const m of nuxt.options._installedModules) {
const path = m.meta?.rawPath || m.entryPath;
if (path) {
moduleEntryPaths.push(getDirectory(path));
}
}
const modules = await resolveNuxtModule(rootDirWithSlash, moduleEntryPaths);
addTemplate(nitroSchemaTemplate);
const sharedDirs = /* @__PURE__ */ new Set();
const isNuxtV4 = nuxt.options.future?.compatibilityVersion === 4;
if (isNuxtV4 && (nuxt.options.nitro.imports !== false && nuxt.options.imports.scan !== false)) {
for (const layer of nuxt.options._layers) {
if (layer.config?.imports?.scan === false) {
continue;
}
sharedDirs.add(resolve(layer.config.rootDir, layer.config.dir?.shared ?? "shared", "utils"));
sharedDirs.add(resolve(layer.config.rootDir, layer.config.dir?.shared ?? "shared", "types"));
}
}
nuxt.options.nitro.plugins ||= [];
nuxt.options.nitro.plugins = nuxt.options.nitro.plugins.map((plugin) => plugin ? resolveAlias(plugin, nuxt.options.alias) : plugin);
if (nuxt.options.dev && nuxt.options.features.devLogs) {
addPlugin(resolve(nuxt.options.appDir, "plugins/dev-server-logs"));
nuxt.options.nitro.plugins.push(resolve(distDir, "runtime/plugins/dev-server-logs"));
nuxt.options.nitro.externals = defu(nuxt.options.nitro.externals, {
inline: [/#internal\/dev-server-logs-options/]
});
nuxt.options.nitro.virtual = defu(nuxt.options.nitro.virtual, {
"#internal/dev-server-logs-options": () => `export const rootDir = ${JSON.stringify(nuxt.options.rootDir)};`
});
}
if (nuxt.options.experimental.componentIslands) {
nuxt.options.nitro.virtual ||= {};
nuxt.options.nitro.virtual["#internal/nuxt/island-renderer.mjs"] = () => {
if (nuxt.options.dev || nuxt.options.experimental.componentIslands !== "auto" || nuxt.apps.default?.pages?.some((p) => p.mode === "server") || nuxt.apps.default?.components?.some((c) => c.mode === "server" && !nuxt.apps.default?.components.some((other) => other.pascalName === c.pascalName && other.mode === "client"))) {
return `export { default } from '${resolve(distDir, "runtime/handlers/island")}'`;
}
return `import { defineEventHandler } from 'h3'; export default defineEventHandler(() => {});`;
};
nuxt.options.nitro.handlers ||= [];
nuxt.options.nitro.handlers.push({
route: "/__nuxt_island/**",
handler: "#internal/nuxt/island-renderer.mjs"
});
if (!nuxt.options.ssr && nuxt.options.experimental.componentIslands !== "auto") {
nuxt.options.ssr = true;
nuxt.options.nitro.routeRules ||= {};
nuxt.options.nitro.routeRules["/**"] = defu(nuxt.options.nitro.routeRules["/**"], { ssr: false });
}
}
const mockProxy = resolveModulePath("mocked-exports/proxy", { from: import.meta.url });
const { version: nuxtVersion } = await readPackageJSON("nuxt", { from: import.meta.url });
const nitroConfig = defu(nuxt.options.nitro, {
debug: nuxt.options.debug ? nuxt.options.debug.nitro : false,
rootDir: nuxt.options.rootDir,
workspaceDir: nuxt.options.workspaceDir,
srcDir: nuxt.options.serverDir,
dev: nuxt.options.dev,
buildDir: nuxt.options.buildDir,
experimental: {
asyncContext: nuxt.options.experimental.asyncContext,
typescriptBundlerResolution: nuxt.options.future.typescriptBundlerResolution || nuxt.options.typescript?.tsConfig?.compilerOptions?.moduleResolution?.toLowerCase() === "bundler" || nuxt.options.nitro.typescript?.tsConfig?.compilerOptions?.moduleResolution?.toLowerCase() === "bundler"
},
framework: {
name: "nuxt",
version: nuxtVersion || version
},
imports: {
autoImport: nuxt.options.imports.autoImport,
dirs: [...sharedDirs],
imports: [
{
as: "__buildAssetsURL",
name: "buildAssetsURL",
from: resolve(distDir, "runtime/utils/paths")
},
{
as: "__publicAssetsURL",
name: "publicAssetsURL",
from: resolve(distDir, "runtime/utils/paths")
},
{
// TODO: Remove after https://github.com/nitrojs/nitro/issues/1049
as: "defineAppConfig",
name: "defineAppConfig",
from: resolve(distDir, "runtime/utils/config"),
priority: -1
}
],
exclude: [...excludePattern, /[\\/]\.git[\\/]/]
},
esbuild: {
options: { exclude: excludePattern }
},
analyze: !nuxt.options.test && nuxt.options.build.analyze && (nuxt.options.build.analyze === true || nuxt.options.build.analyze.enabled) ? {
template: "treemap",
projectRoot: nuxt.options.rootDir,
filename: join(nuxt.options.analyzeDir, "{name}.html")
} : false,
scanDirs: layerDirs.map((dirs) => dirs.server),
renderer: resolve(distDir, "runtime/handlers/renderer"),
errorHandler: resolve(distDir, "runtime/handlers/error"),
nodeModulesDirs: nuxt.options.modulesDir,
handlers: nuxt.options.serverHandlers,
devHandlers: [],
baseURL: nuxt.options.app.baseURL,
virtual: {
"#internal/nuxt.config.mjs": () => nuxt.vfs["#build/nuxt.config.mjs"],
"#spa-template": async () => `export const template = ${JSON.stringify(await spaLoadingTemplate(nuxt))}`,
// this will be overridden in vite plugin
"#internal/entry-chunk.mjs": () => `export const entryFileName = undefined`,
"#internal/nuxt/entry-ids.mjs": () => `export default []`
},
routeRules: {
"/__nuxt_error": { cache: false }
},
appConfig: nuxt.options.appConfig,
appConfigFiles: layerDirs.map((dirs) => join(dirs.app, "app.config")),
typescript: {
strict: true,
generateTsConfig: true,
tsconfigPath: "tsconfig.server.json",
tsConfig: {
compilerOptions: {
lib: ["esnext", "webworker", "dom.iterable"]
},
include: [
join(nuxt.options.buildDir, "types/nitro-nuxt.d.ts"),
...modules.flatMap((m) => {
const moduleDir = relativeWithDot(nuxt.options.buildDir, m);
return [
join(moduleDir, "runtime/server"),
join(moduleDir, "dist/runtime/server")
];
}),
...layerDirs.map((dirs) => relativeWithDot(nuxt.options.buildDir, join(dirs.server, "**/*"))),
...layerDirs.map((dirs) => relativeWithDot(nuxt.options.buildDir, join(dirs.shared, "**/*.d.ts")))
],
exclude: [
...nuxt.options.modulesDir.map((m) => relativeWithDot(nuxt.options.buildDir, m)),
// nitro generate output: https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/core/nitro.ts#L186
relativeWithDot(nuxt.options.buildDir, resolve(nuxt.options.rootDir, "dist"))
]
}
},
publicAssets: [
nuxt.options.dev ? { dir: resolve(nuxt.options.buildDir, "dist/client") } : {
dir: join(nuxt.options.buildDir, "dist/client", nuxt.options.app.buildAssetsDir),
maxAge: 31536e3,
baseURL: nuxt.options.app.buildAssetsDir
},
...layerPublicAssetsDirs
],
prerender: {
ignoreUnprefixedPublicAssets: true,
failOnError: true,
concurrency: cpus().length * 4 || 4,
routes: [].concat(nuxt.options.generate.routes)
},
sourceMap: nuxt.options.sourcemap.server,
externals: {
inline: [
...nuxt.options.dev ? [] : [
...nuxt.options.experimental.externalVue ? [] : ["vue", "@vue/"],
"@nuxt/",
nuxt.options.buildDir
],
...nuxt.options.build.transpile.filter((i) => typeof i === "string"),
"nuxt/dist",
"nuxt3/dist",
"nuxt-nightly/dist",
distDir,
// Ensure app config files have auto-imports injected even if they are pure .js files
...layerDirs.map((dirs) => join(dirs.app, "app.config"))
],
traceInclude: [
// force include files used in generated code from the runtime-compiler
...nuxt.options.vue.runtimeCompiler && !nuxt.options.experimental.externalVue ? [
...nuxt.options.modulesDir.reduce((targets, path) => {
const serverRendererPath = resolve(path, "vue/server-renderer/index.js");
if (existsSync(serverRendererPath)) {
targets.push(serverRendererPath);
}
return targets;
}, [])
] : []
]
},
alias: {
// Vue 3 mocks
...nuxt.options.vue.runtimeCompiler || nuxt.options.experimental.externalVue ? {} : {
"estree-walker": mockProxy,
"@babel/parser": mockProxy,
"@vue/compiler-core": mockProxy,
"@vue/compiler-dom": mockProxy,
"@vue/compiler-ssr": mockProxy
},
"@vue/devtools-api": "vue-devtools-stub",
// Nuxt aliases
...nuxt.options.alias,
// Paths
"#internal/nuxt/paths": resolve(distDir, "runtime/utils/paths")
},
replace: {
"process.env.NUXT_NO_SSR": nuxt.options.ssr === false,
"process.env.NUXT_EARLY_HINTS": nuxt.options.experimental.writeEarlyHints !== false,
"process.env.NUXT_NO_SCRIPTS": String(nuxt.options.features.noScripts === "all" || !!nuxt.options.features.noScripts && !nuxt.options.dev),
"process.env.NUXT_INLINE_STYLES": !!nuxt.options.features.inlineStyles,
"process.env.PARSE_ERROR_DATA": String(!!nuxt.options.experimental.parseErrorData),
"process.env.NUXT_JSON_PAYLOADS": !!nuxt.options.experimental.renderJsonPayloads,
"process.env.NUXT_ASYNC_CONTEXT": !!nuxt.options.experimental.asyncContext,
"process.env.NUXT_SHARED_DATA": !!nuxt.options.experimental.sharedPrerenderData,
"process.dev": nuxt.options.dev,
"__VUE_PROD_DEVTOOLS__": false
},
rollupConfig: {
output: {
generatedCode: {
symbols: true
// temporary fix for https://github.com/vuejs/core/issues/8351
}
},
plugins: []
},
logLevel: logLevelMapReverse[nuxt.options.logLevel]
});
nitroConfig.srcDir = resolve(nuxt.options.rootDir, nuxt.options.srcDir, nitroConfig.srcDir);
nitroConfig.ignore ||= [];
nitroConfig.ignore.push(
...resolveIgnorePatterns(nitroConfig.srcDir),
`!${join(nuxt.options.buildDir, "dist/client", nuxt.options.app.buildAssetsDir, "**/*")}`
);
if (nuxt.options.experimental.appManifest) {
const buildId = nuxt.options.runtimeConfig.app.buildId ||= nuxt.options.buildId;
const buildTimestamp = Date.now();
const manifestPrefix = joinURL(nuxt.options.app.buildAssetsDir, "builds");
const tempDir = join(nuxt.options.buildDir, "manifest");
nitroConfig.prerender ||= {};
nitroConfig.prerender.ignore ||= [];
nitroConfig.prerender.ignore.push(joinURL(nuxt.options.app.baseURL, manifestPrefix));
nitroConfig.publicAssets.unshift(
// build manifest
{
dir: join(tempDir, "meta"),
maxAge: 31536e3,
baseURL: joinURL(manifestPrefix, "meta")
},
// latest build
{
dir: tempDir,
maxAge: 1,
baseURL: manifestPrefix
}
);
nuxt.options.alias["#app-manifest"] = join(tempDir, `meta/${buildId}.json`);
if (!nuxt.options.dev) {
nuxt.hook("build:before", async () => {
await promises.mkdir(join(tempDir, "meta"), { recursive: true });
await promises.writeFile(join(tempDir, `meta/${buildId}.json`), JSON.stringify({}));
});
}
if (nuxt.options.future.compatibilityVersion !== 4) {
nuxt.hook("nitro:config", (config) => {
for (const value of Object.values(config.routeRules || {})) {
if ("experimentalNoScripts" in value) {
value.noScripts = value.experimentalNoScripts;
delete value.experimentalNoScripts;
}
}
});
}
nuxt.hook("nitro:config", (config) => {
config.alias ||= {};
config.alias["#app-manifest"] = join(tempDir, `meta/${buildId}.json`);
const rules = config.routeRules;
for (const rule in rules) {
if (!rules[rule].appMiddleware) {
continue;
}
const value = rules[rule].appMiddleware;
if (typeof value === "string") {
rules[rule].appMiddleware = { [value]: true };
} else if (Array.isArray(value)) {
const normalizedRules = {};
for (const middleware of value) {
normalizedRules[middleware] = true;
}
rules[rule].appMiddleware = normalizedRules;
}
}
});
nuxt.hook("nitro:init", (nitro2) => {
nitro2.hooks.hook("rollup:before", async (nitro3) => {
const routeRules = {};
const _routeRules = nitro3.options.routeRules;
const validManifestKeys = /* @__PURE__ */ new Set(["prerender", "redirect", "appMiddleware"]);
for (const key in _routeRules) {
if (key === "/__nuxt_error") {
continue;
}
let hasRules = false;
const filteredRules = {};
for (const routeKey in _routeRules[key]) {
const value = _routeRules[key][routeKey];
if (value && validManifestKeys.has(routeKey)) {
if (routeKey === "redirect") {
filteredRules[routeKey] = typeof value === "string" ? value : value.to;
} else {
filteredRules[routeKey] = value;
}
hasRules = true;
}
}
if (hasRules) {
routeRules[key] = filteredRules;
}
}
const prerenderedRoutes = /* @__PURE__ */ new Set();
const routeRulesMatcher = toRouteMatcher(
createRouter({ routes: routeRules })
);
if (nitro3._prerenderedRoutes?.length) {
const payloadSuffix = nuxt.options.experimental.renderJsonPayloads ? "/_payload.json" : "/_payload.js";
for (const route of nitro3._prerenderedRoutes) {
if (!route.error && route.route.endsWith(payloadSuffix)) {
const url = route.route.slice(0, -payloadSuffix.length) || "/";
const rules = defu({}, ...routeRulesMatcher.matchAll(url).reverse());
if (!rules.prerender) {
prerenderedRoutes.add(url);
}
}
}
}
const manifest = {
id: buildId,
timestamp: buildTimestamp,
matcher: exportMatcher(routeRulesMatcher),
prerendered: nuxt.options.dev ? [] : [...prerenderedRoutes]
};
await promises.mkdir(join(tempDir, "meta"), { recursive: true });
await promises.writeFile(join(tempDir, "latest.json"), JSON.stringify({
id: buildId,
timestamp: buildTimestamp
}));
await promises.writeFile(join(tempDir, `meta/${buildId}.json`), JSON.stringify(manifest));
});
});
}
if (!nuxt.options.experimental.appManifest) {
nuxt.options.alias["#app-manifest"] = mockProxy;
}
const FORWARD_SLASH_RE = /\//g;
if (!nuxt.options.ssr) {
nitroConfig.virtual["#build/dist/server/server.mjs"] = "export default () => {}";
if (process.platform === "win32") {
nitroConfig.virtual["#build/dist/server/server.mjs".replace(FORWARD_SLASH_RE, "\\")] = "export default () => {}";
}
}
if (nuxt.options.dev || nuxt.options.builder === "@nuxt/webpack-builder" || nuxt.options.builder === "@nuxt/rspack-builder") {
nitroConfig.virtual["#build/dist/server/styles.mjs"] = "export default {}";
if (process.platform === "win32") {
nitroConfig.virtual["#build/dist/server/styles.mjs".replace(FORWARD_SLASH_RE, "\\")] = "export default {}";
}
}
if (nuxt.options.experimental.respectNoSSRHeader) {
nitroConfig.handlers ||= [];
nitroConfig.handlers.push({
handler: resolve(distDir, "runtime/middleware/no-ssr"),
middleware: true
});
}
nitroConfig.rollupConfig.plugins = await nitroConfig.rollupConfig.plugins || [];
nitroConfig.rollupConfig.plugins = toArray(nitroConfig.rollupConfig.plugins);
const sharedDir = withTrailingSlash(resolve(nuxt.options.rootDir, nuxt.options.dir.shared));
const relativeSharedDir = withTrailingSlash(relative(nuxt.options.rootDir, resolve(nuxt.options.rootDir, nuxt.options.dir.shared)));
const sharedPatterns = [/^#shared\//, new RegExp("^" + escapeRE(sharedDir)), new RegExp("^" + escapeRE(relativeSharedDir))];
nitroConfig.rollupConfig.plugins.push(
ImpoundPlugin.rollup({
cwd: nuxt.options.rootDir,
include: sharedPatterns,
patterns: createImportProtectionPatterns(nuxt, { context: "shared" })
}),
ImpoundPlugin.rollup({
cwd: nuxt.options.rootDir,
patterns: createImportProtectionPatterns(nuxt, { context: "nitro-app" }),
exclude: [/node_modules[\\/]nitro(?:pack)?(?:-nightly)?[\\/]|(packages|@nuxt)[\\/]nitro-server(?:-nightly)?[\\/](src|dist)[\\/]runtime[\\/]/, ...sharedPatterns]
})
);
const isIgnored = createIsIgnored(nuxt);
nitroConfig.devStorage ??= {};
nitroConfig.devStorage.root ??= {
driver: "fs",
readOnly: true,
base: nitroConfig.rootDir,
watchOptions: {
ignored: [isIgnored]
}
};
nitroConfig.devStorage.src ??= {
driver: "fs",
readOnly: true,
base: nitroConfig.srcDir,
watchOptions: {
ignored: [isIgnored]
}
};
await nuxt.callHook("nitro:config", nitroConfig);
const excludedAlias = [/^@vue\/.*$/, "vue", /vue-router/, "vite/client", "#imports", "vue-demi", /^#app/, "~", "@", "~~", "@@"];
const basePath = nitroConfig.typescript.tsConfig.compilerOptions?.baseUrl ? resolve(nuxt.options.buildDir, nitroConfig.typescript.tsConfig.compilerOptions?.baseUrl) : nuxt.options.buildDir;
const aliases = nitroConfig.alias;
const tsConfig = nitroConfig.typescript.tsConfig;
tsConfig.compilerOptions ||= {};
tsConfig.compilerOptions.paths ||= {};
for (const _alias in aliases) {
const alias = _alias;
if (excludedAlias.some((pattern) => typeof pattern === "string" ? alias === pattern : pattern.test(alias))) {
continue;
}
if (alias in tsConfig.compilerOptions.paths) {
continue;
}
const absolutePath = resolve(basePath, aliases[alias]);
const isDirectory = aliases[alias].endsWith("/") || await promises.stat(absolutePath).then((r) => r.isDirectory()).catch(
() => null
/* file does not exist */
);
tsConfig.compilerOptions.paths[alias] = [absolutePath];
if (isDirectory) {
tsConfig.compilerOptions.paths[`${alias}/*`] = [`${absolutePath}/*`];
}
}
const nitro = await createNitro(nitroConfig, {
compatibilityDate: nuxt.options.compatibilityDate,
dotenv: nuxt.options._loadOptions?.dotenv
});
const spaLoadingTemplateFilePath = await spaLoadingTemplatePath(nuxt);
nuxt.hook("builder:watch", async (_event, relativePath) => {
const path = resolve(nuxt.options.srcDir, relativePath);
if (path === spaLoadingTemplateFilePath) {
await nitro.hooks.callHook("rollup:reload");
}
});
const cacheDir = resolve(nuxt.options.buildDir, "cache/nitro/prerender");
const cacheDriverPath = join(distDir, "runtime/utils/cache-driver.js");
await promises.rm(cacheDir, { recursive: true, force: true }).catch(() => {
});
nitro.options._config.storage = defu(nitro.options._config.storage, {
"internal:nuxt:prerender": {
// TODO: resolve upstream where file URLs are not being resolved/inlined correctly
driver: isWindows ? pathToFileURL(cacheDriverPath).href : cacheDriverPath,
base: cacheDir
}
});
nuxt._nitro = nitro;
await nuxt.callHook("nitro:init", nitro);
nitro.vfs = nuxt.vfs = nitro.vfs || nuxt.vfs || {};
nuxt.hook("close", () => nitro.hooks.callHook("close"));
nitro.hooks.hook("prerender:routes", (routes) => {
return nuxt.callHook("prerender:routes", { routes });
});
if (nuxt.options.vue.runtimeCompiler) {
addVitePlugin({
name: "nuxt:vue:runtime-compiler",
applyToEnvironment: (environment) => environment.name === "client",
enforce: "pre",
resolveId(id, importer) {
if (id === "vue") {
return this.resolve("vue/dist/vue.esm-bundler", importer, { skipSelf: true });
}
}
});
for (const hook of ["webpack:config", "rspack:config"]) {
nuxt.hook(hook, (configuration) => {
const clientConfig = configuration.find((config) => config.name === "client");
if (!clientConfig.resolve) {
clientConfig.resolve.alias = {};
}
if (Array.isArray(clientConfig.resolve.alias)) {
clientConfig.resolve.alias.push({
name: "vue",
alias: "vue/dist/vue.esm-bundler"
});
} else {
clientConfig.resolve.alias.vue = "vue/dist/vue.esm-bundler";
}
});
}
}
const devMiddlewareHandler = dynamicEventHandler();
nitro.options.devHandlers.unshift({ handler: devMiddlewareHandler });
nitro.options.devHandlers.push(...nuxt.options.devServerHandlers);
nitro.options.handlers.unshift({
route: "/__nuxt_error",
lazy: true,
handler: resolve(distDir, "runtime/handlers/renderer")
});
if (nuxt.options.experimental.chromeDevtoolsProjectSettings) {
const cacheDir2 = resolve(nuxt.options.rootDir, "node_modules/.cache/nuxt");
let projectConfiguration = await readFile(join(cacheDir2, "chrome-workspace.json"), "utf-8").then((r) => JSON.parse(r)).catch(() => null);
if (!projectConfiguration) {
projectConfiguration = { uuid: randomUUID() };
await mkdir(cacheDir2, { recursive: true });
await writeFile(join(cacheDir2, "chrome-workspace.json"), JSON.stringify(projectConfiguration), "utf-8");
}
nitro.options.devHandlers.push({
route: "/.well-known/appspecific/com.chrome.devtools.json",
handler: defineEventHandler(() => ({
workspace: {
...projectConfiguration,
root: nuxt.options.rootDir
}
}))
});
}
if (!nuxt.options.dev && nuxt.options.experimental.noVueServer) {
nitro.hooks.hook("rollup:before", (nitro2) => {
if (nitro2.options.preset === "nitro-prerender") {
return;
}
const nuxtErrorHandler = nitro2.options.handlers.findIndex((h) => h.route === "/__nuxt_error");
if (nuxtErrorHandler >= 0) {
nitro2.options.handlers.splice(nuxtErrorHandler, 1);
}
nitro2.options.renderer = void 0;
nitro2.options.errorHandler = "#internal/nitro/error";
});
}
nuxt.hook("prepare:types", async (opts) => {
if (!nuxt.options.dev) {
await scanHandlers(nitro);
await writeTypes(nitro);
}
opts.tsConfig.exclude ||= [];
opts.tsConfig.exclude.push(relative(nuxt.options.buildDir, resolve(nuxt.options.rootDir, nitro.options.output.dir)));
opts.references.push({ path: resolve(nuxt.options.buildDir, "types/nitro.d.ts") });
});
if (nitro.options.static) {
nitro.hooks.hook("prerender:routes", (routes) => {
for (const route of ["/200.html", "/404.html"]) {
routes.add(route);
}
if (!nuxt.options.ssr) {
routes.add("/index.html");
}
});
}
if (!nuxt.options.dev) {
nitro.hooks.hook("rollup:before", async (nitro2) => {
await copyPublicAssets(nitro2);
await nuxt.callHook("nitro:build:public-assets", nitro2);
});
}
async function symlinkDist() {
if (nitro.options.static) {
const distDir2 = resolve(nuxt.options.rootDir, "dist");
if (!existsSync(distDir2)) {
await promises.symlink(nitro.options.output.publicDir, distDir2, "junction").catch(() => {
});
}
}
}
nuxt.hook("build:done", async () => {
await nuxt.callHook("nitro:build:before", nitro);
await prepare(nitro);
if (nuxt.options.dev) {
return build(nitro);
}
await prerender(nitro);
logger.restoreAll();
await build(nitro);
logger.wrapAll();
await symlinkDist();
});
if (nuxt.options.dev) {
for (const builder of ["webpack", "rspack"]) {
nuxt.hook(`${builder}:compile`, ({ name, compiler }) => {
if (name === "server") {
const memfs = compiler.outputFileSystem;
nitro.options.virtual["#build/dist/server/server.mjs"] = () => memfs.readFileSync(join(nuxt.options.buildDir, "dist/server/server.mjs"), "utf-8");
}
});
nuxt.hook(`${builder}:compiled`, () => {
nuxt.server.reload();
});
}
nuxt.hook("vite:compiled", () => {
nuxt.server.reload();
});
nuxt.hook("server:devHandler", (h) => {
devMiddlewareHandler.set(h);
});
nuxt.server = createDevServer(nitro);
const waitUntilCompile = new Promise((resolve2) => nitro.hooks.hook("compiled", () => resolve2()));
nuxt.hook("build:done", () => waitUntilCompile);
}
}
const RELATIVE_RE = /^([^.])/;
function relativeWithDot(from, to) {
return relative(from, to).replace(RELATIVE_RE, "./$1") || ".";
}
async function spaLoadingTemplatePath(nuxt) {
if (typeof nuxt.options.spaLoadingTemplate === "string") {
return resolve(nuxt.options.srcDir, nuxt.options.spaLoadingTemplate);
}
const possiblePaths = nuxt.options._layers.map((layer) => resolve(layer.config.srcDir, layer.config.dir?.app || "app", "spa-loading-template.html"));
return await findPath(possiblePaths) ?? resolve(nuxt.options.srcDir, nuxt.options.dir?.app || "app", "spa-loading-template.html");
}
async function spaLoadingTemplate(nuxt) {
if (nuxt.options.spaLoadingTemplate === false) {
return "";
}
const spaLoadingTemplate2 = await spaLoadingTemplatePath(nuxt);
try {
if (existsSync(spaLoadingTemplate2)) {
return readFileSync(spaLoadingTemplate2, "utf-8").trim();
}
} catch {
}
if (nuxt.options.spaLoadingTemplate === true) {
return template();
}
if (nuxt.options.spaLoadingTemplate) {
logger.warn(`Could not load custom \`spaLoadingTemplate\` path as it does not exist: \`${nuxt.options.spaLoadingTemplate}\`.`);
}
return "";
}
export { bundle };

View file

@ -0,0 +1,3 @@
import type { NitroErrorHandler } from 'nitropack';
declare const _default: NitroErrorHandler;
export default _default;

View file

@ -0,0 +1,64 @@
import { joinURL, withQuery, withoutBase } from "ufo";
import { appendResponseHeader, getRequestHeaders, send, setResponseHeader, setResponseHeaders, setResponseStatus } from "h3";
import { isJsonRequest } from "../utils/error.js";
import { useRuntimeConfig } from "#internal/nitro";
import { useNitroApp } from "#internal/nitro/app";
import { generateErrorOverlayHTML } from "../utils/dev.js";
export default (async function errorhandler(error, event, { defaultHandler }) {
if (event.handled || isJsonRequest(event)) {
return;
}
const defaultRes = await defaultHandler(error, event, { json: true });
const statusCode = error.statusCode || 500;
if (statusCode === 404 && defaultRes.status === 302) {
setResponseHeaders(event, defaultRes.headers);
setResponseStatus(event, defaultRes.status, defaultRes.statusText);
return send(event, JSON.stringify(defaultRes.body, null, 2));
}
if (import.meta.dev && typeof defaultRes.body !== "string" && Array.isArray(defaultRes.body.stack)) {
defaultRes.body.stack = defaultRes.body.stack.join("\n");
}
const errorObject = defaultRes.body;
const url = new URL(errorObject.url);
errorObject.url = withoutBase(url.pathname, useRuntimeConfig(event).app.baseURL) + url.search + url.hash;
errorObject.message ||= "Server Error";
errorObject.data ||= error.data;
errorObject.statusMessage ||= error.statusMessage;
delete defaultRes.headers["content-type"];
delete defaultRes.headers["content-security-policy"];
setResponseHeaders(event, defaultRes.headers);
const reqHeaders = getRequestHeaders(event);
const isRenderingError = event.path.startsWith("/__nuxt_error") || !!reqHeaders["x-nuxt-error"];
const res = isRenderingError ? null : await useNitroApp().localFetch(
withQuery(joinURL(useRuntimeConfig(event).app.baseURL, "/__nuxt_error"), errorObject),
{
headers: { ...reqHeaders, "x-nuxt-error": "true" },
redirect: "manual"
}
).catch(() => null);
if (event.handled) {
return;
}
if (!res) {
const { template } = await import("../templates/error-500.js");
if (import.meta.dev) {
errorObject.description = errorObject.message;
}
setResponseHeader(event, "Content-Type", "text/html;charset=UTF-8");
return send(event, template(errorObject));
}
const html = await res.text();
for (const [header, value] of res.headers.entries()) {
if (header === "set-cookie") {
appendResponseHeader(event, header, value);
continue;
}
setResponseHeader(event, header, value);
}
setResponseStatus(event, res.status && res.status !== 200 ? res.status : defaultRes.status, res.statusText || defaultRes.statusText);
if (import.meta.dev && !import.meta.test && typeof html === "string") {
const prettyResponse = await defaultHandler(error, event, { json: false });
return send(event, html.replace("</body>", `${generateErrorOverlayHTML(prettyResponse.body, { startMinimized: 300 <= statusCode && statusCode < 500 })}</body>`));
}
return send(event, html);
});

View file

@ -0,0 +1,4 @@
import type { RenderResponse } from 'nitropack';
import type { NuxtIslandResponse } from 'nuxt/app';
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<NuxtIslandResponse | Partial<RenderResponse>>>;
export default _default;

View file

@ -0,0 +1,102 @@
import { destr } from "destr";
import { defineEventHandler, getQuery, readBody, setResponseHeaders } from "h3";
import { resolveUnrefHeadInput } from "@unhead/vue";
import { getRequestDependencies } from "vue-bundle-renderer/runtime";
import { getQuery as getURLQuery } from "ufo";
import { islandCache, islandPropCache } from "../utils/cache.js";
import { createSSRContext } from "../utils/renderer/app.js";
import { getSSRRenderer } from "../utils/renderer/build-files.js";
import { renderInlineStyles } from "../utils/renderer/inline-styles.js";
import { getClientIslandResponse, getServerComponentHTML, getSlotIslandResponse } from "../utils/renderer/islands.js";
import { useNitroApp } from "#internal/nitro";
const ISLAND_SUFFIX_RE = /\.json(?:\?.*)?$/;
export default defineEventHandler(async (event) => {
const nitroApp = useNitroApp();
setResponseHeaders(event, {
"content-type": "application/json;charset=utf-8",
"x-powered-by": "Nuxt"
});
if (import.meta.prerender && event.path && await islandCache.hasItem(event.path)) {
return islandCache.getItem(event.path);
}
const islandContext = await getIslandContext(event);
const ssrContext = {
...createSSRContext(event),
islandContext,
noSSR: false,
url: islandContext.url
};
const renderer = await getSSRRenderer();
const renderResult = await renderer.renderToString(ssrContext).catch(async (err) => {
await ssrContext.nuxt?.hooks.callHook("app:error", err);
throw err;
});
if (ssrContext.payload?.error) {
throw ssrContext.payload.error;
}
const inlinedStyles = await renderInlineStyles(ssrContext.modules ?? []);
await ssrContext.nuxt?.hooks.callHook("app:rendered", { ssrContext, renderResult });
if (inlinedStyles.length) {
ssrContext.head.push({ style: inlinedStyles });
}
if (import.meta.dev) {
const { styles } = getRequestDependencies(ssrContext, renderer.rendererContext);
const link = [];
for (const resource of Object.values(styles)) {
if ("inline" in getURLQuery(resource.file)) {
continue;
}
if (resource.file.includes("scoped") && !resource.file.includes("pages/")) {
link.push({ rel: "stylesheet", href: renderer.rendererContext.buildAssetsURL(resource.file), crossorigin: "" });
}
}
if (link.length) {
ssrContext.head.push({ link }, { mode: "server" });
}
}
const islandHead = {};
for (const entry of ssrContext.head.entries.values()) {
for (const [key, value] of Object.entries(resolveUnrefHeadInput(entry.input))) {
const currentValue = islandHead[key];
if (Array.isArray(currentValue)) {
currentValue.push(...value);
}
islandHead[key] = value;
}
}
islandHead.link ||= [];
islandHead.style ||= [];
const islandResponse = {
id: islandContext.id,
head: islandHead,
html: getServerComponentHTML(renderResult.html),
components: getClientIslandResponse(ssrContext),
slots: getSlotIslandResponse(ssrContext)
};
await nitroApp.hooks.callHook("render:island", islandResponse, { event, islandContext });
if (import.meta.prerender) {
await islandCache.setItem(`/__nuxt_island/${islandContext.name}_${islandContext.id}.json`, islandResponse);
await islandPropCache.setItem(`/__nuxt_island/${islandContext.name}_${islandContext.id}.json`, event.path);
}
return islandResponse;
});
async function getIslandContext(event) {
let url = event.path || "";
if (import.meta.prerender && event.path && await islandPropCache.hasItem(event.path)) {
url = await islandPropCache.getItem(event.path);
}
const componentParts = url.substring("/__nuxt_island".length + 1).replace(ISLAND_SUFFIX_RE, "").split("_");
const hashId = componentParts.length > 1 ? componentParts.pop() : void 0;
const componentName = componentParts.join("_");
const context = event.method === "GET" ? getQuery(event) : await readBody(event);
const ctx = {
url: "/",
...context,
id: hashId,
name: componentName,
props: destr(context.props) || {},
slots: {},
components: {}
};
return ctx;
}

View file

@ -0,0 +1,8 @@
export interface NuxtRenderResponse {
body: string;
statusCode: number;
statusMessage?: string;
headers: Record<string, string>;
}
declare const _default: any;
export default _default;

View file

@ -0,0 +1,238 @@
import { AsyncLocalStorage } from "node:async_hooks";
import process from "node:process";
import {
getPrefetchLinks,
getPreloadLinks,
getRequestDependencies,
renderResourceHeaders
} from "vue-bundle-renderer/runtime";
import { appendResponseHeader, createError, getQuery, getResponseStatus, getResponseStatusText, writeEarlyHints } from "h3";
import { getQuery as getURLQuery, joinURL } from "ufo";
import { propsToString, renderSSRHead } from "@unhead/vue/server";
import destr from "destr";
import { getRenderer } from "../utils/renderer/build-files.js";
import { payloadCache } from "../utils/cache.js";
import { renderPayloadJsonScript, renderPayloadResponse, renderPayloadScript, splitPayload } from "../utils/renderer/payload.js";
import { createSSRContext, setSSRError } from "../utils/renderer/app.js";
import { renderInlineStyles } from "../utils/renderer/inline-styles.js";
import { replaceIslandTeleports } from "../utils/renderer/islands.js";
import { defineRenderHandler, getRouteRules, useNitroApp } from "#internal/nitro";
import { renderSSRHeadOptions } from "#internal/unhead.config.mjs";
import { appHead, appTeleportAttrs, appTeleportTag, componentIslands, appManifest as isAppManifestEnabled } from "#internal/nuxt.config.mjs";
import entryIds from "#internal/nuxt/entry-ids.mjs";
import { entryFileName } from "#internal/entry-chunk.mjs";
import { buildAssetsURL, publicAssetsURL } from "#internal/nuxt/paths";
import { relative } from "pathe";
globalThis.__buildAssetsURL = buildAssetsURL;
globalThis.__publicAssetsURL = publicAssetsURL;
if (process.env.NUXT_ASYNC_CONTEXT && !("AsyncLocalStorage" in globalThis)) {
globalThis.AsyncLocalStorage = AsyncLocalStorage;
}
const HAS_APP_TELEPORTS = !!(appTeleportTag && appTeleportAttrs.id);
const APP_TELEPORT_OPEN_TAG = HAS_APP_TELEPORTS ? `<${appTeleportTag}${propsToString(appTeleportAttrs)}>` : "";
const APP_TELEPORT_CLOSE_TAG = HAS_APP_TELEPORTS ? `</${appTeleportTag}>` : "";
const PAYLOAD_URL_RE = process.env.NUXT_JSON_PAYLOADS ? /^[^?]*\/_payload.json(?:\?.*)?$/ : /^[^?]*\/_payload.js(?:\?.*)?$/;
const PAYLOAD_FILENAME = process.env.NUXT_JSON_PAYLOADS ? "_payload.json" : "_payload.js";
let entryPath;
export default defineRenderHandler(async (event) => {
const nitroApp = useNitroApp();
const ssrError = event.path.startsWith("/__nuxt_error") ? getQuery(event) : null;
if (ssrError && !("__unenv__" in event.node.req)) {
throw createError({
statusCode: 404,
statusMessage: "Page Not Found: /__nuxt_error"
});
}
const ssrContext = createSSRContext(event);
const headEntryOptions = { mode: "server" };
ssrContext.head.push(appHead, headEntryOptions);
if (ssrError) {
ssrError.statusCode &&= Number.parseInt(ssrError.statusCode);
if (process.env.PARSE_ERROR_DATA && typeof ssrError.data === "string") {
try {
ssrError.data = destr(ssrError.data);
} catch {
}
}
setSSRError(ssrContext, ssrError);
}
const isRenderingPayload = process.env.NUXT_PAYLOAD_EXTRACTION && PAYLOAD_URL_RE.test(ssrContext.url);
if (isRenderingPayload) {
const url = ssrContext.url.substring(0, ssrContext.url.lastIndexOf("/")) || "/";
ssrContext.url = url;
event._path = event.node.req.url = url;
if (import.meta.prerender && await payloadCache.hasItem(url)) {
return payloadCache.getItem(url);
}
}
const routeOptions = getRouteRules(event);
if (routeOptions.ssr === false) {
ssrContext.noSSR = true;
}
const _PAYLOAD_EXTRACTION = import.meta.prerender && process.env.NUXT_PAYLOAD_EXTRACTION && !ssrContext.noSSR;
const payloadURL = _PAYLOAD_EXTRACTION ? joinURL(ssrContext.runtimeConfig.app.cdnURL || ssrContext.runtimeConfig.app.baseURL, ssrContext.url.replace(/\?.*$/, ""), PAYLOAD_FILENAME) + "?" + ssrContext.runtimeConfig.app.buildId : void 0;
const renderer = await getRenderer(ssrContext);
if (process.env.NUXT_EARLY_HINTS && !isRenderingPayload && !import.meta.prerender) {
const { link: link2 } = renderResourceHeaders({}, renderer.rendererContext);
if (link2) {
writeEarlyHints(event, link2);
}
}
if (process.env.NUXT_INLINE_STYLES) {
for (const id of entryIds) {
ssrContext.modules.add(id);
}
}
const _rendered = await renderer.renderToString(ssrContext).catch(async (error) => {
if (ssrContext._renderResponse && error.message === "skipping render") {
return {};
}
const _err = !ssrError && ssrContext.payload?.error || error;
await ssrContext.nuxt?.hooks.callHook("app:error", _err);
throw _err;
});
const inlinedStyles = process.env.NUXT_INLINE_STYLES && !ssrContext._renderResponse && !isRenderingPayload ? await renderInlineStyles(ssrContext.modules ?? []) : [];
await ssrContext.nuxt?.hooks.callHook("app:rendered", { ssrContext, renderResult: _rendered });
if (ssrContext._renderResponse) {
return ssrContext._renderResponse;
}
if (ssrContext.payload?.error && !ssrError) {
throw ssrContext.payload.error;
}
if (isRenderingPayload) {
const response = renderPayloadResponse(ssrContext);
if (import.meta.prerender) {
await payloadCache.setItem(ssrContext.url, response);
}
return response;
}
if (_PAYLOAD_EXTRACTION) {
appendResponseHeader(event, "x-nitro-prerender", joinURL(ssrContext.url.replace(/\?.*$/, ""), PAYLOAD_FILENAME));
await payloadCache.setItem(ssrContext.url === "/" ? "/" : ssrContext.url.replace(/\/$/, ""), renderPayloadResponse(ssrContext));
}
const NO_SCRIPTS = process.env.NUXT_NO_SCRIPTS || routeOptions.noScripts;
const { styles, scripts } = getRequestDependencies(ssrContext, renderer.rendererContext);
if (entryFileName && !NO_SCRIPTS) {
let path = entryPath;
if (!path) {
path = buildAssetsURL(entryFileName);
if (ssrContext.runtimeConfig.app.cdnURL || /^(?:\/|\.+\/)/.test(path)) {
entryPath = path;
} else {
path = relative(event.path.replace(/\/[^/]+$/, "/"), joinURL("/", path));
if (!/^(?:\/|\.+\/)/.test(path)) {
path = `./${path}`;
}
}
}
ssrContext.head.push({
script: [{
tagPosition: "head",
tagPriority: -2,
type: "importmap",
innerHTML: JSON.stringify({ imports: { "#entry": path } })
}]
}, headEntryOptions);
}
if (_PAYLOAD_EXTRACTION && !NO_SCRIPTS) {
ssrContext.head.push({
link: [
process.env.NUXT_JSON_PAYLOADS ? { rel: "preload", as: "fetch", crossorigin: "anonymous", href: payloadURL } : { rel: "modulepreload", crossorigin: "", href: payloadURL }
]
}, headEntryOptions);
}
if (isAppManifestEnabled && ssrContext._preloadManifest && !NO_SCRIPTS) {
ssrContext.head.push({
link: [
{ rel: "preload", as: "fetch", fetchpriority: "low", crossorigin: "anonymous", href: buildAssetsURL(`builds/meta/${ssrContext.runtimeConfig.app.buildId}.json`) }
]
}, { ...headEntryOptions, tagPriority: "low" });
}
if (inlinedStyles.length) {
ssrContext.head.push({ style: inlinedStyles });
}
const link = [];
for (const resource of Object.values(styles)) {
if (import.meta.dev && "inline" in getURLQuery(resource.file)) {
continue;
}
link.push({ rel: "stylesheet", href: renderer.rendererContext.buildAssetsURL(resource.file), crossorigin: "" });
}
if (link.length) {
ssrContext.head.push({ link }, headEntryOptions);
}
if (!NO_SCRIPTS) {
ssrContext.head.push({
link: getPreloadLinks(ssrContext, renderer.rendererContext)
}, headEntryOptions);
ssrContext.head.push({
link: getPrefetchLinks(ssrContext, renderer.rendererContext)
}, headEntryOptions);
ssrContext.head.push({
script: _PAYLOAD_EXTRACTION ? process.env.NUXT_JSON_PAYLOADS ? renderPayloadJsonScript({ ssrContext, data: splitPayload(ssrContext).initial, src: payloadURL }) : renderPayloadScript({ ssrContext, data: splitPayload(ssrContext).initial, src: payloadURL }) : process.env.NUXT_JSON_PAYLOADS ? renderPayloadJsonScript({ ssrContext, data: ssrContext.payload }) : renderPayloadScript({ ssrContext, data: ssrContext.payload })
}, {
...headEntryOptions,
// this should come before another end of body scripts
tagPosition: "bodyClose",
tagPriority: "high"
});
}
if (!routeOptions.noScripts) {
const tagPosition = _PAYLOAD_EXTRACTION && !process.env.NUXT_JSON_PAYLOADS ? "bodyClose" : "head";
ssrContext.head.push({
script: Object.values(scripts).map((resource) => ({
type: resource.module ? "module" : null,
src: renderer.rendererContext.buildAssetsURL(resource.file),
defer: resource.module ? null : true,
// if we are rendering script tag payloads that import an async payload
// we need to ensure this resolves before executing the Nuxt entry
tagPosition,
crossorigin: ""
}))
}, headEntryOptions);
}
const { headTags, bodyTags, bodyTagsOpen, htmlAttrs, bodyAttrs } = await renderSSRHead(ssrContext.head, renderSSRHeadOptions);
const htmlContext = {
htmlAttrs: htmlAttrs ? [htmlAttrs] : [],
head: normalizeChunks([headTags]),
bodyAttrs: bodyAttrs ? [bodyAttrs] : [],
bodyPrepend: normalizeChunks([bodyTagsOpen, ssrContext.teleports?.body]),
body: [
componentIslands ? replaceIslandTeleports(ssrContext, _rendered.html) : _rendered.html,
APP_TELEPORT_OPEN_TAG + (HAS_APP_TELEPORTS ? joinTags([ssrContext.teleports?.[`#${appTeleportAttrs.id}`]]) : "") + APP_TELEPORT_CLOSE_TAG
],
bodyAppend: [bodyTags]
};
await nitroApp.hooks.callHook("render:html", htmlContext, { event });
return {
body: renderHTMLDocument(htmlContext),
statusCode: getResponseStatus(event),
statusMessage: getResponseStatusText(event),
headers: {
"content-type": "text/html;charset=utf-8",
"x-powered-by": "Nuxt"
}
};
});
function normalizeChunks(chunks) {
const result = [];
for (const _chunk of chunks) {
const chunk = _chunk?.trim();
if (chunk) {
result.push(chunk);
}
}
return result;
}
function joinTags(tags) {
return tags.join("");
}
function joinAttrs(chunks) {
if (chunks.length === 0) {
return "";
}
return " " + chunks.join(" ");
}
function renderHTMLDocument(html) {
return `<!DOCTYPE html><html${joinAttrs(html.htmlAttrs)}><head>${joinTags(html.head)}</head><body${joinAttrs(html.bodyAttrs)}>${joinTags(html.bodyPrepend)}${joinTags(html.body)}${joinTags(html.bodyAppend)}</body></html>`;
}

View file

@ -0,0 +1,2 @@
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, void>;
export default _default;

View file

@ -0,0 +1,7 @@
import { defineEventHandler, getRequestHeader } from "h3";
export default defineEventHandler((event) => {
if (getRequestHeader(event, "x-nuxt-no-ssr")) {
event.context.nuxt ||= {};
event.context.nuxt.noSSR = true;
}
});

View file

@ -0,0 +1,3 @@
import type { NitroApp } from '#internal/nitro/app';
declare const _default: (nitroApp: NitroApp) => void;
export default _default;

View file

@ -0,0 +1,82 @@
import { AsyncLocalStorage } from "node:async_hooks";
import { consola } from "consola";
import { stringify } from "devalue";
import { withTrailingSlash } from "ufo";
import { getContext } from "unctx";
import { captureRawStackTrace, parseRawStackTrace } from "errx";
import { isVNode } from "vue";
import { rootDir } from "#internal/dev-server-logs-options";
import { appId } from "#internal/nuxt.config.mjs";
const devReducers = {
VNode: (data) => isVNode(data) ? { type: data.type, props: data.props } : void 0,
URL: (data) => data instanceof URL ? data.toString() : void 0
};
const asyncContext = getContext("nuxt-dev", { asyncContext: true, AsyncLocalStorage });
export default (nitroApp) => {
const handler = nitroApp.h3App.handler;
nitroApp.h3App.handler = (event) => {
return asyncContext.callAsync({ logs: [], event }, () => handler(event));
};
onConsoleLog((_log) => {
const ctx = asyncContext.tryUse();
if (!ctx) {
return;
}
const rawStack = captureRawStackTrace();
if (!rawStack || rawStack.includes("runtime/vite-node.mjs")) {
return;
}
const trace = [];
let filename = "";
for (const entry of parseRawStackTrace(rawStack)) {
if (entry.source === import.meta.url) {
continue;
}
if (EXCLUDE_TRACE_RE.test(entry.source)) {
continue;
}
filename ||= entry.source.replace(withTrailingSlash(rootDir), "");
trace.push({
...entry,
source: entry.source.startsWith("file://") ? entry.source.replace("file://", "") : entry.source
});
}
const log = {
..._log,
// Pass along filename to allow the client to display more info about where log comes from
filename,
// Clean up file names in stack trace
stack: trace
};
ctx.logs.push(log);
});
nitroApp.hooks.hook("afterResponse", () => {
const ctx = asyncContext.tryUse();
if (!ctx) {
return;
}
return nitroApp.hooks.callHook("dev:ssr-logs", { logs: ctx.logs, path: ctx.event.path });
});
nitroApp.hooks.hook("render:html", (htmlContext) => {
const ctx = asyncContext.tryUse();
if (!ctx) {
return;
}
try {
const reducers = Object.assign(/* @__PURE__ */ Object.create(null), devReducers, ctx.event.context._payloadReducers);
htmlContext.bodyAppend.unshift(`<script type="application/json" data-nuxt-logs="${appId}">${stringify(ctx.logs, reducers)}<\/script>`);
} catch (e) {
const shortError = e instanceof Error && "toString" in e ? ` Received \`${e.toString()}\`.` : "";
console.warn(`[nuxt] Failed to stringify dev server logs.${shortError} You can define your own reducer/reviver for rich types following the instructions in https://nuxt.com/docs/api/composables/use-nuxt-app#payload.`);
}
});
};
const EXCLUDE_TRACE_RE = /\/node_modules\/(?:.*\/)?(?:nuxt|nuxt-nightly|nuxt-edge|nuxt3|consola|@vue)\/|core\/runtime\/nitro/;
function onConsoleLog(callback) {
consola.addReporter({
log(logObj) {
callback(logObj);
}
});
consola.wrapConsole();
}

View file

@ -0,0 +1,2 @@
export type DefaultMessages = Record<"appName" | "version" | "statusCode" | "statusMessage" | "description", string | boolean | number>;
export declare const template: (messages: Partial<DefaultMessages>) => string;

View file

@ -0,0 +1,6 @@
import { escapeHtml } from "@vue/shared";
const _messages = { "appName": "Nuxt", "version": "", "statusCode": 500, "statusMessage": "Server error", "description": "This page is temporarily unavailable." };
export const template = (messages) => {
messages = { ..._messages, ...messages };
return '<!DOCTYPE html><html lang="en"><head><title>' + escapeHtml(messages.statusCode) + " - " + escapeHtml(messages.statusMessage) + " | " + escapeHtml(messages.appName) + `</title><meta charset="utf-8"><meta content="width=device-width,initial-scale=1.0,minimum-scale=1.0" name="viewport"><style>.spotlight{background:linear-gradient(45deg,#00dc82,#36e4da 50%,#0047e1);filter:blur(20vh)}*,:after,:before{border-color:var(--un-default-border-color,#e5e7eb);border-style:solid;border-width:0;box-sizing:border-box}:after,:before{--un-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}h1{font-size:inherit;font-weight:inherit}h1,p{margin:0}*,:after,:before{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 transparent;--un-ring-shadow:0 0 transparent;--un-shadow-inset: ;--un-shadow:0 0 transparent;--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgba(147,197,253,.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }.fixed{position:fixed}.-bottom-1\\/2{bottom:-50%}.left-0{left:0}.right-0{right:0}.grid{display:grid}.mb-16{margin-bottom:4rem}.mb-8{margin-bottom:2rem}.h-1\\/2{height:50%}.max-w-520px{max-width:520px}.min-h-screen{min-height:100vh}.place-content-center{place-content:center}.overflow-hidden{overflow:hidden}.bg-white{--un-bg-opacity:1;background-color:rgb(255 255 255/var(--un-bg-opacity))}.px-8{padding-left:2rem;padding-right:2rem}.text-center{text-align:center}.text-8xl{font-size:6rem;line-height:1}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-black{--un-text-opacity:1;color:rgb(0 0 0/var(--un-text-opacity))}.font-light{font-weight:300}.font-medium{font-weight:500}.leading-tight{line-height:1.25}.font-sans{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media(prefers-color-scheme:dark){.dark\\:bg-black{--un-bg-opacity:1;background-color:rgb(0 0 0/var(--un-bg-opacity))}.dark\\:text-white{--un-text-opacity:1;color:rgb(255 255 255/var(--un-text-opacity))}}@media(min-width:640px){.sm\\:px-0{padding-left:0;padding-right:0}.sm\\:text-4xl{font-size:2.25rem;line-height:2.5rem}}</style><script>!function(){const e=document.createElement("link").relList;if(!(e&&e.supports&&e.supports("modulepreload"))){for(const e of document.querySelectorAll('link[rel="modulepreload"]'))r(e);new MutationObserver((e=>{for(const o of e)if("childList"===o.type)for(const e of o.addedNodes)"LINK"===e.tagName&&"modulepreload"===e.rel&&r(e)})).observe(document,{childList:!0,subtree:!0})}function r(e){if(e.ep)return;e.ep=!0;const r=function(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),"use-credentials"===e.crossOrigin?r.credentials="include":"anonymous"===e.crossOrigin?r.credentials="omit":r.credentials="same-origin",r}(e);fetch(e.href,r)}}();<\/script></head><body class="antialiased bg-white dark:bg-black dark:text-white font-sans grid min-h-screen overflow-hidden place-content-center text-black"><div class="-bottom-1/2 fixed h-1/2 left-0 right-0 spotlight"></div><div class="max-w-520px text-center"><h1 class="font-medium mb-8 sm:text-10xl text-8xl">` + escapeHtml(messages.statusCode) + '</h1><p class="font-light leading-tight mb-16 px-8 sm:px-0 sm:text-4xl text-xl">' + escapeHtml(messages.description) + "</p></div></body></html>";
};

View file

@ -0,0 +1,6 @@
declare const _default: (opts: {
base?: string;
}) => import("unstorage").Driver<{
base?: string;
}, never>;
export default _default;

View file

@ -0,0 +1,42 @@
// @ts-check
import crypto from 'node:crypto'
import { defineDriver } from 'unstorage'
import fsDriver from 'unstorage/drivers/fs-lite'
import lruCache from 'unstorage/drivers/lru-cache'
/**
* @param {string} item
*/
function normalizeFsKey (item) {
const safe = item.replace(/[^\w.-]/g, '_')
const prefix = safe.slice(0, 20)
const hash = crypto.createHash('sha256').update(item).digest('hex')
return `${prefix}-${hash}`
}
export default defineDriver(
/**
* @param {{ base?: string }} opts
*/
(opts) => {
const fs = fsDriver({ base: opts.base })
const lru = lruCache({ max: 1000 })
return {
...fs, // fall back to file system - only the bottom three methods are used in renderer
async setItem (key, value, opts) {
await Promise.all([
fs.setItem?.(normalizeFsKey(key), value, opts),
lru.setItem?.(key, value, opts),
])
},
async hasItem (key, opts) {
return await lru.hasItem(key, opts) || await fs.hasItem(normalizeFsKey(key), opts)
},
async getItem (key, opts) {
return await lru.getItem(key, opts) || await fs.getItem(normalizeFsKey(key), opts)
},
}
},
)

View file

@ -0,0 +1,8 @@
export declare const payloadCache: any;
export declare const islandCache: any;
export declare const islandPropCache: any;
export declare const sharedPrerenderPromises: Map<string, Promise<any>> | null;
export declare const sharedPrerenderCache: {
get<T = unknown>(key: string): Promise<T> | undefined;
set<T>(key: string, value: Promise<T>): Promise<void>;
} | null;

View file

@ -0,0 +1,19 @@
import { useStorage } from "#internal/nitro";
import process from "node:process";
export const payloadCache = import.meta.prerender ? useStorage("internal:nuxt:prerender:payload") : null;
export const islandCache = import.meta.prerender ? useStorage("internal:nuxt:prerender:island") : null;
export const islandPropCache = import.meta.prerender ? useStorage("internal:nuxt:prerender:island-props") : null;
export const sharedPrerenderPromises = import.meta.prerender && process.env.NUXT_SHARED_DATA ? /* @__PURE__ */ new Map() : null;
const sharedPrerenderKeys = /* @__PURE__ */ new Set();
export const sharedPrerenderCache = import.meta.prerender && process.env.NUXT_SHARED_DATA ? {
get(key) {
if (sharedPrerenderKeys.has(key)) {
return sharedPrerenderPromises.get(key) ?? useStorage("internal:nuxt:prerender:shared").getItem(key);
}
},
async set(key, value) {
sharedPrerenderKeys.add(key);
sharedPrerenderPromises.set(key, value);
useStorage("internal:nuxt:prerender:shared").setItem(key, await value).finally(() => sharedPrerenderPromises.delete(key));
}
} : null;

View file

@ -0,0 +1 @@
export declare const defineAppConfig: (config: any) => any;

View file

@ -0,0 +1 @@
export const defineAppConfig = (config) => config;

View file

@ -0,0 +1,3 @@
export declare function generateErrorOverlayHTML(html: string, options?: {
startMinimized?: boolean;
}): string;

View file

@ -0,0 +1,334 @@
const iframeStorageBridge = (nonce) => (
/* js */
`
(function() {
const memoryStore = {};
const NONCE = ${JSON.stringify(nonce)}
const mockStorage = {
getItem: function(key) {
return memoryStore[key] !== undefined ? memoryStore[key] : null;
},
setItem: function(key, value) {
memoryStore[key] = String(value);
window.parent.postMessage({
type: 'storage-set',
key: key,
value: String(value),
nonce: NONCE
}, '*');
},
removeItem: function(key) {
delete memoryStore[key];
window.parent.postMessage({
type: 'storage-remove',
key: key,
nonce: NONCE
}, '*');
},
clear: function() {
for (const key in memoryStore) {
delete memoryStore[key];
}
window.parent.postMessage({
type: 'storage-clear',
nonce: NONCE
}, '*');
},
key: function(index) {
const keys = Object.keys(memoryStore);
return keys[index] !== undefined ? keys[index] : null;
},
get length() {
return Object.keys(memoryStore).length;
}
};
try {
Object.defineProperty(window, 'localStorage', {
value: mockStorage,
writable: false,
configurable: true
});
} catch (e) {
window.localStorage = mockStorage;
}
window.addEventListener('message', function(event) {
if (event.data.type === 'storage-sync-data' && event.data.nonce === NONCE) {
const data = event.data.data;
for (const key in data) {
if (Object.prototype.hasOwnProperty.call(data, key)) {
memoryStore[key] = data[key];
}
}
if (typeof window.initTheme === 'function') {
window.initTheme();
}
window.dispatchEvent(new Event('storage-ready'));
}
});
window.parent.postMessage({
type: 'storage-sync-request',
nonce: NONCE
}, '*');
})();
`
);
const parentStorageBridge = (nonce) => (
/* js */
`
(function() {
const host = document.querySelector('nuxt-error-overlay');
if (!host) return;
// Wait for shadow root to be attached
const checkShadow = setInterval(function() {
if (host.shadowRoot) {
clearInterval(checkShadow);
const iframe = host.shadowRoot.getElementById('frame');
if (!iframe) return;
const NONCE = ${JSON.stringify(nonce)}
window.addEventListener('message', function(event) {
if (!event.data || event.data.nonce !== NONCE) return;
const data = event.data;
if (data.type === 'storage-set') {
localStorage.setItem(data.key, data.value);
} else if (data.type === 'storage-remove') {
localStorage.removeItem(data.key);
} else if (data.type === 'storage-clear') {
localStorage.clear();
} else if (data.type === 'storage-sync-request') {
const allData = {};
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
allData[key] = localStorage.getItem(key);
}
iframe.contentWindow.postMessage({
type: 'storage-sync-data',
data: allData,
nonce: NONCE
}, '*');
}
});
}
}, 10);
})();
`
);
const errorCSS = (
/* css */
`
:host {
--preview-width: 240px;
--preview-height: 180px;
--base-width: 1200px;
--base-height: 900px;
--z-base: 999999998;
all: initial;
display: contents;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
#frame {
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
border: none;
z-index: var(--z-base);
}
#frame[inert] {
right: 5px;
bottom: 5px;
left: auto;
top: auto;
width: var(--base-width);
height: var(--base-height);
transform: scale(calc(240 / 1200));
transform-origin: bottom right;
overflow: hidden;
border-radius: calc(1200 * 8px / 240);
}
#preview {
position: fixed;
right: 5px;
bottom: 5px;
width: var(--preview-width);
height: var(--preview-height);
overflow: hidden;
border-radius: 8px;
pointer-events: none;
z-index: var(--z-base);
background: white;
display: none;
}
#frame:not([inert]) + #preview {
display: block;
}
#toggle {
position: fixed;
right: 5px;
bottom: 5px;
width: var(--preview-width);
height: var(--preview-height);
background: none;
border: 3px solid #00DC82;
border-radius: 8px;
cursor: pointer;
opacity: 0.8;
transition: opacity 0.2s, box-shadow 0.2s;
z-index: calc(var(--z-base) + 1);
}
#toggle:hover,
#toggle:focus {
opacity: 1;
box-shadow: 0 0 20px rgba(0, 220, 130, 0.6);
}
#toggle:focus-visible {
outline: 3px solid #00DC82;
outline-offset: 3px;
box-shadow: 0 0 24px rgba(0, 220, 130, 0.8);
}
@media (prefers-reduced-motion: reduce) {
#toggle {
transition: none;
}
}
`
);
function webComponentScript(base64HTML, startMinimized) {
return (
/* js */
`
(function() {
try {
const host = document.querySelector('nuxt-error-overlay');
if (!host) return;
const shadow = host.attachShadow({ mode: 'open' });
// Create elements
const style = document.createElement('style');
style.textContent = ${JSON.stringify(errorCSS)};
const iframe = document.createElement('iframe');
iframe.id = 'frame';
iframe.src = 'data:text/html;base64,${base64HTML}';
iframe.title = 'Detailed error stack trace';
iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');
const preview = document.createElement('div');
preview.id = 'preview';
const button = document.createElement('button');
button.id = 'toggle';
button.setAttribute('aria-expanded', 'true');
button.setAttribute('type', 'button');
button.innerHTML = '<span class="sr-only">Toggle detailed error view</span>';
const liveRegion = document.createElement('div');
liveRegion.setAttribute('role', 'status');
liveRegion.setAttribute('aria-live', 'polite');
liveRegion.className = 'sr-only';
// Update preview snapshot
function updatePreview() {
try {
let previewIframe = preview.querySelector('iframe');
if (!previewIframe) {
previewIframe = document.createElement('iframe');
previewIframe.style.cssText = 'width: 1200px; height: 900px; transform: scale(0.2); transform-origin: top left; border: none;';
previewIframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');
preview.appendChild(previewIframe);
}
const doctype = document.doctype ? '<!DOCTYPE ' + document.doctype.name + '>' : '';
const cleanedHTML = document.documentElement.outerHTML
.replace(/<nuxt-error-overlay[^>]*>.*?<\\/nuxt-error-overlay>/gs, '')
.replace(/<script[^>]*>.*?<\\/script>/gs, '');
const iframeDoc = previewIframe.contentDocument || previewIframe.contentWindow.document;
iframeDoc.open();
iframeDoc.write(doctype + cleanedHTML);
iframeDoc.close();
} catch (error) {
console.error('Failed to update preview:', error);
}
}
function toggleView() {
const isMinimized = iframe.hasAttribute('inert');
if (isMinimized) {
updatePreview();
iframe.removeAttribute('inert');
button.setAttribute('aria-expanded', 'true');
liveRegion.textContent = 'Showing detailed error view';
setTimeout(function() {
try { iframe.contentWindow.focus(); } catch {}
}, 100);
} else {
iframe.setAttribute('inert', '');
button.setAttribute('aria-expanded', 'false');
liveRegion.textContent = 'Showing error page';
button.focus();
}
}
button.onclick = toggleView;
document.addEventListener('keydown', function(e) {
if ((e.key === 'Escape' || e.key === 'Esc') && !iframe.hasAttribute('inert')) {
toggleView();
}
});
// Append to shadow DOM
shadow.appendChild(style);
shadow.appendChild(liveRegion);
shadow.appendChild(iframe);
shadow.appendChild(preview);
shadow.appendChild(button);
if (${startMinimized}) {
iframe.setAttribute('inert', '');
button.setAttribute('aria-expanded', 'false');
}
// Initialize preview
setTimeout(updatePreview, 100);
} catch (error) {
console.error('Failed to initialize Nuxt error overlay:', error);
}
})();
`
);
}
export function generateErrorOverlayHTML(html, options) {
const nonce = Array.from(crypto.getRandomValues(new Uint8Array(16)), (b) => b.toString(16).padStart(2, "0")).join("");
const errorPage = html.replace("<head>", `<head><script>${iframeStorageBridge(nonce)}<\/script>`);
const base64HTML = Buffer.from(errorPage, "utf8").toString("base64");
return `
<script>${parentStorageBridge(nonce)}<\/script>
<nuxt-error-overlay></nuxt-error-overlay>
<script>${webComponentScript(base64HTML, options?.startMinimized ?? false)}<\/script>
`;
}

View file

@ -0,0 +1,6 @@
import type { H3Event } from 'h3';
/**
* Nitro internal functions extracted from https://github.com/nitrojs/nitro/blob/v2/src/runtime/internal/utils.ts
*/
export declare function isJsonRequest(event: H3Event): boolean;
export declare function hasReqHeader(event: H3Event, name: string, includes: string): boolean | "" | undefined;

View file

@ -0,0 +1,11 @@
import { getRequestHeader } from "h3";
export function isJsonRequest(event) {
if (hasReqHeader(event, "accept", "text/html")) {
return false;
}
return hasReqHeader(event, "accept", "application/json") || hasReqHeader(event, "user-agent", "curl/") || hasReqHeader(event, "user-agent", "httpie/") || hasReqHeader(event, "sec-fetch-mode", "cors") || event.path.startsWith("/api/") || event.path.endsWith(".json");
}
export function hasReqHeader(event, name, includes) {
const value = getRequestHeader(event, name);
return value && typeof value === "string" && value.toLowerCase().includes(includes);
}

View file

@ -0,0 +1,4 @@
export declare function baseURL(): string;
export declare function buildAssetsDir(): string;
export declare function buildAssetsURL(...path: string[]): string;
export declare function publicAssetsURL(...path: string[]): string;

View file

@ -0,0 +1,16 @@
import { joinRelativeURL } from "ufo";
import { useRuntimeConfig } from "#internal/nitro";
export function baseURL() {
return useRuntimeConfig().app.baseURL;
}
export function buildAssetsDir() {
return useRuntimeConfig().app.buildAssetsDir;
}
export function buildAssetsURL(...path) {
return joinRelativeURL(publicAssetsURL(), buildAssetsDir(), ...path);
}
export function publicAssetsURL(...path) {
const app = useRuntimeConfig().app;
const publicBase = app.cdnURL || app.baseURL;
return path.length ? joinRelativeURL(publicBase, ...path) : publicBase;
}

View file

@ -0,0 +1,6 @@
import type { H3Event } from 'h3';
import type { NuxtPayload, NuxtSSRContext } from 'nuxt/app';
export declare function createSSRContext(event: H3Event): NuxtSSRContext;
export declare function setSSRError(ssrContext: NuxtSSRContext, error: NuxtPayload['error'] & {
url: string;
}): void;

View file

@ -0,0 +1,33 @@
import process from "node:process";
import { createHead } from "@unhead/vue/server";
import { sharedPrerenderCache } from "../cache.js";
import { useRuntimeConfig } from "#internal/nitro";
import unheadOptions from "#internal/unhead-options.mjs";
const PRERENDER_NO_SSR_ROUTES = /* @__PURE__ */ new Set(["/index.html", "/200.html", "/404.html"]);
export function createSSRContext(event) {
const ssrContext = {
url: event.path,
event,
runtimeConfig: useRuntimeConfig(event),
noSSR: !!process.env.NUXT_NO_SSR || event.context.nuxt?.noSSR || (import.meta.prerender ? PRERENDER_NO_SSR_ROUTES.has(event.path) : false),
head: createHead(unheadOptions),
error: false,
nuxt: void 0,
/* NuxtApp */
payload: {},
_payloadReducers: /* @__PURE__ */ Object.create(null),
modules: /* @__PURE__ */ new Set()
};
if (import.meta.prerender) {
if (process.env.NUXT_SHARED_DATA) {
ssrContext._sharedPrerenderCache = sharedPrerenderCache;
}
ssrContext.payload.prerenderedAt = Date.now();
}
return ssrContext;
}
export function setSSRError(ssrContext, error) {
ssrContext.error = true;
ssrContext.payload = { error };
ssrContext.url = error.url;
}

View file

@ -0,0 +1,22 @@
import type { NuxtSSRContext } from 'nuxt/app';
export declare const getSSRRenderer: () => Promise<{
rendererContext: import("vue-bundle-renderer/runtime").RendererContext;
renderToString(ssrContext: import("vue-bundle-renderer/runtime").SSRContext): Promise<{
html: string;
renderResourceHeaders: () => Record<string, string>;
renderResourceHints: () => string;
renderStyles: () => string;
renderScripts: () => string;
}>;
}>;
export declare function getRenderer(ssrContext: NuxtSSRContext): Promise<{
rendererContext: import("vue-bundle-renderer/runtime").RendererContext;
renderToString(ssrContext: import("vue-bundle-renderer/runtime").SSRContext): Promise<{
html: string;
renderResourceHeaders: () => Record<string, string>;
renderResourceHints: () => string;
renderStyles: () => string;
renderScripts: () => string;
}>;
}>;
export declare const getSSRStyles: () => Promise<Record<string, () => Promise<string[]>>>;

View file

@ -0,0 +1,85 @@
import process from "node:process";
import { createRenderer } from "vue-bundle-renderer/runtime";
import { renderToString as _renderToString } from "vue/server-renderer";
import { propsToString } from "@unhead/vue/server";
import { useRuntimeConfig } from "#internal/nitro";
import { appRootAttrs, appRootTag, appSpaLoaderAttrs, appSpaLoaderTag, spaLoadingTemplateOutside } from "#internal/nuxt.config.mjs";
import { buildAssetsURL } from "#internal/nuxt/paths";
const APP_ROOT_OPEN_TAG = `<${appRootTag}${propsToString(appRootAttrs)}>`;
const APP_ROOT_CLOSE_TAG = `</${appRootTag}>`;
const getServerEntry = () => import("#build/dist/server/server.mjs").then((r) => r.default || r);
const getClientManifest = () => import("#build/dist/server/client.manifest.mjs").then((r) => r.default || r).then((r) => typeof r === "function" ? r() : r);
const getPrecomputedDependencies = () => import("#build/dist/server/client.precomputed.mjs").then((r) => r.default || r).then((r) => typeof r === "function" ? r() : r);
export const getSSRRenderer = lazyCachedFunction(async () => {
const createSSRApp = await getServerEntry();
if (!createSSRApp) {
throw new Error("Server bundle is not available");
}
const precomputed = import.meta.dev ? void 0 : await getPrecomputedDependencies();
const renderer = createRenderer(createSSRApp, {
precomputed,
manifest: import.meta.dev ? await getClientManifest() : void 0,
renderToString,
buildAssetsURL
});
async function renderToString(input, context) {
const html = await _renderToString(input, context);
if (import.meta.dev && process.env.NUXT_VITE_NODE_OPTIONS) {
renderer.rendererContext.updateManifest(await getClientManifest());
}
return APP_ROOT_OPEN_TAG + html + APP_ROOT_CLOSE_TAG;
}
return renderer;
});
const getSPARenderer = lazyCachedFunction(async () => {
const precomputed = import.meta.dev ? void 0 : await getPrecomputedDependencies();
const spaTemplate = await import("#spa-template").then((r) => r.template).catch(() => "").then((r) => {
if (spaLoadingTemplateOutside) {
const APP_SPA_LOADER_OPEN_TAG = `<${appSpaLoaderTag}${propsToString(appSpaLoaderAttrs)}>`;
const APP_SPA_LOADER_CLOSE_TAG = `</${appSpaLoaderTag}>`;
const appTemplate = APP_ROOT_OPEN_TAG + APP_ROOT_CLOSE_TAG;
const loaderTemplate = r ? APP_SPA_LOADER_OPEN_TAG + r + APP_SPA_LOADER_CLOSE_TAG : "";
return appTemplate + loaderTemplate;
} else {
return APP_ROOT_OPEN_TAG + r + APP_ROOT_CLOSE_TAG;
}
});
const renderer = createRenderer(() => () => {
}, {
precomputed,
manifest: import.meta.dev ? await getClientManifest() : void 0,
renderToString: () => spaTemplate,
buildAssetsURL
});
const result = await renderer.renderToString({});
const renderToString = (ssrContext) => {
const config = useRuntimeConfig(ssrContext.event);
ssrContext.modules ||= /* @__PURE__ */ new Set();
ssrContext.payload.serverRendered = false;
ssrContext.config = {
public: config.public,
app: config.app
};
return Promise.resolve(result);
};
return {
rendererContext: renderer.rendererContext,
renderToString
};
});
function lazyCachedFunction(fn) {
let res = null;
return () => {
if (res === null) {
res = fn().catch((err) => {
res = null;
throw err;
});
}
return res;
};
}
export function getRenderer(ssrContext) {
return process.env.NUXT_NO_SSR || ssrContext.noSSR ? getSPARenderer() : getSSRRenderer();
}
export const getSSRStyles = lazyCachedFunction(() => import("#build/dist/server/styles.mjs").then((r) => r.default || r));

View file

@ -0,0 +1,2 @@
import type { Style } from '@unhead/vue/types';
export declare function renderInlineStyles(usedModules: Set<string> | string[]): Promise<Style[]>;

View file

@ -0,0 +1,13 @@
import { getSSRStyles } from "./build-files.js";
export async function renderInlineStyles(usedModules) {
const styleMap = await getSSRStyles();
const inlinedStyles = /* @__PURE__ */ new Set();
for (const mod of usedModules) {
if (mod in styleMap && styleMap[mod]) {
for (const style of await styleMap[mod]()) {
inlinedStyles.add(style);
}
}
}
return Array.from(inlinedStyles).map((style) => ({ innerHTML: style }));
}

View file

@ -0,0 +1,9 @@
import type { NuxtIslandResponse, NuxtSSRContext } from 'nuxt/app';
/**
* remove the root node from the html body
*/
export declare function getServerComponentHTML(body: string): string;
export declare function getSlotIslandResponse(ssrContext: NuxtSSRContext): NuxtIslandResponse['slots'];
export declare function getClientIslandResponse(ssrContext: NuxtSSRContext): NuxtIslandResponse['components'];
export declare function getComponentSlotTeleport(clientUid: string, teleports: Record<string, string>): Record<string, string>;
export declare function replaceIslandTeleports(ssrContext: NuxtSSRContext, html: string): string;

View file

@ -0,0 +1,82 @@
import { appRootTag } from "#internal/nuxt.config.mjs";
const ROOT_NODE_REGEX = new RegExp(`^<${appRootTag}[^>]*>([\\s\\S]*)<\\/${appRootTag}>$`);
export function getServerComponentHTML(body) {
const match = body.match(ROOT_NODE_REGEX);
return match?.[1] || body;
}
const SSR_SLOT_TELEPORT_MARKER = /^uid=([^;]*);slot=(.*)$/;
const SSR_CLIENT_TELEPORT_MARKER = /^uid=([^;]*);client=(.*)$/;
const SSR_CLIENT_SLOT_MARKER = /^island-slot=([^;]*);(.*)$/;
export function getSlotIslandResponse(ssrContext) {
if (!ssrContext.islandContext || !Object.keys(ssrContext.islandContext.slots).length) {
return void 0;
}
const response = {};
for (const [name, slot] of Object.entries(ssrContext.islandContext.slots)) {
response[name] = {
...slot,
fallback: ssrContext.teleports?.[`island-fallback=${name}`]
};
}
return response;
}
export function getClientIslandResponse(ssrContext) {
if (!ssrContext.islandContext || !Object.keys(ssrContext.islandContext.components).length) {
return void 0;
}
const response = {};
for (const [clientUid, component] of Object.entries(ssrContext.islandContext.components)) {
const html = ssrContext.teleports?.[clientUid]?.replaceAll("<!--teleport start anchor-->", "") || "";
response[clientUid] = {
...component,
html,
slots: getComponentSlotTeleport(clientUid, ssrContext.teleports ?? {})
};
}
return response;
}
export function getComponentSlotTeleport(clientUid, teleports) {
const entries = Object.entries(teleports);
const slots = {};
for (const [key, value] of entries) {
const match = key.match(SSR_CLIENT_SLOT_MARKER);
if (match) {
const [, id, slot] = match;
if (!slot || clientUid !== id) {
continue;
}
slots[slot] = value;
}
}
return slots;
}
export function replaceIslandTeleports(ssrContext, html) {
const { teleports, islandContext } = ssrContext;
if (islandContext || !teleports) {
return html;
}
for (const key in teleports) {
const matchClientComp = key.match(SSR_CLIENT_TELEPORT_MARKER);
if (matchClientComp) {
const [, uid, clientId] = matchClientComp;
if (!uid || !clientId) {
continue;
}
html = html.replace(new RegExp(` data-island-uid="${uid}" data-island-component="${clientId}"[^>]*>`), (full) => {
return full + teleports[key];
});
continue;
}
const matchSlot = key.match(SSR_SLOT_TELEPORT_MARKER);
if (matchSlot) {
const [, uid, slot] = matchSlot;
if (!uid || !slot) {
continue;
}
html = html.replace(new RegExp(` data-island-uid="${uid}" data-island-slot="${slot}"[^>]*>`), (full) => {
return full + teleports[key];
});
}
}
return html;
}

View file

@ -0,0 +1,37 @@
import type { Script } from '@unhead/vue';
import type { NuxtSSRContext } from 'nuxt/app';
export declare function renderPayloadResponse(ssrContext: NuxtSSRContext): {
body: string;
statusCode: number;
statusMessage: string;
headers: {
'content-type': string;
'x-powered-by': string;
};
};
export declare function renderPayloadJsonScript(opts: {
ssrContext: NuxtSSRContext;
data?: any;
src?: string;
}): Script[];
export declare function renderPayloadScript(opts: {
ssrContext: NuxtSSRContext;
data?: any;
src?: string;
}): Script[];
export declare function splitPayload(ssrContext: NuxtSSRContext): {
initial: {
prerenderedAt: number | undefined;
path?: string | undefined;
serverRendered?: boolean | undefined;
state?: Record<string, any> | undefined;
once?: Set<string> | undefined;
config?: Pick<import("nuxt/schema").RuntimeConfig, "public" | "app"> | undefined;
error?: (import("nuxt/app").NuxtError | import("nuxt/app/defaults").DefaultErrorValue) | undefined;
_errors?: Record<string, import("nuxt/app").NuxtError<unknown> | null> | undefined;
};
payload: {
data: Record<string, any> | undefined;
prerenderedAt: number | undefined;
};
};

View file

@ -0,0 +1,67 @@
import process from "node:process";
import { getResponseStatus, getResponseStatusText } from "h3";
import devalue from "@nuxt/devalue";
import { stringify, uneval } from "devalue";
import { appId, multiApp } from "#internal/nuxt.config.mjs";
export function renderPayloadResponse(ssrContext) {
return {
body: process.env.NUXT_JSON_PAYLOADS ? stringify(splitPayload(ssrContext).payload, ssrContext._payloadReducers) : `export default ${devalue(splitPayload(ssrContext).payload)}`,
statusCode: getResponseStatus(ssrContext.event),
statusMessage: getResponseStatusText(ssrContext.event),
headers: {
"content-type": process.env.NUXT_JSON_PAYLOADS ? "application/json;charset=utf-8" : "text/javascript;charset=utf-8",
"x-powered-by": "Nuxt"
}
};
}
export function renderPayloadJsonScript(opts) {
const contents = opts.data ? stringify(opts.data, opts.ssrContext._payloadReducers) : "";
const payload = {
"type": "application/json",
"innerHTML": contents,
"data-nuxt-data": appId,
"data-ssr": !(process.env.NUXT_NO_SSR || opts.ssrContext.noSSR)
};
if (!multiApp) {
payload.id = "__NUXT_DATA__";
}
if (opts.src) {
payload["data-src"] = opts.src;
}
const config = uneval(opts.ssrContext.config);
return [
payload,
{
innerHTML: multiApp ? `window.__NUXT__=window.__NUXT__||{};window.__NUXT__[${JSON.stringify(appId)}]={config:${config}}` : `window.__NUXT__={};window.__NUXT__.config=${config}`
}
];
}
export function renderPayloadScript(opts) {
opts.data.config = opts.ssrContext.config;
const _PAYLOAD_EXTRACTION = import.meta.prerender && process.env.NUXT_PAYLOAD_EXTRACTION && !opts.ssrContext.noSSR;
const nuxtData = devalue(opts.data);
if (_PAYLOAD_EXTRACTION) {
const singleAppPayload2 = `import p from "${opts.src}";window.__NUXT__={...p,...(${nuxtData})}`;
const multiAppPayload2 = `import p from "${opts.src}";window.__NUXT__=window.__NUXT__||{};window.__NUXT__[${JSON.stringify(appId)}]={...p,...(${nuxtData})}`;
return [
{
type: "module",
innerHTML: multiApp ? multiAppPayload2 : singleAppPayload2
}
];
}
const singleAppPayload = `window.__NUXT__=${nuxtData}`;
const multiAppPayload = `window.__NUXT__=window.__NUXT__||{};window.__NUXT__[${JSON.stringify(appId)}]=${nuxtData}`;
return [
{
innerHTML: multiApp ? multiAppPayload : singleAppPayload
}
];
}
export function splitPayload(ssrContext) {
const { data, prerenderedAt, ...initial } = ssrContext.payload;
return {
initial: { ...initial, prerenderedAt },
payload: { data, prerenderedAt }
};
}

View file

@ -0,0 +1,63 @@
{
"name": "@nuxt/nitro-server",
"version": "3.20.2",
"repository": {
"type": "git",
"url": "git+https://github.com/nuxt/nuxt.git",
"directory": "packages/nitro-server"
},
"description": "Nitro server integration for Nuxt",
"homepage": "https://nuxt.com",
"license": "MIT",
"type": "module",
"types": "./dist/index.d.ts",
"exports": {
".": "./dist/index.mjs"
},
"files": [
"dist"
],
"dependencies": {
"@nuxt/devalue": "^2.0.2",
"@unhead/vue": "^2.0.19",
"@vue/shared": "^3.5.25",
"consola": "^3.4.2",
"defu": "^6.1.4",
"destr": "^2.0.5",
"devalue": "^5.6.0",
"errx": "^0.1.0",
"escape-string-regexp": "^5.0.0",
"exsolve": "^1.0.8",
"h3": "^1.15.4",
"impound": "^1.0.0",
"klona": "^2.0.6",
"mocked-exports": "^0.1.1",
"nitropack": "^2.12.9",
"pathe": "^2.0.3",
"pkg-types": "^2.3.0",
"radix3": "^1.1.2",
"std-env": "^3.10.0",
"ufo": "^1.6.1",
"unctx": "^2.4.1",
"unstorage": "^1.17.3",
"vue": "^3.5.25",
"vue-bundle-renderer": "^2.2.0",
"vue-devtools-stub": "^0.1.0",
"@nuxt/kit": "3.20.2"
},
"peerDependencies": {
"nuxt": "^3.20.2"
},
"devDependencies": {
"unbuild": "3.6.1",
"vitest": "3.2.4",
"@nuxt/schema": "3.20.2",
"nuxt": "3.20.2"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"scripts": {
"test:attw": "attw --pack"
}
}