2025-09-05 14:59:21 +08:00

7370 lines
281 KiB
JavaScript

import fs, { promises, existsSync, readdirSync, statSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
import { mkdir, readFile, readdir, writeFile, rm, stat, unlink, open } from 'node:fs/promises';
import { randomUUID } from 'node:crypto';
import { AsyncLocalStorage } from 'node:async_hooks';
import { dirname, resolve, normalize, basename, extname, relative, isAbsolute, join } from 'pathe';
import { createHooks, createDebugger } from 'hookable';
import ignore from 'ignore';
import { useLogger, tryUseNuxt, useNuxt, directoryToURL, getLayerDirectories, resolveFiles, resolvePath, defineNuxtModule, findPath, addPlugin, addTemplate, addTypeTemplate, addComponent, useNitro, addBuildPlugin, isIgnored, resolveAlias as resolveAlias$1, addPluginTemplate, addImportsSources, addVitePlugin, createIsIgnored, updateTemplates, tryResolveModule, normalizeModuleTranspilePath, resolveNuxtModule, resolveIgnorePatterns, logger as logger$1, createResolver, importModule, tryImportModule, runWithNuxtContext, nuxtCtx, loadNuxtConfig, addWebpackPlugin, addServerPlugin, installModules, addServerTemplate, addServerHandler, addRouteMiddleware, normalizeTemplate, compileTemplate, normalizePlugin, templateUtils } from '@nuxt/kit';
import { resolvePackageJSON, readPackageJSON } from 'pkg-types';
import { hash, isEqual, serialize } from 'ohash';
import consola, { consola as consola$1 } from 'consola';
import onChange from 'on-change';
import { colors } from 'consola/utils';
import { resolveCompatibilityDatesFromEnv, formatDate } from 'compatx';
import escapeRE from 'escape-string-regexp';
import { withTrailingSlash as withTrailingSlash$1, parseURL, parseQuery, joinURL, withLeadingSlash, encodePath, withoutLeadingSlash } from 'ufo';
import { ImpoundPlugin } from 'impound';
import defu$1, { defu } from 'defu';
import { satisfies, coerce, gt } from 'semver';
import { isCI, provider, isWindows, hasTTY } from 'std-env';
import { genArrayFromRaw, genSafeVariableName, genImport, genDynamicImport, genObjectFromRawEntries, genString, genExport } from 'knitwork';
import { addDependency } from 'nypm';
import { reverseResolveAlias, filename, resolveAlias } from 'pathe/utils';
import { createRoutesContext } from 'unplugin-vue-router';
import { resolveOptions } from 'unplugin-vue-router/options';
import { toRouteMatcher, createRouter, exportMatcher } from 'radix3';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { resolveModulePath } from 'exsolve';
import { runInNewContext } from 'node:vm';
import { klona } from 'klona';
import { parseAndWalk, ScopeTracker, walk, isBindingIdentifier, getUndeclaredIdentifiersInFunction } from 'oxc-walker';
import { parseSync } from 'oxc-parser';
import { transform } from 'oxc-transform';
import { splitByCase, kebabCase, pascalCase, camelCase } from 'scule';
import { createUnplugin } from 'unplugin';
import { findStaticImports, findExports, parseStaticImport, parseNodeModulePath, lookupNodeModuleSubpath } from 'mlly';
import MagicString from 'magic-string';
import { stripLiteral } from 'strip-literal';
import { unheadVueComposablesImports } from '@unhead/vue';
import { defineUnimportPreset, createUnimport, toExports, scanDirExports } from 'unimport';
import { glob } from 'tinyglobby';
import { parse, walk as walk$1, ELEMENT_NODE } from 'ultrahtml';
import { parseQuery as parseQuery$1 } from 'vue-router';
import { createTransformer } from 'unctx/transform';
import { cpus } from 'node:os';
import { createNitro, scanHandlers, writeTypes, copyPublicAssets, prepare, build as build$1, prerender, createDevServer } from 'nitropack';
import { dynamicEventHandler, defineEventHandler } from 'h3';
import { watch as watch$1 } from 'chokidar';
import { debounce } from 'perfect-debounce';
import { resolveSchema, generateTypes } from 'untyped';
import untypedPlugin from 'untyped/babel-plugin';
import { createJiti } from 'jiti';
import { minify } from 'oxc-minify';
import { resolve as resolve$1 } from 'node:path';
import { parseTar, createTar } from 'nanotar';
function toArray(value) {
return Array.isArray(value) ? value : [value];
}
async function isDirectory$1(path) {
return (await promises.lstat(path)).isDirectory();
}
const logger = useLogger("nuxt");
function resolveToAlias(path, nuxt = tryUseNuxt()) {
return reverseResolveAlias(path, { ...nuxt?.options.alias || {}, ...strippedAtAliases }).pop() || path;
}
const strippedAtAliases = {
"@": "",
"@@": ""
};
const isStackblitz = provider === "stackblitz";
async function promptToInstall(name, installCommand, options) {
for (const parent of options.searchPaths || []) {
if (await resolvePackageJSON(name, { parent }).catch(() => null)) {
return true;
}
}
logger.info(`Package ${name} is missing`);
if (isCI) {
return false;
}
if (options.prompt === true || options.prompt !== false && !isStackblitz) {
const confirm = await logger.prompt(`Do you want to install ${name} package?`, {
type: "confirm",
name: "confirm",
initial: true
});
if (!confirm) {
return false;
}
}
logger.info(`Installing ${name}...`);
try {
await installCommand();
logger.success(`Installed ${name}`);
return true;
} catch (err) {
logger.error(err);
return false;
}
}
const installPrompts = /* @__PURE__ */ new Set();
function installNuxtModule(name, options) {
if (installPrompts.has(name)) {
return;
}
installPrompts.add(name);
const nuxt = useNuxt();
return promptToInstall(name, async () => {
const { runCommand } = await import('@nuxt/cli');
await runCommand("module", ["add", name, "--cwd", nuxt.options.rootDir]);
}, { rootDir: nuxt.options.rootDir, searchPaths: nuxt.options.modulesDir, ...options });
}
function ensurePackageInstalled(name, options) {
return promptToInstall(name, () => addDependency(name, {
cwd: options.rootDir,
dev: true
}), options);
}
const features = {
__proto__: null,
ensurePackageInstalled: ensurePackageInstalled,
installNuxtModule: installNuxtModule
};
let _distDir = dirname(fileURLToPath(import.meta.url));
if (/(?:chunks|shared)$/.test(_distDir)) {
_distDir = dirname(_distDir);
}
const distDir = _distDir;
const pkgDir = resolve(distDir, "..");
async function resolveTypePath(path, subpath, searchPaths = tryUseNuxt()?.options.modulesDir) {
try {
const r = resolveModulePath(path, {
from: searchPaths?.map((d) => directoryToURL(d)),
conditions: ["types", "import", "require"],
extensions: [".js", ".mjs", ".cjs", ".ts", ".mts", ".cts"]
});
if (subpath) {
return r.replace(/(?:\.d)?\.[mc]?[jt]s$/, "");
}
const rootPath = await resolvePackageJSON(r);
return dirname(rootPath);
} catch {
return null;
}
}
function getNameFromPath(path, relativeTo) {
const relativePath = relativeTo ? normalize(path).replace(withTrailingSlash$1(normalize(relativeTo)), "") : basename(path);
const prefixParts = splitByCase(dirname(relativePath));
const fileName = basename(relativePath, extname(relativePath));
const segments = resolveComponentNameSegments(fileName.toLowerCase() === "index" ? "" : fileName, prefixParts).filter(Boolean);
return kebabCase(segments).replace(QUOTE_RE, "");
}
function hasSuffix(path, suffix) {
return basename(path, extname(path)).endsWith(suffix);
}
function resolveComponentNameSegments(fileName, prefixParts) {
const fileNameParts = splitByCase(fileName);
const fileNamePartsContent = fileNameParts.join("/").toLowerCase();
const componentNameParts = prefixParts.flatMap((p) => splitByCase(p));
let index = prefixParts.length - 1;
const matchedSuffix = [];
while (index >= 0) {
const prefixPart = prefixParts[index];
matchedSuffix.unshift(...splitByCase(prefixPart).map((p) => p.toLowerCase()));
const matchedSuffixContent = matchedSuffix.join("/");
if (fileNamePartsContent === matchedSuffixContent || fileNamePartsContent.startsWith(matchedSuffixContent + "/") || // e.g. Item/Item/Item.vue -> Item
prefixPart.toLowerCase() === fileNamePartsContent && prefixParts[index + 1] && prefixParts[index] === prefixParts[index + 1]) {
componentNameParts.length = index;
}
index--;
}
return [...componentNameParts, ...fileNameParts];
}
function isVue(id, opts = {}) {
const { search } = parseURL(decodeURIComponent(pathToFileURL(id).href));
if (id.endsWith(".vue") && !search) {
return true;
}
if (!search) {
return false;
}
const query = parseQuery(search);
if (query.nuxt_component) {
return false;
}
if (query.macro && (search === "?macro=true" || !opts.type || opts.type.includes("script"))) {
return true;
}
const type = "setup" in query ? "script" : query.type;
if (!("vue" in query) || opts.type && !opts.type.includes(type)) {
return false;
}
return true;
}
const JS_RE$1 = /\.(?:[cm]?j|t)sx?$/;
function isJS(id) {
const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href));
return JS_RE$1.test(pathname);
}
function getLoader(id) {
const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href));
const ext = extname(pathname);
if (ext === ".vue") {
return "vue";
}
if (!JS_RE$1.test(ext)) {
return null;
}
return ext.endsWith("x") ? "tsx" : "ts";
}
function matchWithStringOrRegex(value, matcher) {
if (typeof matcher === "string") {
return value === matcher;
} else if (matcher instanceof RegExp) {
return matcher.test(value);
}
return false;
}
function uniqueBy(arr, key) {
if (arr.length < 2) {
return arr;
}
const res = [];
const seen = /* @__PURE__ */ new Set();
for (const item of arr) {
if (seen.has(item[key])) {
continue;
}
seen.add(item[key]);
res.push(item);
}
return res;
}
const QUOTE_RE = /["']/g;
const EXTENSION_RE = /\b\.\w+$/g;
const SX_RE = /\.[tj]sx$/;
const SegmentTokenType = {
static: "static",
dynamic: "dynamic",
optional: "optional",
catchall: "catchall",
group: "group"
};
const SegmentParserState = {
initial: "initial",
...SegmentTokenType
};
const enUSComparator = new Intl.Collator("en-US");
async function resolvePagesRoutes(pattern, nuxt = useNuxt()) {
const pagesDirs = getLayerDirectories(nuxt).map((d) => d.appPages);
const scannedFiles = [];
for (const dir of pagesDirs) {
const files = await resolveFiles(dir, pattern);
scannedFiles.push(...files.map((file) => ({ relativePath: relative(dir, file), absolutePath: file })));
}
scannedFiles.sort((a, b) => enUSComparator.compare(a.relativePath, b.relativePath));
const allRoutes = generateRoutesFromFiles(uniqueBy(scannedFiles, "relativePath"), {
shouldUseServerComponents: !!nuxt.options.experimental.componentIslands
});
const pages = uniqueBy(allRoutes, "path");
const shouldAugment = nuxt.options.experimental.scanPageMeta || nuxt.options.experimental.typedPages;
if (shouldAugment === false) {
await nuxt.callHook("pages:extend", pages);
return pages;
}
const extraPageMetaExtractionKeys = nuxt.options?.experimental?.extraPageMetaExtractionKeys || [];
const augmentCtx = {
extraExtractionKeys: /* @__PURE__ */ new Set([
"middleware",
...extraPageMetaExtractionKeys
]),
fullyResolvedPaths: new Set(scannedFiles.map((file) => file.absolutePath))
};
if (shouldAugment === "after-resolve") {
await nuxt.callHook("pages:extend", pages);
await augmentPages(pages, nuxt.vfs, augmentCtx);
} else {
const augmentedPages = await augmentPages(pages, nuxt.vfs, augmentCtx);
await nuxt.callHook("pages:extend", pages);
await augmentPages(pages, nuxt.vfs, { pagesToSkip: augmentedPages, ...augmentCtx });
augmentedPages?.clear();
}
await nuxt.callHook("pages:resolved", pages);
return pages;
}
const INDEX_PAGE_RE = /\/index$/;
function generateRoutesFromFiles(files, options = {}) {
if (!files.length) {
return [];
}
const routes = [];
const sortedFiles = [...files].sort((a, b) => a.relativePath.length - b.relativePath.length);
for (const file of sortedFiles) {
const segments = file.relativePath.replace(new RegExp(`${escapeRE(extname(file.relativePath))}$`), "").split("/");
const route = {
name: "",
path: "",
file: file.absolutePath,
children: []
};
let parent = routes;
const lastSegment = segments[segments.length - 1];
if (lastSegment.endsWith(".server")) {
segments[segments.length - 1] = lastSegment.replace(".server", "");
if (options.shouldUseServerComponents) {
route.mode = "server";
}
} else if (lastSegment.endsWith(".client")) {
segments[segments.length - 1] = lastSegment.replace(".client", "");
route.mode = "client";
}
for (let i = 0; i < segments.length; i++) {
const segment = segments[i];
const tokens = parseSegment(segment, file.absolutePath);
if (tokens.every((token) => token.type === SegmentTokenType.group)) {
continue;
}
const segmentName = tokens.map(({ value, type }) => type === SegmentTokenType.group ? "" : value).join("");
route.name += (route.name && "/") + segmentName;
const routePath = getRoutePath(tokens, segments[i + 1] !== void 0 && segments[i + 1] !== "index");
const path = withLeadingSlash(joinURL(route.path, routePath.replace(INDEX_PAGE_RE, "/")));
const child = parent.find((parentRoute) => parentRoute.name === route.name && parentRoute.path === path.replace("([^/]*)*", "(.*)*"));
if (child && child.children) {
parent = child.children;
route.path = "";
} else if (segmentName === "index" && !route.path) {
route.path += "/";
} else if (segmentName !== "index") {
route.path += routePath;
}
}
parent.push(route);
}
return prepareRoutes(routes);
}
async function augmentPages(routes, vfs, ctx = {}) {
ctx.augmentedPages ??= /* @__PURE__ */ new Set();
for (const route of routes) {
if (route.file && !ctx.pagesToSkip?.has(route.file)) {
const fileContent = route.file in vfs ? vfs[route.file] : fs.readFileSync(ctx.fullyResolvedPaths?.has(route.file) ? route.file : await resolvePath(route.file), "utf-8");
const routeMeta = getRouteMeta(fileContent, route.file, ctx.extraExtractionKeys);
if (route.meta) {
routeMeta.meta = defu({}, routeMeta.meta, route.meta);
}
if (route.rules) {
routeMeta.rules = defu({}, routeMeta.rules, route.rules);
}
Object.assign(route, routeMeta);
ctx.augmentedPages.add(route.file);
}
if (route.children && route.children.length > 0) {
await augmentPages(route.children, vfs, ctx);
}
}
return ctx.augmentedPages;
}
const SFC_SCRIPT_RE = /<script(?<attrs>[^>]*)>(?<content>[\s\S]*?)<\/script[^>]*>/gi;
function extractScriptContent(sfc) {
const contents = [];
for (const match of sfc.matchAll(SFC_SCRIPT_RE)) {
if (match?.groups?.content) {
contents.push({
loader: match.groups.attrs && /[tj]sx/.test(match.groups.attrs) ? "tsx" : "ts",
code: match.groups.content.trim()
});
}
}
return contents;
}
const PAGE_EXTRACT_RE = /(definePageMeta|defineRouteRules)\([\s\S]*?\)/g;
const defaultExtractionKeys = ["name", "path", "props", "alias", "redirect", "middleware"];
const DYNAMIC_META_KEY = "__nuxt_dynamic_meta_key";
const pageContentsCache = {};
const extractCache = {};
function getRouteMeta(contents, absolutePath, extraExtractionKeys = /* @__PURE__ */ new Set()) {
if (!(absolutePath in pageContentsCache) || pageContentsCache[absolutePath] !== contents) {
pageContentsCache[absolutePath] = contents;
delete extractCache[absolutePath];
}
if (absolutePath in extractCache && extractCache[absolutePath]) {
return klona(extractCache[absolutePath]);
}
const loader = getLoader(absolutePath);
const scriptBlocks = !loader ? null : loader === "vue" ? extractScriptContent(contents) : [{ code: contents, loader }];
if (!scriptBlocks) {
extractCache[absolutePath] = {};
return {};
}
const extractedData = {};
const extractionKeys = /* @__PURE__ */ new Set([...defaultExtractionKeys, ...extraExtractionKeys]);
for (const script of scriptBlocks) {
const found = {};
for (const macro of script.code.matchAll(PAGE_EXTRACT_RE)) {
found[macro[1]] = false;
}
if (Object.keys(found).length === 0) {
continue;
}
const dynamicProperties = /* @__PURE__ */ new Set();
parseAndWalk(script.code, absolutePath.replace(/\.\w+$/, "." + script.loader), (node) => {
if (node.type !== "ExpressionStatement" || node.expression.type !== "CallExpression" || node.expression.callee.type !== "Identifier") {
return;
}
const fnName = node.expression.callee.name;
if (fnName in found === false || found[fnName] !== false) {
return;
}
found[fnName] = true;
let code = script.code;
let pageExtractArgument = node.expression.arguments[0];
if (/tsx?/.test(script.loader)) {
const transformed = transform(absolutePath, script.code.slice(node.start, node.end), { lang: script.loader });
if (transformed.errors.length) {
for (const error of transformed.errors) {
logger.warn(`Error while transforming \`${fnName}()\`` + error.codeframe);
}
return;
}
pageExtractArgument = parseSync("", transformed.code, { lang: "js" }).program.body[0].expression.arguments[0];
code = transformed.code;
}
if (pageExtractArgument?.type !== "ObjectExpression") {
logger.warn(`\`${fnName}\` must be called with an object literal (reading \`${absolutePath}\`), found ${pageExtractArgument?.type} instead.`);
return;
}
if (fnName === "defineRouteRules") {
const { value, serializable } = isSerializable(code, pageExtractArgument);
if (!serializable) {
logger.warn(`\`${fnName}\` must be called with a serializable object literal (reading \`${absolutePath}\`).`);
return;
}
extractedData.rules = value;
return;
}
if (fnName === "definePageMeta") {
for (const key of extractionKeys) {
const property = pageExtractArgument.properties.find((property2) => property2.type === "Property" && property2.key.type === "Identifier" && property2.key.name === key);
if (!property) {
continue;
}
const { value, serializable } = isSerializable(code, property.value);
if (!serializable) {
logger.debug(`Skipping extraction of \`${key}\` metadata as it is not JSON-serializable (reading \`${absolutePath}\`).`);
dynamicProperties.add(extraExtractionKeys.has(key) ? "meta" : key);
continue;
}
if (extraExtractionKeys.has(key)) {
extractedData.meta ??= {};
extractedData.meta[key] = value;
} else {
extractedData[key] = value;
}
}
for (const property of pageExtractArgument.properties) {
if (property.type !== "Property") {
continue;
}
const isIdentifierOrLiteral = property.key.type === "Literal" || property.key.type === "Identifier";
if (!isIdentifierOrLiteral) {
continue;
}
const name = property.key.type === "Identifier" ? property.key.name : String(property.value);
if (!extraExtractionKeys.has(name)) {
dynamicProperties.add("meta");
break;
}
}
if (dynamicProperties.size) {
extractedData.meta ??= {};
extractedData.meta[DYNAMIC_META_KEY] = dynamicProperties;
}
}
});
}
extractCache[absolutePath] = extractedData;
return klona(extractedData);
}
const COLON_RE = /:/g;
function getRoutePath(tokens, hasSucceedingSegment = false) {
return tokens.reduce((path, token) => {
switch (token.type) {
case SegmentTokenType.optional:
return path + `:${token.value}?`;
case SegmentTokenType.dynamic:
return path + `:${token.value}()`;
case SegmentTokenType.catchall:
return path + (hasSucceedingSegment ? `:${token.value}([^/]*)*` : `:${token.value}(.*)*`);
case SegmentTokenType.group:
return path;
case SegmentTokenType.static:
default:
return path + encodePath(token.value).replace(COLON_RE, "\\:");
}
}, "/");
}
const PARAM_CHAR_RE = /[\w.]/;
function parseSegment(segment, absolutePath) {
let state = SegmentParserState.initial;
let i = 0;
let buffer = "";
const tokens = [];
function consumeBuffer() {
if (!buffer) {
return;
}
if (state === SegmentParserState.initial) {
throw new Error("wrong state");
}
tokens.push({ type: state, value: buffer });
buffer = "";
}
while (i < segment.length) {
const c = segment[i];
switch (state) {
case SegmentParserState.initial:
buffer = "";
if (c === "[") {
state = SegmentParserState.dynamic;
} else if (c === "(") {
state = SegmentParserState.group;
} else {
i--;
state = SegmentParserState.static;
}
break;
case SegmentParserState.static:
if (c === "[") {
consumeBuffer();
state = SegmentParserState.dynamic;
} else if (c === "(") {
consumeBuffer();
state = SegmentParserState.group;
} else {
buffer += c;
}
break;
case SegmentParserState.catchall:
case SegmentParserState.dynamic:
case SegmentParserState.optional:
case SegmentParserState.group:
if (buffer === "...") {
buffer = "";
state = SegmentParserState.catchall;
}
if (c === "[" && state === SegmentParserState.dynamic) {
state = SegmentParserState.optional;
}
if (c === "]" && (state !== SegmentParserState.optional || segment[i - 1] === "]")) {
if (!buffer) {
throw new Error("Empty param");
} else {
consumeBuffer();
}
state = SegmentParserState.initial;
} else if (c === ")" && state === SegmentParserState.group) {
if (!buffer) {
throw new Error("Empty group");
} else {
consumeBuffer();
}
state = SegmentParserState.initial;
} else if (c && PARAM_CHAR_RE.test(c)) {
buffer += c;
} else if (state === SegmentParserState.dynamic || state === SegmentParserState.optional) {
if (c !== "[" && c !== "]") {
logger.warn(`'\`${c}\`' is not allowed in a dynamic route parameter and has been ignored. Consider renaming \`${absolutePath}\`.`);
}
}
break;
}
i++;
}
if (state === SegmentParserState.dynamic) {
throw new Error(`Unfinished param "${buffer}"`);
}
consumeBuffer();
return tokens;
}
function findRouteByName(name, routes) {
for (const route of routes) {
if (route.name === name) {
return route;
}
if (route.children && route.children.length > 0) {
const child = findRouteByName(name, route.children);
if (child) {
return child;
}
}
}
}
const NESTED_PAGE_RE = /\//g;
function prepareRoutes(routes, parent, names = /* @__PURE__ */ new Set()) {
for (const route of routes) {
if (route.name) {
route.name = route.name.replace(INDEX_PAGE_RE, "").replace(NESTED_PAGE_RE, "-");
if (names.has(route.name)) {
const existingRoute = findRouteByName(route.name, routes);
const extra = existingRoute?.name ? `is the same as \`${existingRoute.file}\`` : "is a duplicate";
logger.warn(`Route name generated for \`${route.file}\` ${extra}. You may wish to set a custom name using \`definePageMeta\` within the page file.`);
}
}
if (parent && route.path[0] === "/") {
route.path = route.path.slice(1);
}
if (route.children?.length) {
route.children = prepareRoutes(route.children, route, names);
}
if (route.children?.find((childRoute) => childRoute.path === "")) {
delete route.name;
}
if (route.name) {
names.add(route.name);
}
}
return routes;
}
function serializeRouteValue(value, skipSerialisation = false) {
if (skipSerialisation || value === void 0) {
return void 0;
}
return JSON.stringify(value);
}
function normalizeRoutes(routes, metaImports = /* @__PURE__ */ new Set(), options) {
return {
imports: metaImports,
routes: genArrayFromRaw(routes.map((page) => {
const markedDynamic = page.meta?.[DYNAMIC_META_KEY] ?? /* @__PURE__ */ new Set();
const metaFiltered = {};
let skipMeta = true;
for (const key in page.meta || {}) {
if (key !== DYNAMIC_META_KEY && page.meta[key] !== void 0) {
skipMeta = false;
metaFiltered[key] = page.meta[key];
}
}
const skipAlias = toArray(page.alias).every((val) => !val);
const route = {
path: serializeRouteValue(page.path),
props: serializeRouteValue(page.props),
name: serializeRouteValue(page.name),
meta: serializeRouteValue(metaFiltered, skipMeta),
alias: serializeRouteValue(toArray(page.alias), skipAlias),
redirect: serializeRouteValue(page.redirect)
};
for (const key of [...defaultExtractionKeys, "meta"]) {
if (route[key] === void 0) {
delete route[key];
}
}
if (page.children?.length) {
route.children = normalizeRoutes(page.children, metaImports, options).routes;
}
if (!page.file) {
return route;
}
const file = normalize(page.file);
const pageImportName = genSafeVariableName(filename(file) + hash(file));
const metaImportName = pageImportName + "Meta";
metaImports.add(genImport(`${file}?macro=true`, [{ name: "default", as: metaImportName }]));
if (page._sync) {
metaImports.add(genImport(file, [{ name: "default", as: pageImportName }]));
}
const pageImport = page._sync && page.mode !== "client" ? pageImportName : genDynamicImport(file);
const metaRoute = {
name: `${metaImportName}?.name ?? ${route.name}`,
path: `${metaImportName}?.path ?? ${route.path}`,
props: `${metaImportName}?.props ?? ${route.props ?? false}`,
meta: `${metaImportName} || {}`,
alias: `${metaImportName}?.alias || []`,
redirect: `${metaImportName}?.redirect`,
component: page.mode === "server" ? `() => createIslandPage(${route.name})` : page.mode === "client" ? `() => createClientPage(${pageImport})` : pageImport
};
if (page.mode === "server") {
metaImports.add(`
let _createIslandPage
async function createIslandPage (name) {
_createIslandPage ||= await import(${JSON.stringify(options?.serverComponentRuntime)}).then(r => r.createIslandPage)
return _createIslandPage(name)
};`);
} else if (page.mode === "client") {
metaImports.add(`
let _createClientPage
async function createClientPage(loader) {
_createClientPage ||= await import(${JSON.stringify(options?.clientComponentRuntime)}).then(r => r.createClientPage)
return _createClientPage(loader);
}`);
}
if (route.children) {
metaRoute.children = route.children;
}
if (route.meta) {
metaRoute.meta = `{ ...(${metaImportName} || {}), ...${route.meta} }`;
}
if (options?.overrideMeta) {
for (const key of ["name", "path"]) {
if (markedDynamic.has(key)) {
continue;
}
metaRoute[key] = route[key] ?? `${metaImportName}?.${key}`;
}
for (const key of ["meta", "alias", "redirect", "props"]) {
if (markedDynamic.has(key)) {
continue;
}
if (route[key] == null) {
delete metaRoute[key];
continue;
}
metaRoute[key] = route[key];
}
} else {
if (route.alias != null) {
metaRoute.alias = `${route.alias}.concat(${metaImportName}?.alias || [])`;
}
if (route.redirect != null) {
metaRoute.redirect = route.redirect;
}
}
return metaRoute;
}))
};
}
const PATH_TO_NITRO_GLOB_RE = /\/[^:/]*:\w.*$/;
function pathToNitroGlob(path) {
if (!path) {
return null;
}
if (path.indexOf(":") !== path.lastIndexOf(":")) {
return null;
}
return path.replace(PATH_TO_NITRO_GLOB_RE, "/**");
}
function resolveRoutePaths(page, parent = "/") {
return [
joinURL(parent, page.path),
...page.children?.flatMap((child) => resolveRoutePaths(child, joinURL(parent, page.path))) || []
];
}
function isSerializable(code, node) {
if (node.type === "ObjectExpression") {
const valueString = code.slice(node.start, node.end);
try {
return {
value: JSON.parse(runInNewContext(`JSON.stringify(${valueString})`, {})),
serializable: true
};
} catch {
return {
serializable: false
};
}
}
if (node.type === "ArrayExpression") {
const values = [];
for (const element of node.elements) {
if (!element) {
continue;
}
const { serializable, value } = isSerializable(code, element);
if (!serializable) {
return {
serializable: false
};
}
values.push(value);
}
return {
value: values,
serializable: true
};
}
if (node.type === "Literal" && (typeof node.value === "string" || typeof node.value === "boolean" || typeof node.value === "number" || node.value === null)) {
return {
value: node.value,
serializable: true
};
}
if (node.type === "TSSatisfiesExpression" || node.type === "TSAsExpression" || node.type === "ParenthesizedExpression") {
return isSerializable(code, node.expression);
}
return {
serializable: false
};
}
function toRou3Patterns(pages, prefix = "/") {
const routes = [];
for (const page of pages) {
const path = page.path.replace(/\([^)]*\)/g, "").replace(/:(\w+)\*.*/g, (_, name) => `**:${name}`).replace(/:([^/*]*)/g, (_, name) => `:${name.replace(/\W/g, (r) => r === "?" ? "" : "_")}`);
routes.push(joinURL(prefix, path));
if (page.children) {
routes.push(...toRou3Patterns(page.children, joinURL(prefix, path)));
}
}
return routes;
}
function globRouteRulesFromPages(pages, paths = {}, prefix = "") {
for (const page of pages) {
if (page.rules) {
if (Object.keys(page.rules).length) {
const glob = pathToNitroGlob(prefix + page.path);
if (glob) {
paths[glob] = page.rules;
}
}
delete page.rules;
}
if (page.children?.length) {
globRouteRulesFromPages(page.children, paths, prefix + page.path + "/");
}
}
return paths;
}
function removePagesRules(routes) {
for (const route of routes) {
delete route.rules;
if (route.children?.length) {
removePagesRules(route.children);
}
}
}
const HAS_MACRO_RE = /\bdefinePageMeta\s*\(\s*/;
const CODE_EMPTY = `
const __nuxt_page_meta = null
export default __nuxt_page_meta
`;
const CODE_DEV_EMPTY = `
const __nuxt_page_meta = {}
export default __nuxt_page_meta
`;
const CODE_HMR = `
// Vite
if (import.meta.hot) {
import.meta.hot.accept(mod => {
Object.assign(__nuxt_page_meta, mod)
})
}
// webpack
if (import.meta.webpackHot) {
import.meta.webpackHot.accept((err) => {
if (err) { window.location = window.location.href }
})
}`;
const PageMetaPlugin = (options = {}) => createUnplugin(() => {
return {
name: "nuxt:pages-macros-transform",
enforce: "post",
transformInclude(id) {
return !!parseMacroQuery(id).macro;
},
transform(code, id) {
const query = parseMacroQuery(id);
if (query.type && query.type !== "script") {
return;
}
const s = new MagicString(code);
function result() {
if (s.hasChanged()) {
return {
code: s.toString(),
map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
};
}
}
const hasMacro = HAS_MACRO_RE.test(code);
const imports = findStaticImports(code);
const scriptImport = imports.find((i) => parseMacroQuery(i.specifier).type === "script");
if (scriptImport) {
const reorderedQuery = rewriteQuery(scriptImport.specifier);
const quotedSpecifier = getQuotedSpecifier(scriptImport.code)?.replace(scriptImport.specifier, reorderedQuery) ?? JSON.stringify(reorderedQuery);
s.overwrite(0, code.length, `export { default } from ${quotedSpecifier}`);
return result();
}
const currentExports = findExports(code);
for (const match of currentExports) {
if (match.type !== "default" || !match.specifier) {
continue;
}
const reorderedQuery = rewriteQuery(match.specifier);
const quotedSpecifier = getQuotedSpecifier(match.code)?.replace(match.specifier, reorderedQuery) ?? JSON.stringify(reorderedQuery);
s.overwrite(0, code.length, `export { default } from ${quotedSpecifier}`);
return result();
}
if (!hasMacro && !code.includes("export { default }") && !code.includes("__nuxt_page_meta")) {
if (!code) {
s.append(options.dev ? CODE_DEV_EMPTY + CODE_HMR : CODE_EMPTY);
const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href));
logger.error(`The file \`${pathname}\` is not a valid page as it has no content.`);
} else {
s.overwrite(0, code.length, options.dev ? CODE_DEV_EMPTY + CODE_HMR : CODE_EMPTY);
}
return result();
}
const importMap = /* @__PURE__ */ new Map();
const addedImports = /* @__PURE__ */ new Set();
for (const i of imports) {
const parsed = parseStaticImport(i);
for (const name of [
parsed.defaultImport,
...Object.values(parsed.namedImports || {}),
parsed.namespacedImport
].filter(Boolean)) {
importMap.set(name, i);
}
}
function isStaticIdentifier(name) {
return !!(name && importMap.has(name));
}
function addImport(name) {
if (!isStaticIdentifier(name)) {
return;
}
const importValue = importMap.get(name).code.trim();
if (!addedImports.has(importValue)) {
addedImports.add(importValue);
}
}
const declarationNodes = [];
const addedDeclarations = /* @__PURE__ */ new Set();
function addDeclaration(node) {
const codeSectionKey = `${resolveStart(node)}-${resolveEnd(node)}`;
if (addedDeclarations.has(codeSectionKey)) {
return;
}
addedDeclarations.add(codeSectionKey);
declarationNodes.push(node);
}
function addImportOrDeclaration(name, node) {
if (isStaticIdentifier(name)) {
addImport(name);
} else {
const declaration = scopeTracker.getDeclaration(name);
if (declaration && declaration !== node) {
processDeclaration(declaration);
}
}
}
const scopeTracker = new ScopeTracker({
preserveExitedScopes: true
});
function processDeclaration(scopeTrackerNode) {
if (scopeTrackerNode?.type === "Variable") {
addDeclaration(scopeTrackerNode);
for (const decl of scopeTrackerNode.variableNode.declarations) {
if (!decl.init) {
continue;
}
walk(decl.init, {
enter: (node, parent) => {
if (node.type === "AwaitExpression") {
logger.error(`Await expressions are not supported in definePageMeta. File: '${id}'`);
throw new Error("await in definePageMeta");
}
if (isBindingIdentifier(node, parent) || node.type !== "Identifier") {
return;
}
addImportOrDeclaration(node.name, scopeTrackerNode);
}
});
}
} else if (scopeTrackerNode?.type === "Function") {
if (scopeTrackerNode.node.type === "ArrowFunctionExpression") {
return;
}
const name = scopeTrackerNode.node.id?.name;
if (!name) {
return;
}
addDeclaration(scopeTrackerNode);
const undeclaredIdentifiers = getUndeclaredIdentifiersInFunction(scopeTrackerNode.node);
for (const name2 of undeclaredIdentifiers) {
addImportOrDeclaration(name2);
}
}
}
const { program: ast } = parseAndWalk(code, id, {
scopeTracker,
parseOptions: {
lang: query.lang ?? "ts"
}
});
scopeTracker.freeze();
let instances = 0;
walk(ast, {
scopeTracker,
enter: (node) => {
if (node.type !== "CallExpression" || node.callee.type !== "Identifier") {
return;
}
if (!("name" in node.callee) || node.callee.name !== "definePageMeta") {
return;
}
instances++;
const meta = node.arguments[0];
if (!meta) {
return;
}
const metaCode = code.slice(meta.start, meta.end);
const m = new MagicString(metaCode);
if (meta.type === "ObjectExpression") {
for (let i = 0; i < meta.properties.length; i++) {
const prop = meta.properties[i];
if (prop.type === "Property" && prop.key.type === "Identifier" && options.extractedKeys?.includes(prop.key.name)) {
const { serializable } = isSerializable(metaCode, prop.value);
if (!serializable) {
continue;
}
const nextProperty = meta.properties[i + 1];
if (nextProperty) {
m.overwrite(prop.start - meta.start, nextProperty.start - meta.start, "");
} else if (code[prop.end] === ",") {
m.overwrite(prop.start - meta.start, prop.end - meta.start + 1, "");
} else {
m.overwrite(prop.start - meta.start, prop.end - meta.start, "");
}
}
}
}
const definePageMetaScope = scopeTracker.getCurrentScope();
walk(meta, {
scopeTracker,
enter(node2, parent) {
if (isBindingIdentifier(node2, parent) || node2.type !== "Identifier") {
return;
}
const declaration = scopeTracker.getDeclaration(node2.name);
if (declaration) {
if (declaration.isUnderScope(definePageMetaScope) && (scopeTracker.isCurrentScopeUnder(declaration.scope) || resolveStart(declaration) < node2.start)) {
return;
}
}
if (isStaticIdentifier(node2.name)) {
addImport(node2.name);
} else if (declaration) {
processDeclaration(declaration);
}
}
});
const importStatements = Array.from(addedImports).join("\n");
const declarations = declarationNodes.sort((a, b) => resolveStart(a) - resolveStart(b)).map((node2) => code.slice(resolveStart(node2), resolveEnd(node2))).join("\n");
const extracted = [
importStatements,
declarations,
`const __nuxt_page_meta = ${m.toString() || "null"}
export default __nuxt_page_meta` + (options.dev ? CODE_HMR : "")
].join("\n");
s.overwrite(0, code.length, extracted.trim());
}
});
if (instances > 1) {
throw new Error("Multiple `definePageMeta` calls are not supported. File: " + id.replace(/\?.+$/, ""));
}
if (!s.hasChanged() && !code.includes("__nuxt_page_meta")) {
s.overwrite(0, code.length, options.dev ? CODE_DEV_EMPTY + CODE_HMR : CODE_EMPTY);
}
return result();
},
vite: {
handleHotUpdate: {
order: "post",
handler: ({ file, modules, server }) => {
if (options.routesPath && options.isPage?.(file)) {
const macroModule = server.moduleGraph.getModuleById(file + "?macro=true");
const routesModule = server.moduleGraph.getModuleById("virtual:nuxt:" + encodeURIComponent(options.routesPath));
return [
...modules,
...macroModule ? [macroModule] : [],
...routesModule ? [routesModule] : []
];
}
}
}
}
};
});
const QUERY_START_RE = /^\?/;
const MACRO_RE = /&macro=true/;
function rewriteQuery(id) {
return id.replace(/\?.+$/, (r) => "?macro=true&" + r.replace(QUERY_START_RE, "").replace(MACRO_RE, ""));
}
function parseMacroQuery(id) {
const { search } = parseURL(decodeURIComponent(isAbsolute(id) ? pathToFileURL(id).href : id).replace(/\?macro=true$/, ""));
const query = parseQuery(search);
if (id.includes("?macro=true")) {
return { macro: "true", ...query };
}
return query;
}
const QUOTED_SPECIFIER_RE = /(["']).*\1/;
function getQuotedSpecifier(id) {
return id.match(QUOTED_SPECIFIER_RE)?.[0];
}
function resolveStart(node) {
return "fnNode" in node ? node.fnNode.start : node.start;
}
function resolveEnd(node) {
return "fnNode" in node ? node.fnNode.end : node.end;
}
const INJECTION_RE_TEMPLATE = /\b_ctx\.\$route\b/g;
const INJECTION_RE_SCRIPT = /\bthis\.\$route\b/g;
const INJECTION_SINGLE_RE = /\bthis\.\$route\b|\b_ctx\.\$route\b/;
const RouteInjectionPlugin = (nuxt) => createUnplugin(() => {
return {
name: "nuxt:route-injection-plugin",
enforce: "post",
transformInclude(id) {
return isVue(id, { type: ["template", "script"] });
},
transform: {
filter: {
code: { include: INJECTION_SINGLE_RE }
},
handler(code) {
if (code.includes("_ctx._.provides[__nuxt_route_symbol") || code.includes("this._.provides[__nuxt_route_symbol")) {
return;
}
let replaced = false;
const s = new MagicString(code);
const strippedCode = stripLiteral(code);
const replaceMatches = (regExp, replacement) => {
for (const match of strippedCode.matchAll(regExp)) {
const start = match.index;
const end = start + match[0].length;
s.overwrite(start, end, replacement);
replaced ||= true;
}
};
replaceMatches(INJECTION_RE_TEMPLATE, "(_ctx._.provides[__nuxt_route_symbol] || _ctx.$route)");
replaceMatches(INJECTION_RE_SCRIPT, "(this._.provides[__nuxt_route_symbol] || this.$route)");
if (replaced) {
s.prepend("import { PageRouteSymbol as __nuxt_route_symbol } from '#app/components/injections';\n");
}
if (s.hasChanged()) {
return {
code: s.toString(),
map: nuxt.options.sourcemap.client || nuxt.options.sourcemap.server ? s.generateMap({ hires: true }) : void 0
};
}
}
}
};
});
const OPTIONAL_PARAM_RE = /^\/?:.*(?:\?|\(\.\*\)\*)$/;
const runtimeDir = resolve(distDir, "pages/runtime");
async function resolveRouterOptions(nuxt, builtInRouterOptions) {
const context = {
files: []
};
for (const layer of nuxt.options._layers) {
const path = await findPath(resolve(layer.config.srcDir, layer.config.dir?.app || "app", "router.options"));
if (path) {
context.files.unshift({ path });
}
}
context.files.unshift({ path: builtInRouterOptions, optional: true });
await nuxt.callHook("pages:routerOptions", context);
return context.files;
}
const pagesModule = defineNuxtModule({
meta: {
name: "nuxt:pages",
configKey: "pages"
},
defaults: (nuxt) => ({
enabled: typeof nuxt.options.pages === "boolean" ? nuxt.options.pages : void 0,
pattern: `**/*{${nuxt.options.extensions.join(",")}}`
}),
async setup(_options, nuxt) {
const options = typeof _options === "boolean" ? { enabled: _options ?? nuxt.options.pages, pattern: `**/*{${nuxt.options.extensions.join(",")}}` } : { ..._options };
options.pattern = Array.isArray(options.pattern) ? [...new Set(options.pattern)] : options.pattern;
let inlineRulesCache = {};
let updateRouteConfig;
if (nuxt.options.experimental.inlineRouteRules) {
nuxt.hook("nitro:init", (nitro) => {
updateRouteConfig = async (inlineRules) => {
if (!isEqual(inlineRulesCache, inlineRules)) {
await nitro.updateConfig({ routeRules: defu(inlineRules, nitro.options._config.routeRules) });
inlineRulesCache = inlineRules;
}
};
});
}
const resolvePagesRoutes$1 = async (pattern, nuxt2) => {
const pages = await resolvePagesRoutes(pattern, nuxt2);
if (nuxt2.options.experimental.inlineRouteRules) {
const routeRules = globRouteRulesFromPages(pages);
await updateRouteConfig?.(routeRules);
} else {
removePagesRules(pages);
}
return pages;
};
const useExperimentalTypedPages = nuxt.options.experimental.typedPages;
const builtInRouterOptions = await findPath(resolve(runtimeDir, "router.options")) || resolve(runtimeDir, "router.options");
const pagesDirs = getLayerDirectories(nuxt).map((dirs) => dirs.appPages);
nuxt.options.alias["#vue-router"] = "vue-router";
const routerPath = await resolveTypePath("vue-router", "", nuxt.options.modulesDir) || "vue-router";
nuxt.hook("prepare:types", ({ tsConfig }) => {
tsConfig.compilerOptions ||= {};
tsConfig.compilerOptions.paths ||= {};
tsConfig.compilerOptions.paths["#vue-router"] = [routerPath];
delete tsConfig.compilerOptions.paths["#vue-router/*"];
});
const isNonEmptyDir = (dir) => existsSync(dir) && readdirSync(dir).length;
const userPreference = options.enabled;
const isPagesEnabled = async () => {
if (typeof userPreference === "boolean") {
return userPreference;
}
const routerOptionsFiles = await resolveRouterOptions(nuxt, builtInRouterOptions);
if (routerOptionsFiles.filter((p) => !p.optional).length > 0) {
return true;
}
if (pagesDirs.some((dir) => isNonEmptyDir(dir))) {
return true;
}
const pages = await resolvePagesRoutes$1(options.pattern, nuxt);
if (pages.length) {
if (nuxt.apps.default) {
nuxt.apps.default.pages = pages;
}
return true;
}
return false;
};
options.enabled = await isPagesEnabled();
nuxt.options.pages = options;
Object.defineProperty(nuxt.options.pages, "toString", {
enumerable: false,
get: () => () => options.enabled
});
if (nuxt.options.dev && options.enabled) {
addPlugin(resolve(runtimeDir, "plugins/check-if-page-unused"));
}
nuxt.hook("app:templates", (app) => {
if (!nuxt.options.ssr && app.pages?.some((p) => p.mode === "server")) {
logger.warn("Using server pages with `ssr: false` is not supported with auto-detected component islands. Set `experimental.componentIslands` to `true`.");
}
});
const restartPaths = nuxt.options._layers.flatMap((layer) => {
const pagesDir = (layer.config.rootDir === nuxt.options.rootDir ? nuxt.options.dir : layer.config.dir)?.pages || "pages";
return [
resolve(layer.config.srcDir || layer.cwd, layer.config.dir?.app || "app", "router.options.ts"),
resolve(layer.config.srcDir || layer.cwd, pagesDir)
];
});
nuxt.hooks.hook("builder:watch", async (event, relativePath) => {
const path = resolve(nuxt.options.srcDir, relativePath);
if (restartPaths.some((p) => p === path || path.startsWith(p + "/"))) {
const newSetting = await isPagesEnabled();
if (options.enabled !== newSetting) {
logger.info("Pages", newSetting ? "enabled" : "disabled");
return nuxt.callHook("restart");
}
}
});
if (!options.enabled) {
addPlugin(resolve(distDir, "app/plugins/router"));
addTemplate({
filename: "pages.mjs",
getContents: () => [
"export { useRoute } from '#app/composables/router'",
"export const START_LOCATION = Symbol('router:start-location')"
].join("\n")
});
addTemplate({
filename: "router.options.mjs",
getContents: () => {
return [
"export const hashMode = false",
"export default {}"
].join("\n");
}
});
addTypeTemplate({
filename: "types/middleware.d.ts",
getContents: () => [
"declare module 'nitropack' {",
" interface NitroRouteConfig {",
" appMiddleware?: string | string[] | Record<string, boolean>",
" }",
"}",
"export {}"
].join("\n")
}, { nuxt: true, nitro: true });
addComponent({
name: "NuxtPage",
priority: 10,
// built-in that we do not expect the user to override
filePath: resolve(distDir, "pages/runtime/page-placeholder")
});
nuxt.hook("nitro:init", (nitro) => {
if (nuxt.options.dev || !nuxt.options.ssr || !nitro.options.static || !nitro.options.prerender.crawlLinks) {
return;
}
nitro.options.prerender.routes.push("/");
});
return;
}
if (useExperimentalTypedPages) {
const declarationFile = "./types/typed-router.d.ts";
const typedRouterOptions = {
routesFolder: [],
dts: resolve(nuxt.options.buildDir, declarationFile),
logs: nuxt.options.debug && nuxt.options.debug.router,
async beforeWriteFiles(rootPage) {
rootPage.children.forEach((child) => child.delete());
const pages = nuxt.apps.default?.pages || await resolvePagesRoutes$1(options.pattern, nuxt);
if (nuxt.apps.default) {
nuxt.apps.default.pages = pages;
}
const addedPagePaths = /* @__PURE__ */ new Set();
function addPage(parent, page, basePath = "") {
const absolutePagePath = joinURL(basePath, page.path);
const route = addedPagePaths.has(absolutePagePath) ? parent : page.path[0] === "/" ? rootPage.insert(page.path, page.file) : parent.insert(page.path, page.file);
addedPagePaths.add(absolutePagePath);
if (page.meta) {
route.addToMeta(page.meta);
}
if (page.alias) {
route.addAlias(page.alias);
}
if (page.name) {
route.name = page.name;
}
if (page.children) {
page.children.forEach((child) => addPage(route, child, absolutePagePath));
}
}
for (const page of pages) {
addPage(rootPage, page);
}
}
};
nuxt.hook("prepare:types", ({ references }) => {
references.push({ path: declarationFile });
references.push({ types: "unplugin-vue-router/client" });
});
const context = createRoutesContext(resolveOptions(typedRouterOptions));
const dtsFile = resolve(nuxt.options.buildDir, declarationFile);
await mkdir(dirname(dtsFile), { recursive: true });
await context.scanPages(false);
if (nuxt.options._prepare || !nuxt.options.dev) {
const dts = await readFile(dtsFile, "utf-8");
addTemplate({
filename: "types/typed-router.d.ts",
getContents: () => dts
});
}
nuxt.hook("app:templatesGenerated", async (_app, _templates, options2) => {
if (!options2?.filter || options2.filter({ filename: "routes.mjs" })) {
await context.scanPages();
}
});
}
nuxt.hook("prepare:types", ({ references }) => {
references.push({ types: useExperimentalTypedPages ? "vue-router/auto-routes" : "vue-router" });
});
nuxt.hook("imports:sources", (sources) => {
const routerImports = sources.find((s) => s.from === "#app/composables/router" && s.imports.includes("onBeforeRouteLeave"));
if (routerImports) {
routerImports.from = "vue-router";
}
});
const updateTemplatePaths = getLayerDirectories(nuxt).flatMap((dirs) => [
dirs.appPages,
dirs.appLayouts,
dirs.appMiddleware
]);
function isPage(file, pages = nuxt.apps.default?.pages) {
if (!pages) {
return false;
}
return pages.some((page) => page.file === file) || pages.some((page) => page.children && isPage(file, page.children));
}
nuxt.hooks.hookOnce("app:templates", async (app) => {
app.pages ||= await resolvePagesRoutes$1(options.pattern, nuxt);
});
nuxt.hook("builder:watch", async (event, relativePath) => {
const path = resolve(nuxt.options.srcDir, relativePath);
const shouldAlwaysRegenerate = nuxt.options.experimental.scanPageMeta && isPage(path);
if (event === "change" && !shouldAlwaysRegenerate) {
return;
}
if (shouldAlwaysRegenerate || updateTemplatePaths.some((dir) => path.startsWith(dir))) {
nuxt.apps.default.pages = await resolvePagesRoutes$1(options.pattern, nuxt);
}
});
nuxt.hook("app:resolve", (app) => {
if (app.mainComponent === resolve(nuxt.options.appDir, "components/welcome.vue")) {
app.mainComponent = resolve(runtimeDir, "app.vue");
}
app.middleware.unshift({
name: "validate",
path: resolve(runtimeDir, "validate"),
global: true
});
});
nuxt.hook("app:resolve", (app) => {
const nitro = useNitro();
if (nitro.options.prerender.crawlLinks || Object.values(nitro.options.routeRules).some((rule) => rule.prerender)) {
app.plugins.push({
src: resolve(runtimeDir, "plugins/prerender.server"),
mode: "server"
});
}
});
const prerenderRoutes = /* @__PURE__ */ new Set();
function processPages(pages, currentPath = "/") {
for (const page of pages) {
if (OPTIONAL_PARAM_RE.test(page.path) && !page.children?.length) {
prerenderRoutes.add(currentPath);
}
if (page.path.includes(":")) {
continue;
}
const route = joinURL(currentPath, page.path);
prerenderRoutes.add(route);
if (page.children) {
processPages(page.children, route);
}
}
}
nuxt.hook("pages:extend", (pages) => {
if (nuxt.options.dev) {
return;
}
prerenderRoutes.clear();
processPages(pages);
});
nuxt.hook("nitro:build:before", (nitro) => {
if (nuxt.options.dev || nuxt.options.router.options.hashMode) {
return;
}
nitro.options.ssrRoutes = [
...nitro.options.ssrRoutes || [],
...toRou3Patterns(nuxt.apps.default?.pages || [])
];
if (!nitro.options.static && !nitro.options.prerender.crawlLinks) {
const routeRulesMatcher = toRouteMatcher(createRouter({ routes: nitro.options.routeRules }));
for (const route of prerenderRoutes) {
const rules = defu({}, ...routeRulesMatcher.matchAll(route).reverse());
if (rules.prerender) {
nitro.options.prerender.routes.push(route);
}
}
}
if (!nitro.options.static || !nitro.options.prerender.crawlLinks) {
return;
}
if (nuxt.options.ssr) {
const [firstPage] = [...prerenderRoutes].sort();
nitro.options.prerender.routes.push(firstPage || "/");
return;
}
for (const route of nitro.options.prerender.routes || []) {
prerenderRoutes.add(route);
}
nitro.options.prerender.routes = Array.from(prerenderRoutes);
});
nuxt.hook("imports:extend", (imports) => {
imports.push(
{ name: "definePageMeta", as: "definePageMeta", from: resolve(runtimeDir, "composables") },
{ name: "useLink", as: "useLink", from: "vue-router" }
);
if (nuxt.options.experimental.inlineRouteRules) {
imports.push({ name: "defineRouteRules", as: "defineRouteRules", from: resolve(runtimeDir, "composables") });
}
});
const componentStubPath = await resolvePath(resolve(runtimeDir, "component-stub"));
if (nuxt.options.test && nuxt.options.dev) {
nuxt.hook("pages:extend", (routes) => {
routes.push({
_sync: true,
path: "/__nuxt_component_test__/:pathMatch(.*)",
file: componentStubPath
});
});
}
if (nuxt.options.experimental.appManifest) {
nuxt.hook("pages:extend", (routes) => {
const nitro = useNitro();
let resolvedRoutes;
for (const [path, rule] of Object.entries(nitro.options.routeRules)) {
if (!rule.redirect) {
continue;
}
resolvedRoutes ||= routes.flatMap((route) => resolveRoutePaths(route));
if (resolvedRoutes.includes(path)) {
continue;
}
routes.push({
_sync: true,
path: path.replace(/\/[^/]*\*\*/, "/:pathMatch(.*)"),
file: componentStubPath
});
}
});
}
const extraPageMetaExtractionKeys = nuxt.options?.experimental?.extraPageMetaExtractionKeys || [];
const extractedKeys = nuxt.options.future.compatibilityVersion === 4 ? [...defaultExtractionKeys, "middleware", ...extraPageMetaExtractionKeys] : ["middleware", ...extraPageMetaExtractionKeys];
nuxt.hook("modules:done", () => {
addBuildPlugin(PageMetaPlugin({
dev: nuxt.options.dev,
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
isPage,
routesPath: resolve(nuxt.options.buildDir, "routes.mjs"),
extractedKeys: nuxt.options.experimental.scanPageMeta ? extractedKeys : []
}));
});
addPlugin(resolve(runtimeDir, "plugins/prefetch.client"));
if (nuxt.options.experimental.templateRouteInjection) {
addBuildPlugin(RouteInjectionPlugin(nuxt), { server: false });
}
addPlugin(resolve(runtimeDir, "plugins/router"));
const getSources = (pages) => pages.filter((p) => Boolean(p.file)).flatMap(
(p) => [relative(nuxt.options.srcDir, p.file), ...p.children?.length ? getSources(p.children) : []]
);
nuxt.hook("build:manifest", (manifest) => {
if (nuxt.options.dev) {
return;
}
const sourceFiles = nuxt.apps.default?.pages?.length ? getSources(nuxt.apps.default.pages) : [];
for (const [key, chunk] of Object.entries(manifest)) {
if (chunk.src && Object.values(nuxt.apps).some((app) => app.pages?.some((page) => page.mode === "server" && page.file === join(nuxt.options.srcDir, chunk.src)))) {
delete manifest[key];
continue;
}
if (chunk.isEntry) {
chunk.dynamicImports = chunk.dynamicImports?.filter((i) => !sourceFiles.includes(i));
}
}
});
const serverComponentRuntime = await findPath(join(distDir, "components/runtime/server-component")) ?? join(distDir, "components/runtime/server-component");
const clientComponentRuntime = await findPath(join(distDir, "components/runtime/client-component")) ?? join(distDir, "components/runtime/client-component");
addTemplate({
filename: "routes.mjs",
getContents({ app }) {
if (!app.pages) {
return ROUTES_HMR_CODE + "export default []";
}
const { routes, imports } = normalizeRoutes(app.pages, /* @__PURE__ */ new Set(), {
serverComponentRuntime,
clientComponentRuntime,
overrideMeta: !!nuxt.options.experimental.scanPageMeta
});
return ROUTES_HMR_CODE + [...imports, `export default ${routes}`].join("\n");
}
});
addTemplate({
filename: "pages.mjs",
getContents: () => "export { START_LOCATION, useRoute } from 'vue-router'"
});
nuxt.options.vite.resolve ||= {};
nuxt.options.vite.resolve.dedupe ||= [];
nuxt.options.vite.resolve.dedupe.push("vue-router");
addTemplate({
filename: "router.options.mjs",
getContents: async ({ nuxt: nuxt2 }) => {
const routerOptionsFiles = await resolveRouterOptions(nuxt2, builtInRouterOptions);
const configRouterOptions = genObjectFromRawEntries(Object.entries(nuxt2.options.router.options).map(([key, value]) => [key, genString(value)]));
return [
...routerOptionsFiles.map((file, index) => genImport(file.path, `routerOptions${index}`)),
`const configRouterOptions = ${configRouterOptions}`,
`export const hashMode = ${[...routerOptionsFiles.filter((o) => o.path !== builtInRouterOptions).map((_, index) => `routerOptions${index}.hashMode`).reverse(), nuxt2.options.router.options.hashMode].join(" ?? ")}`,
"export default {",
"...configRouterOptions,",
...routerOptionsFiles.map((_, index) => `...routerOptions${index},`),
"}"
].join("\n");
}
});
addTypeTemplate({
filename: "types/middleware.d.ts",
getContents: ({ app }) => {
const namedMiddleware = app.middleware.filter((mw) => !mw.global);
return [
"import type { NavigationGuard } from 'vue-router'",
`export type MiddlewareKey = ${namedMiddleware.map((mw) => genString(mw.name)).join(" | ") || "never"}`,
"declare module 'nuxt/app' {",
" interface PageMeta {",
" middleware?: MiddlewareKey | NavigationGuard | Array<MiddlewareKey | NavigationGuard>",
" }",
"}"
].join("\n");
}
});
addTypeTemplate({
filename: "types/nitro-middleware.d.ts",
getContents: ({ app }) => {
const namedMiddleware = app.middleware.filter((mw) => !mw.global);
return [
`export type MiddlewareKey = ${namedMiddleware.map((mw) => genString(mw.name)).join(" | ") || "never"}`,
"declare module 'nitropack' {",
" interface NitroRouteConfig {",
" appMiddleware?: MiddlewareKey | MiddlewareKey[] | Record<MiddlewareKey, boolean>",
" }",
"}"
].join("\n");
}
}, { nuxt: true, nitro: true });
addTypeTemplate({
filename: "types/layouts.d.ts",
getContents: ({ app }) => {
return [
"import type { ComputedRef, MaybeRef } from 'vue'",
`export type LayoutKey = ${Object.keys(app.layouts).map((name) => genString(name)).join(" | ") || "string"}`,
"declare module 'nuxt/app' {",
" interface PageMeta {",
" layout?: MaybeRef<LayoutKey | false> | ComputedRef<LayoutKey | false>",
" }",
"}"
].join("\n");
}
});
if (nuxt.options.experimental.viewTransition) {
addTypeTemplate({
filename: "types/view-transitions.d.ts",
getContents: () => {
return [
"declare module 'nuxt/app' {",
" interface PageMeta {",
" viewTransition?: boolean | 'always'",
" }",
"}",
"export {}"
].join("\n");
}
});
}
addComponent({
name: "NuxtPage",
priority: 10,
// built-in that we do not expect the user to override
filePath: resolve(distDir, "pages/runtime/page")
});
}
});
const ROUTES_HMR_CODE = (
/* js */
`
if (import.meta.hot) {
import.meta.hot.accept((mod) => {
const router = import.meta.hot.data.router
const generateRoutes = import.meta.hot.data.generateRoutes
if (!router || !generateRoutes) {
import.meta.hot.invalidate('[nuxt] Cannot replace routes because there is no active router. Reloading.')
return
}
router.clearRoutes()
const routes = generateRoutes(mod.default || mod)
function addRoutes (routes) {
for (const route of routes) {
router.addRoute(route)
}
router.replace(router.currentRoute.value.fullPath)
}
if (routes && 'then' in routes) {
routes.then(addRoutes)
} else {
addRoutes(routes)
}
})
}
export function handleHotUpdate(_router, _generateRoutes) {
if (import.meta.hot) {
import.meta.hot.data ||= {}
import.meta.hot.data.router = _router
import.meta.hot.data.generateRoutes = _generateRoutes
}
}
`
);
const UNHEAD_LIB_RE = /node_modules[/\\](?:@unhead[/\\][^/\\]+|unhead)[/\\]/;
function toImports(specifiers) {
return specifiers.map((specifier) => {
const imported = specifier.imported;
const isNamedImport = imported && imported.name !== specifier.local.name;
return isNamedImport ? `${imported.name} as ${specifier.local.name}` : specifier.local.name;
});
}
const UnheadVue = "@unhead/vue";
const UnheadVueRE = /@unhead\/vue/;
const UnheadImportsPlugin = (options) => createUnplugin(() => {
return {
name: "nuxt:head:unhead-imports",
enforce: "post",
transformInclude(id) {
id = normalize(id);
return (isJS(id) || isVue(id, { type: ["script"] })) && !id.startsWith("virtual:") && !id.startsWith(normalize(distDir)) && !UNHEAD_LIB_RE.test(id);
},
transform: {
filter: {
code: { include: UnheadVueRE }
},
handler(code, id) {
const s = new MagicString(code);
const importsToAdd = [];
parseAndWalk(code, id, function(node) {
if (node.type === "ImportDeclaration" && [UnheadVue, "#app/composables/head"].includes(String(node.source.value))) {
importsToAdd.push(...node.specifiers);
const { start, end } = node;
s.remove(start, end);
}
});
const importsFromUnhead = importsToAdd.filter((specifier) => unheadVueComposablesImports[UnheadVue].includes(specifier.imported?.name));
const importsFromHead = importsToAdd.filter((specifier) => !unheadVueComposablesImports[UnheadVue].includes(specifier.imported?.name));
if (importsFromUnhead.length) {
if (!normalize(id).includes("node_modules")) {
logger.warn(`You are importing from \`${UnheadVue}\` in \`./${relative(normalize(options.rootDir), normalize(id))}\`. Please import from \`#imports\` instead for full type safety.`);
}
s.prepend(`${genImport("#app/composables/head", toImports(importsFromUnhead))}
`);
}
if (importsFromHead.length) {
s.prepend(`${genImport(UnheadVue, toImports(importsFromHead))}
`);
}
if (s.hasChanged()) {
return {
code: s.toString(),
map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
};
}
}
}
};
});
const components = ["NoScript", "Link", "Base", "Title", "Meta", "Style", "Head", "Html", "Body"];
const metaModule = defineNuxtModule({
meta: {
name: "nuxt:meta",
configKey: "unhead"
},
setup(options, nuxt) {
const runtimeDir = resolve(distDir, "head/runtime");
nuxt.options.build.transpile.push("@unhead/vue");
const isNuxtV4 = nuxt.options._majorVersion === 4 || nuxt.options.future?.compatibilityVersion === 4;
const componentsPath = resolve(runtimeDir, "components");
for (const componentName of components) {
addComponent({
name: componentName,
filePath: componentsPath,
export: componentName,
// built-in that we do not expect the user to override
priority: 10,
// kebab case version of these tags is not valid
kebabName: componentName
});
}
if (!nuxt.options.dev) {
nuxt.options.optimization.treeShake.composables.client["@unhead/vue"] = [
"useServerHead",
"useServerSeoMeta",
"useServerHeadSafe"
];
}
nuxt.options.alias["#unhead/composables"] = resolve(runtimeDir, "composables", isNuxtV4 ? "v4" : "v3");
addBuildPlugin(UnheadImportsPlugin({
sourcemap: !!nuxt.options.sourcemap.server,
rootDir: nuxt.options.rootDir
}));
const importPaths = nuxt.options.modulesDir.map((d) => directoryToURL(d));
const unheadPlugins = resolveModulePath("@unhead/vue/plugins", { try: true, from: importPaths }) || "@unhead/vue/plugins";
if (nuxt.options.experimental.polyfillVueUseHead) {
nuxt.options.alias["@vueuse/head"] = resolveModulePath("@unhead/vue", { try: true, from: importPaths }) || "@unhead/vue";
addPlugin({ src: resolve(runtimeDir, "plugins/vueuse-head-polyfill") });
}
addTemplate({
filename: "unhead-options.mjs",
getContents() {
if (isNuxtV4 && !options.legacy) {
return `
export default {
disableDefaults: true,
}`;
}
const disableCapoSorting = !nuxt.options.experimental.headNext;
return `import { DeprecationsPlugin, PromisesPlugin, TemplateParamsPlugin, AliasSortingPlugin } from ${JSON.stringify(unheadPlugins)};
export default {
disableDefaults: true,
disableCapoSorting: ${Boolean(disableCapoSorting)},
plugins: [DeprecationsPlugin, PromisesPlugin, TemplateParamsPlugin, AliasSortingPlugin],
}`;
}
});
addTemplate({
filename: "unhead.config.mjs",
getContents() {
return [
`export const renderSSRHeadOptions = ${JSON.stringify(options.renderSSRHeadOptions || {})}`
].join("\n");
}
});
nuxt.hooks.hook("nitro:config", (config) => {
config.virtual["#internal/unhead-options.mjs"] = () => nuxt.vfs["#build/unhead-options.mjs"];
config.virtual["#internal/unhead.config.mjs"] = () => nuxt.vfs["#build/unhead.config.mjs"];
});
addPlugin({ src: resolve(runtimeDir, "plugins/unhead") });
}
});
const commonPresets = [
// vue-demi (mocked)
defineUnimportPreset({
from: "vue-demi",
imports: [
"isVue2",
"isVue3"
]
})
];
const granularAppPresets = [
{
from: "#app/components/nuxt-link",
imports: ["defineNuxtLink"]
},
{
imports: ["useNuxtApp", "tryUseNuxtApp", "defineNuxtPlugin", "definePayloadPlugin", "useRuntimeConfig", "defineAppConfig"],
from: "#app/nuxt"
},
{
imports: ["useAppConfig", "updateAppConfig"],
from: "#app/config"
},
{
imports: ["defineNuxtComponent"],
from: "#app/composables/component"
},
{
imports: ["useAsyncData", "useLazyAsyncData", "useNuxtData", "refreshNuxtData", "clearNuxtData"],
from: "#app/composables/asyncData"
},
{
imports: ["useHydration"],
from: "#app/composables/hydrate"
},
{
imports: ["callOnce"],
from: "#app/composables/once"
},
{
imports: ["useState", "clearNuxtState"],
from: "#app/composables/state"
},
{
imports: ["clearError", "createError", "isNuxtError", "showError", "useError"],
from: "#app/composables/error"
},
{
imports: ["useFetch", "useLazyFetch"],
from: "#app/composables/fetch"
},
{
imports: ["useCookie", "refreshCookie"],
from: "#app/composables/cookie"
},
{
imports: ["onPrehydrate", "prerenderRoutes", "useRequestHeader", "useRequestHeaders", "useResponseHeader", "useRequestEvent", "useRequestFetch", "setResponseStatus"],
from: "#app/composables/ssr"
},
{
imports: ["onNuxtReady"],
from: "#app/composables/ready"
},
{
imports: ["preloadComponents", "prefetchComponents", "preloadRouteComponents"],
from: "#app/composables/preload"
},
{
imports: ["abortNavigation", "addRouteMiddleware", "defineNuxtRouteMiddleware", "setPageLayout", "navigateTo", "useRoute", "useRouter"],
from: "#app/composables/router"
},
{
imports: ["isPrerendered", "loadPayload", "preloadPayload", "definePayloadReducer", "definePayloadReviver"],
from: "#app/composables/payload"
},
{
imports: ["useLoadingIndicator"],
from: "#app/composables/loading-indicator"
},
{
imports: ["getAppManifest", "getRouteRules"],
from: "#app/composables/manifest"
},
{
imports: ["reloadNuxtApp"],
from: "#app/composables/chunk"
},
{
imports: ["useRequestURL"],
from: "#app/composables/url"
},
{
imports: ["usePreviewMode"],
from: "#app/composables/preview"
},
{
imports: ["useRouteAnnouncer"],
from: "#app/composables/route-announcer"
},
{
imports: ["useRuntimeHook"],
from: "#app/composables/runtime-hook"
},
{
imports: ["useHead", "useHeadSafe", "useServerHeadSafe", "useServerHead", "useSeoMeta", "useServerSeoMeta", "injectHead"],
from: "#app/composables/head"
}
];
const scriptsStubsPreset = {
imports: [
"useScriptTriggerConsent",
"useScriptEventPage",
"useScriptTriggerElement",
"useScript",
"useScriptGoogleAnalytics",
"useScriptPlausibleAnalytics",
"useScriptCrisp",
"useScriptClarity",
"useScriptCloudflareWebAnalytics",
"useScriptFathomAnalytics",
"useScriptMatomoAnalytics",
"useScriptGoogleTagManager",
"useScriptGoogleAdsense",
"useScriptSegment",
"useScriptMetaPixel",
"useScriptXPixel",
"useScriptIntercom",
"useScriptHotjar",
"useScriptStripe",
"useScriptLemonSqueezy",
"useScriptVimeoPlayer",
"useScriptYouTubePlayer",
"useScriptGoogleMaps",
"useScriptNpm",
"useScriptUmamiAnalytics",
"useScriptSnapchatPixel",
"useScriptRybbitAnalytics"
],
priority: -1,
from: "#app/composables/script-stubs"
};
const routerPreset = defineUnimportPreset({
imports: ["onBeforeRouteLeave", "onBeforeRouteUpdate"],
from: "#app/composables/router"
});
const vuePreset = defineUnimportPreset({
from: "vue",
imports: [
// <script setup>
"withCtx",
"withDirectives",
"withKeys",
"withMemo",
"withModifiers",
"withScopeId",
// Lifecycle
"onActivated",
"onBeforeMount",
"onBeforeUnmount",
"onBeforeUpdate",
"onDeactivated",
"onErrorCaptured",
"onMounted",
"onRenderTracked",
"onRenderTriggered",
"onServerPrefetch",
"onUnmounted",
"onUpdated",
// Reactivity
"computed",
"customRef",
"isProxy",
"isReactive",
"isReadonly",
"isRef",
"markRaw",
"proxyRefs",
"reactive",
"readonly",
"ref",
"shallowReactive",
"shallowReadonly",
"shallowRef",
"toRaw",
"toRef",
"toRefs",
"triggerRef",
"unref",
"watch",
"watchEffect",
"watchPostEffect",
"watchSyncEffect",
"onWatcherCleanup",
"isShallow",
// effect
"effect",
"effectScope",
"getCurrentScope",
"onScopeDispose",
// Component
"defineComponent",
"defineAsyncComponent",
"resolveComponent",
"getCurrentInstance",
"h",
"inject",
"hasInjectionContext",
"nextTick",
"provide",
"mergeModels",
"toValue",
"useModel",
"useAttrs",
"useCssModule",
"useCssVars",
"useSlots",
"useTransitionState",
"useId",
"useTemplateRef",
"useShadowRoot",
"useCssVars"
]
});
const vueTypesPreset = defineUnimportPreset({
from: "vue",
type: true,
imports: [
"Component",
"ComponentPublicInstance",
"ComputedRef",
"DirectiveBinding",
"ExtractDefaultPropTypes",
"ExtractPropTypes",
"ExtractPublicPropTypes",
"InjectionKey",
"PropType",
"Ref",
"MaybeRef",
"MaybeRefOrGetter",
"VNode",
"WritableComputedRef"
]
});
const appCompatPresets = [
{
imports: ["requestIdleCallback", "cancelIdleCallback"],
from: "#app/compat/idle-callback"
},
{
imports: ["setInterval"],
from: "#app/compat/interval"
}
];
const lazyHydrationMacroPreset = [
{
imports: ["defineLazyHydrationComponent"],
from: "#app/composables/lazy-hydration"
}
];
const defaultPresets = [
...commonPresets,
...granularAppPresets,
routerPreset,
vuePreset,
vueTypesPreset
];
const createImportMagicComments = (options) => {
const { chunkName, prefetch, preload } = options;
return [
`webpackChunkName: "${chunkName}"`,
prefetch === true || typeof prefetch === "number" ? `webpackPrefetch: ${prefetch}` : false,
preload === true || typeof preload === "number" ? `webpackPreload: ${preload}` : false
].filter(Boolean).join(", ");
};
const emptyComponentsPlugin = `
import { defineNuxtPlugin } from '#app/nuxt'
export default defineNuxtPlugin({
name: 'nuxt:global-components',
})
`;
const componentsPluginTemplate = {
filename: "components.plugin.mjs",
getContents({ app }) {
const lazyGlobalComponents = /* @__PURE__ */ new Set();
const syncGlobalComponents = /* @__PURE__ */ new Set();
for (const component of app.components) {
if (component.global === "sync") {
syncGlobalComponents.add(component.pascalName);
} else if (component.global) {
lazyGlobalComponents.add(component.pascalName);
}
}
if (!lazyGlobalComponents.size && !syncGlobalComponents.size) {
return emptyComponentsPlugin;
}
const lazyComponents = [...lazyGlobalComponents];
const syncComponents = [...syncGlobalComponents];
return `import { defineNuxtPlugin } from '#app/nuxt'
import { ${[...lazyComponents.map((c) => "Lazy" + c), ...syncComponents].join(", ")} } from '#components'
const lazyGlobalComponents = [
${lazyComponents.map((c) => `["${c}", Lazy${c}]`).join(",\n")},
${syncComponents.map((c) => `["${c}", ${c}]`).join(",\n")}
]
export default defineNuxtPlugin({
name: 'nuxt:global-components',
setup (nuxtApp) {
for (const [name, component] of lazyGlobalComponents) {
nuxtApp.vueApp.component(name, component)
nuxtApp.vueApp.component('Lazy' + name, component)
}
}
})
`;
}
};
const componentNamesTemplate = {
filename: "component-names.mjs",
getContents({ app }) {
return `export const componentNames = ${JSON.stringify(app.components.filter((c) => !c.island).map((c) => c.pascalName))}`;
}
};
const componentsIslandsTemplate = {
// components.islands.mjs'
getContents({ app, nuxt }) {
if (!nuxt.options.experimental.componentIslands) {
return "export const islandComponents = {}";
}
const components = app.components;
const pages = app.pages;
const islands = components.filter(
(component) => component.island || // .server components without a corresponding .client component will need to be rendered as an island
component.mode === "server" && !components.some((c) => c.pascalName === component.pascalName && c.mode === "client")
);
const pageExports = pages?.filter((p) => p.mode === "server" && p.file && p.name).map((p) => {
return `"page_${p.name}": defineAsyncComponent(${genDynamicImport(p.file)}.then(c => c.default || c))`;
}) || [];
return [
"import { defineAsyncComponent } from 'vue'",
"export const islandComponents = import.meta.client ? {} : {",
islands.map(
(c) => {
const exp = c.export === "default" ? "c.default || c" : `c['${c.export}']`;
const comment = createImportMagicComments(c);
return ` "${c.pascalName}": defineAsyncComponent(${genDynamicImport(c.filePath, { comment })}.then(c => ${exp}))`;
}
).concat(pageExports).join(",\n"),
"}"
].join("\n");
}
};
const NON_VUE_RE = /\b\.(?!vue)\w+$/g;
function resolveComponentTypes(nuxt, app) {
const buildDir = nuxt.options.buildDir;
const serverPlaceholderPath = resolve(distDir, "app/components/server-placeholder");
const componentTypes = app.components.filter((c) => !c.island).map((c) => {
const type = `typeof ${genDynamicImport(isAbsolute(c.filePath) ? relative(buildDir, c.filePath).replace(NON_VUE_RE, "") : c.filePath.replace(NON_VUE_RE, ""), { wrapper: false })}['${c.export}']`;
const isServerOnly = c.mode === "server" && c.filePath !== serverPlaceholderPath && !app.components.some((other) => other.pascalName === c.pascalName && other.mode === "client");
return [
c.pascalName,
isServerOnly ? `IslandComponent<${type}>` : type
];
});
return componentTypes;
}
const islandType = "type IslandComponent<T extends DefineComponent> = T & DefineComponent<{}, {refresh: () => Promise<void>}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, SlotsType<{ fallback: { error: unknown } }>>";
const hydrationTypes = `
type HydrationStrategies = {
hydrateOnVisible?: IntersectionObserverInit | true
hydrateOnIdle?: number | true
hydrateOnInteraction?: keyof HTMLElementEventMap | Array<keyof HTMLElementEventMap> | true
hydrateOnMediaQuery?: string
hydrateAfter?: number
hydrateWhen?: boolean
hydrateNever?: true
}
type LazyComponent<T> = (T & DefineComponent<HydrationStrategies, {}, {}, {}, {}, {}, {}, { hydrated: () => void }>)
`;
const componentsDeclarationTemplate = {
filename: "components.d.ts",
write: true,
getContents: ({ app, nuxt }) => {
const componentTypes = resolveComponentTypes(nuxt, app);
return `
import type { DefineComponent, SlotsType } from 'vue'
${nuxt.options.experimental.componentIslands ? islandType : ""}
${hydrationTypes}
${componentTypes.map(([pascalName, type]) => `export const ${pascalName}: ${type}`).join("\n")}
${componentTypes.map(([pascalName, type]) => `export const Lazy${pascalName}: LazyComponent<${type}>`).join("\n")}
export const componentNames: string[]
`;
}
};
const componentsTypeTemplate = {
filename: "types/components.d.ts",
getContents: ({ app, nuxt }) => {
const componentTypes = resolveComponentTypes(nuxt, app);
return `
import type { DefineComponent, SlotsType } from 'vue'
${nuxt.options.experimental.componentIslands ? islandType : ""}
${hydrationTypes}
interface _GlobalComponents {
${componentTypes.map(([pascalName, type]) => ` '${pascalName}': ${type}`).join("\n")}
${componentTypes.map(([pascalName, type]) => ` 'Lazy${pascalName}': LazyComponent<${type}>`).join("\n")}
}
declare module 'vue' {
export interface GlobalComponents extends _GlobalComponents { }
}
export {}
`;
}
};
const componentsMetadataTemplate = {
filename: "components.json",
write: true,
getContents: ({ app }) => JSON.stringify(app.components, null, 2)
};
const ISLAND_RE = /\.island(?:\.global)?$/;
const GLOBAL_RE = /\.global(?:\.island)?$/;
const COMPONENT_MODE_RE = /(?<=\.)(client|server)(?:\.global|\.island)*$/;
const MODE_REPLACEMENT_RE = /(?:\.(?:client|server))?(?:\.global|\.island)*$/;
async function scanComponents(dirs, srcDir) {
const components = [];
const filePaths = /* @__PURE__ */ new Set();
const scannedPaths = [];
for (const dir of dirs) {
if (dir.enabled === false) {
continue;
}
const resolvedNames = /* @__PURE__ */ new Map();
const files = (await glob(dir.pattern, { cwd: dir.path, ignore: dir.ignore })).sort();
if (files.length) {
const siblings = new Set(await readdir(dirname(dir.path)).catch(() => []));
const directory = basename(dir.path);
if (!siblings.has(directory)) {
const directoryLowerCase = directory.toLowerCase();
for (const sibling of siblings) {
if (sibling.toLowerCase() === directoryLowerCase) {
const nuxt = useNuxt();
const original = resolveToAlias(dir.path, nuxt);
const corrected = resolveToAlias(join(dirname(dir.path), sibling), nuxt);
logger.warn(`Components not scanned from \`${corrected}\`. Did you mean to name the directory \`${original}\` instead?`);
break;
}
}
}
}
for (const _file of files) {
const filePath = join(dir.path, _file);
if (scannedPaths.find((d) => filePath.startsWith(withTrailingSlash$1(d))) || isIgnored(filePath)) {
continue;
}
if (filePaths.has(filePath)) {
continue;
}
filePaths.add(filePath);
const prefixParts = [].concat(
dir.prefix ? splitByCase(dir.prefix) : [],
dir.pathPrefix !== false ? splitByCase(relative(dir.path, dirname(filePath))) : []
);
let fileName = basename(filePath, extname(filePath));
const island = ISLAND_RE.test(fileName) || dir.island;
const global = GLOBAL_RE.test(fileName) || dir.global;
const mode = island ? "server" : fileName.match(COMPONENT_MODE_RE)?.[1] || "all";
fileName = fileName.replace(MODE_REPLACEMENT_RE, "");
if (fileName.toLowerCase() === "index") {
fileName = dir.pathPrefix === false ? basename(dirname(filePath)) : "";
}
const suffix = mode !== "all" ? `-${mode}` : "";
const componentNameSegments = resolveComponentNameSegments(fileName.replace(QUOTE_RE, ""), prefixParts);
const pascalName = pascalCase(componentNameSegments);
if (LAZY_COMPONENT_NAME_REGEX.test(pascalName)) {
logger.warn(`The component \`${pascalName}\` (in \`${filePath}\`) is using the reserved "Lazy" prefix used for dynamic imports, which may cause it to break at runtime.`);
}
if (resolvedNames.has(pascalName + suffix) || resolvedNames.has(pascalName)) {
warnAboutDuplicateComponent(pascalName, filePath, resolvedNames.get(pascalName) || resolvedNames.get(pascalName + suffix));
continue;
}
resolvedNames.set(pascalName + suffix, filePath);
const kebabName = kebabCase(componentNameSegments);
const shortPath = relative(srcDir, filePath);
const chunkName = "components/" + kebabName + suffix;
let component = {
// inheritable from directory configuration
mode,
global,
island,
prefetch: Boolean(dir.prefetch),
preload: Boolean(dir.preload),
// specific to the file
filePath,
pascalName,
kebabName,
chunkName,
shortPath,
export: "default",
// by default, give priority to scanned components
priority: dir.priority ?? 1,
// @ts-expect-error untyped property
_scanned: true
};
if (typeof dir.extendComponent === "function") {
component = await dir.extendComponent(component) || component;
}
if (!pascalName) {
logger.warn(`Component did not resolve to a file name in \`${resolveToAlias(filePath)}\`.`);
continue;
}
const validModes = /* @__PURE__ */ new Set(["all", component.mode]);
const existingComponent = components.find((c) => c.pascalName === component.pascalName && validModes.has(c.mode));
if (existingComponent) {
const existingPriority = existingComponent.priority ?? 0;
const newPriority = component.priority ?? 0;
if (newPriority > existingPriority) {
components.splice(components.indexOf(existingComponent), 1, component);
}
if (newPriority > 0 && newPriority === existingPriority) {
warnAboutDuplicateComponent(pascalName, filePath, existingComponent.filePath);
}
continue;
}
components.push(component);
}
scannedPaths.push(dir.path);
}
return components;
}
function warnAboutDuplicateComponent(componentName, filePath, duplicatePath) {
logger.warn(
`Two component files resolving to the same name \`${componentName}\`:
- ${filePath}
- ${duplicatePath}`
);
}
const LAZY_COMPONENT_NAME_REGEX = /^Lazy(?=[A-Z])/;
const REPLACE_COMPONENT_TO_DIRECT_IMPORT_RE = /(?<=[\s(=;])_?resolveComponent\s*\(\s*(?<quote>["'`])(?<lazy>lazy-|Lazy(?=[A-Z]))?(?<modifier>Idle|Visible|idle-|visible-|Interaction|interaction-|MediaQuery|media-query-|If|if-|Never|never-|Time|time-)?(?<name>[^'"`]*)\k<quote>[^)]*\)/g;
const LoaderPlugin = (options) => createUnplugin(() => {
const exclude = options.transform?.exclude || [];
const include = options.transform?.include || [];
const nuxt = tryUseNuxt();
return {
name: "nuxt:components-loader",
enforce: "post",
transformInclude(id) {
if (exclude.some((pattern) => pattern.test(id))) {
return false;
}
if (include.some((pattern) => pattern.test(id))) {
return true;
}
return isVue(id, { type: ["template", "script"] }) || !!id.match(SX_RE);
},
transform(code, id) {
const components = options.getComponents();
let num = 0;
const imports = /* @__PURE__ */ new Set();
const map = /* @__PURE__ */ new Map();
const s = new MagicString(code);
s.replace(REPLACE_COMPONENT_TO_DIRECT_IMPORT_RE, (full, ...args) => {
const { lazy, modifier, name } = args.pop();
const normalComponent = findComponent(components, name, options.mode);
const modifierComponent = !normalComponent && modifier ? findComponent(components, modifier + name, options.mode) : null;
const component = normalComponent || modifierComponent;
if (component) {
const internalInstall = component._internal_install;
if (internalInstall && nuxt?.options.test === false) {
if (!nuxt.options.dev) {
throw new Error(`[nuxt] \`${resolveToAlias(id, nuxt)}\` is using \`${component.pascalName}\` which requires \`${internalInstall}\``);
}
installNuxtModule(internalInstall);
}
let identifier = map.get(component) || `__nuxt_component_${num++}`;
map.set(component, identifier);
const isServerOnly = !component._raw && component.mode === "server" && !components.some((c) => c.pascalName === component.pascalName && c.mode === "client");
if (isServerOnly) {
imports.add(genImport(options.serverComponentRuntime, [{ name: "createServerComponent" }]));
imports.add(`const ${identifier} = createServerComponent(${JSON.stringify(component.pascalName)})`);
if (!options.experimentalComponentIslands) {
logger.warn(`Standalone server components (\`${name}\`) are not yet supported without enabling \`experimental.componentIslands\`.`);
}
return identifier;
}
const isClientOnly = !component._raw && component.mode === "client";
if (isClientOnly) {
imports.add(genImport("#app/components/client-only", [{ name: "createClientOnly" }]));
identifier += "_client";
}
if (lazy) {
const dynamicImport = `${genDynamicImport(component.filePath, { interopDefault: false })}.then(c => c.${component.export ?? "default"} || c)`;
if (modifier && normalComponent) {
const relativePath = relative(options.srcDir, component.filePath);
switch (modifier) {
case "Visible":
case "visible-":
imports.add(genImport(options.clientDelayedComponentRuntime, [{ name: "createLazyVisibleComponent" }]));
identifier += "_lazy_visible";
imports.add(`const ${identifier} = createLazyVisibleComponent(${JSON.stringify(relativePath)}, ${dynamicImport})`);
break;
case "Interaction":
case "interaction-":
imports.add(genImport(options.clientDelayedComponentRuntime, [{ name: "createLazyInteractionComponent" }]));
identifier += "_lazy_event";
imports.add(`const ${identifier} = createLazyInteractionComponent(${JSON.stringify(relativePath)}, ${dynamicImport})`);
break;
case "Idle":
case "idle-":
imports.add(genImport(options.clientDelayedComponentRuntime, [{ name: "createLazyIdleComponent" }]));
identifier += "_lazy_idle";
imports.add(`const ${identifier} = createLazyIdleComponent(${JSON.stringify(relativePath)}, ${dynamicImport})`);
break;
case "MediaQuery":
case "media-query-":
imports.add(genImport(options.clientDelayedComponentRuntime, [{ name: "createLazyMediaQueryComponent" }]));
identifier += "_lazy_media";
imports.add(`const ${identifier} = createLazyMediaQueryComponent(${JSON.stringify(relativePath)}, ${dynamicImport})`);
break;
case "If":
case "if-":
imports.add(genImport(options.clientDelayedComponentRuntime, [{ name: "createLazyIfComponent" }]));
identifier += "_lazy_if";
imports.add(`const ${identifier} = createLazyIfComponent(${JSON.stringify(relativePath)}, ${dynamicImport})`);
break;
case "Never":
case "never-":
imports.add(genImport(options.clientDelayedComponentRuntime, [{ name: "createLazyNeverComponent" }]));
identifier += "_lazy_never";
imports.add(`const ${identifier} = createLazyNeverComponent(${JSON.stringify(relativePath)}, ${dynamicImport})`);
break;
case "Time":
case "time-":
imports.add(genImport(options.clientDelayedComponentRuntime, [{ name: "createLazyTimeComponent" }]));
identifier += "_lazy_time";
imports.add(`const ${identifier} = createLazyTimeComponent(${JSON.stringify(relativePath)}, ${dynamicImport})`);
break;
}
} else {
imports.add(genImport("vue", [{ name: "defineAsyncComponent", as: "__defineAsyncComponent" }]));
identifier += "_lazy";
imports.add(`const ${identifier} = __defineAsyncComponent(${dynamicImport}${isClientOnly ? ".then(c => createClientOnly(c))" : ""})`);
}
} else {
imports.add(genImport(component.filePath, [{ name: component._raw ? "default" : component.export, as: identifier }]));
if (isClientOnly) {
imports.add(`const ${identifier}_wrapped = createClientOnly(${identifier})`);
identifier += "_wrapped";
}
}
return identifier;
}
return full;
});
if (imports.size) {
s.prepend([...imports, ""].join("\n"));
}
if (s.hasChanged()) {
return {
code: s.toString(),
map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
};
}
}
};
});
function findComponent(components, name, mode) {
const id = pascalCase(name).replace(QUOTE_RE, "");
const validModes = /* @__PURE__ */ new Set(["all", mode, void 0]);
const component = components.find((component2) => id === component2.pascalName && validModes.has(component2.mode));
if (component) {
return component;
}
const otherModeComponent = components.find((component2) => id === component2.pascalName);
if (mode === "server" && otherModeComponent) {
return components.find((c) => c.pascalName === "ServerPlaceholder");
}
return otherModeComponent;
}
const SCRIPT_RE$2 = /<script[^>]*>/i;
const SCRIPT_RE_GLOBAL = /<script[^>]*>/gi;
const HAS_SLOT_OR_CLIENT_RE = /<slot[^>]*>|nuxt-client/;
const TEMPLATE_RE$1 = /<template>[\s\S]*<\/template>/;
const NUXTCLIENT_ATTR_RE = /\s:?nuxt-client(?:="[^"]*")?/g;
const IMPORT_CODE = "\nimport { mergeProps as __mergeProps } from 'vue'\nimport { vforToArray as __vforToArray } from '#app/components/utils'\nimport NuxtTeleportIslandComponent from '#app/components/nuxt-teleport-island-component'\nimport NuxtTeleportSsrSlot from '#app/components/nuxt-teleport-island-slot'";
const EXTRACTED_ATTRS_RE = /v-(?:if|else-if|else)(?:="[^"]*")?/g;
const KEY_RE = /:?key="[^"]"/g;
function wrapWithVForDiv(code, vfor) {
return `<div v-for="${vfor}" style="display: contents;">${code}</div>`;
}
const IslandsTransformPlugin = (options) => createUnplugin((_options, meta) => {
const isVite = meta.framework === "vite";
return {
name: "nuxt:server-only-component-transform",
enforce: "pre",
transformInclude(id) {
if (!isVue(id)) {
return false;
}
if (isVite && options.selectiveClient === "deep") {
return true;
}
const components = options.getComponents();
const islands = components.filter(
(component) => component.island || component.mode === "server" && !components.some((c) => c.pascalName === component.pascalName && c.mode === "client")
);
const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href));
return islands.some((c) => c.filePath === pathname);
},
transform: {
filter: {
code: {
include: [HAS_SLOT_OR_CLIENT_RE]
}
},
async handler(code, id) {
const template = code.match(TEMPLATE_RE$1);
if (!template) {
return;
}
const startingIndex = template.index || 0;
const s = new MagicString(code);
if (!SCRIPT_RE$2.test(code)) {
s.prepend("<script setup>" + IMPORT_CODE + "<\/script>");
} else {
s.replace(SCRIPT_RE_GLOBAL, (full) => {
return full + IMPORT_CODE;
});
}
let hasNuxtClient = false;
const ast = parse(template[0]);
await walk$1(ast, (node) => {
if (node.type !== ELEMENT_NODE) {
return;
}
if (node.name === "slot") {
const { attributes: attributes2, children, loc: loc2 } = node;
const slotName = attributes2.name ?? "default";
if (attributes2.name) {
delete attributes2.name;
}
if (attributes2["v-bind"]) {
attributes2._bind = extractAttributes(attributes2, ["v-bind"])["v-bind"];
}
const teleportAttributes = extractAttributes(attributes2, ["v-if", "v-else-if", "v-else"]);
const bindings = getPropsToString(attributes2);
s.appendLeft(startingIndex + loc2[0].start, `<NuxtTeleportSsrSlot${attributeToString(teleportAttributes)} name="${slotName}" :props="${bindings}">`);
if (children.length) {
const attrString = attributeToString(attributes2);
const slice = code.slice(startingIndex + loc2[0].end, startingIndex + loc2[1].start).replaceAll(KEY_RE, "");
s.overwrite(startingIndex + loc2[0].start, startingIndex + loc2[1].end, `<slot${attrString.replaceAll(EXTRACTED_ATTRS_RE, "")}/><template #fallback>${attributes2["v-for"] ? wrapWithVForDiv(slice, attributes2["v-for"]) : slice}</template>`);
} else {
s.overwrite(startingIndex + loc2[0].start, startingIndex + loc2[0].end, code.slice(startingIndex + loc2[0].start, startingIndex + loc2[0].end).replaceAll(EXTRACTED_ATTRS_RE, ""));
}
s.appendRight(startingIndex + loc2[1].end, "</NuxtTeleportSsrSlot>");
return;
}
if (!("nuxt-client" in node.attributes) && !(":nuxt-client" in node.attributes)) {
return;
}
hasNuxtClient = true;
if (!isVite || !options.selectiveClient) {
return;
}
const { loc, attributes } = node;
const attributeValue = attributes[":nuxt-client"] || attributes["nuxt-client"] || "true";
const wrapperAttributes = extractAttributes(attributes, ["v-if", "v-else-if", "v-else"]);
let startTag = code.slice(startingIndex + loc[0].start, startingIndex + loc[0].end).replace(NUXTCLIENT_ATTR_RE, "");
if (wrapperAttributes) {
startTag = startTag.replaceAll(EXTRACTED_ATTRS_RE, "");
}
s.appendLeft(startingIndex + loc[0].start, `<NuxtTeleportIslandComponent${attributeToString(wrapperAttributes)} :nuxt-client="${attributeValue}">`);
s.overwrite(startingIndex + loc[0].start, startingIndex + loc[0].end, startTag);
s.appendRight(startingIndex + loc[1].end, "</NuxtTeleportIslandComponent>");
});
if (hasNuxtClient) {
if (!options.selectiveClient) {
console.warn(`The \`nuxt-client\` attribute and client components within islands are only supported when \`experimental.componentIslands.selectiveClient\` is enabled. file: ${id}`);
} else if (!isVite) {
console.warn(`The \`nuxt-client\` attribute and client components within islands are only supported with Vite. file: ${id}`);
}
}
if (s.hasChanged()) {
return {
code: s.toString(),
map: s.generateMap({ source: id, includeContent: true })
};
}
}
}
};
});
function extractAttributes(attributes, names) {
const extracted = {};
for (const name of names) {
if (name in attributes) {
extracted[name] = attributes[name];
delete attributes[name];
}
}
return extracted;
}
function attributeToString(attributes) {
return Object.entries(attributes).map(([name, value]) => value ? ` ${name}="${value}"` : ` ${name}`).join("");
}
function isBinding(attr) {
return attr.startsWith(":");
}
function getPropsToString(bindings) {
const vfor = bindings["v-for"]?.split(" in ").map((v) => v.trim());
if (Object.keys(bindings).length === 0) {
return "undefined";
}
const content = Object.entries(bindings).filter((b) => b[0] && (b[0] !== "_bind" && b[0] !== "v-for")).map(([name, value]) => isBinding(name) ? `[\`${name.slice(1)}\`]: ${value}` : `[\`${name}\`]: \`${value}\``).join(",");
const data = bindings._bind ? `__mergeProps(${bindings._bind}, { ${content} })` : `{ ${content} }`;
if (!vfor) {
return `[${data}]`;
} else {
return `__vforToArray(${vfor[1]}).map(${vfor[0]} => (${data}))`;
}
}
const COMPONENT_CHUNK_ID = `#build/component-chunk`;
const COMPONENT_CHUNK_RESOLVED_ID = "\0nuxt-component-chunk";
const ComponentsChunkPlugin = (options) => {
const chunkIds = /* @__PURE__ */ new Map();
const paths = /* @__PURE__ */ new Map();
return [
{
name: "nuxt:components-chunk:client",
apply: () => !options.dev,
applyToEnvironment: (environment) => environment.name === "client",
buildStart() {
for (const c of options.getComponents()) {
if (!c.filePath || c.mode === "server") {
continue;
}
chunkIds.set(c.pascalName, this.emitFile({
type: "chunk",
name: `${c.pascalName}-chunk.mjs`,
id: c.filePath,
preserveSignature: "strict"
}));
}
},
generateBundle(_, bundle) {
const ids = /* @__PURE__ */ new Set();
for (const [name, id] of chunkIds.entries()) {
const filename = this.getFileName(id);
ids.add(filename);
paths.set(name, filename);
}
for (const chunk of Object.values(bundle)) {
if (chunk.type === "chunk") {
if (ids.has(chunk.fileName)) {
chunk.isEntry = false;
}
}
}
}
},
{
name: "nuxt:components-chunk:server",
resolveId: {
order: "pre",
handler(id) {
if (id === COMPONENT_CHUNK_ID) {
return COMPONENT_CHUNK_RESOLVED_ID;
}
}
},
load(id) {
if (id === COMPONENT_CHUNK_RESOLVED_ID) {
if (options.dev) {
const filePaths = {};
for (const c of options.getComponents()) {
if (!c.filePath || c.mode === "server") {
continue;
}
filePaths[c.pascalName] = `@fs/${c.filePath}`;
}
return `export default ${genObjectFromRawEntries(Object.entries(filePaths).map(([name, path]) => [name, genString(path)]))}`;
}
return `export default ${genObjectFromRawEntries(Array.from(paths.entries()).map(([name, id2]) => [name, genString("/" + id2)]))}`;
}
}
}
];
};
const COMPONENT_QUERY_RE = /[?&]nuxt_component=/;
function TransformPlugin$1(nuxt, options) {
const componentUnimport = createUnimport({
imports: [
{
name: "componentNames",
from: "#build/component-names"
}
],
virtualImports: ["#components"],
injectAtEnd: true
});
function getComponentsImports() {
const components = options.getComponents(options.mode);
const clientOrServerModes = /* @__PURE__ */ new Set(["client", "server"]);
return components.flatMap((c) => {
const withMode = (mode2) => mode2 ? `${c.filePath}${c.filePath.includes("?") ? "&" : "?"}nuxt_component=${mode2}&nuxt_component_name=${c.pascalName}&nuxt_component_export=${c.export || "default"}` : c.filePath;
const mode = !c._raw && c.mode && clientOrServerModes.has(c.mode) ? c.mode : void 0;
return [
{
as: c.pascalName,
from: withMode(mode),
name: c.export || "default"
},
{
as: "Lazy" + c.pascalName,
from: withMode([mode, "async"].filter(Boolean).join(",")),
name: c.export || "default"
}
];
});
}
return createUnplugin(() => ({
name: "nuxt:components:imports",
enforce: "post",
transformInclude(id) {
id = normalize(id);
return id.startsWith("virtual:") || id.startsWith("\0virtual:") || id.startsWith(nuxt.options.buildDir) || !isIgnored(id, void 0, nuxt);
},
async transform(code, id) {
if (COMPONENT_QUERY_RE.test(id)) {
const { search } = parseURL(id);
const query = parseQuery$1(search);
const mode = query.nuxt_component;
const bare = id.replace(/\?.*/, "");
const componentExport = query.nuxt_component_export || "default";
const exportWording = componentExport === "default" ? "export default" : `export const ${componentExport} =`;
if (mode === "async") {
return {
code: [
'import { defineAsyncComponent } from "vue"',
`${exportWording} defineAsyncComponent(() => import(${JSON.stringify(bare)}).then(r => r[${JSON.stringify(componentExport)}] || r.default || r))`
].join("\n"),
map: null
};
} else if (mode === "client") {
return {
code: [
genImport(bare, [{ name: componentExport, as: "__component" }]),
'import { createClientOnly } from "#app/components/client-only"',
`${exportWording} createClientOnly(__component)`
].join("\n"),
map: null
};
} else if (mode === "client,async") {
return {
code: [
'import { defineAsyncComponent } from "vue"',
'import { createClientOnly } from "#app/components/client-only"',
`${exportWording} defineAsyncComponent(() => import(${JSON.stringify(bare)}).then(r => createClientOnly(r[${JSON.stringify(componentExport)}] || r.default || r)))`
].join("\n"),
map: null
};
} else if (mode === "server" || mode === "server,async") {
const name = query.nuxt_component_name;
return {
code: [
`import { createServerComponent } from ${JSON.stringify(options.serverComponentRuntime)}`,
`${exportWording} createServerComponent(${JSON.stringify(name)})`
].join("\n"),
map: null
};
} else {
throw new Error(`Unknown component mode: ${mode}, this might be an internal bug of Nuxt.`);
}
}
if (!code.includes("#components")) {
return;
}
componentUnimport.modifyDynamicImports((imports) => {
imports.length = 0;
imports.push(...getComponentsImports());
return imports;
});
const result = await componentUnimport.injectImports(code, id, { autoImport: false, transformVirtualImports: true });
if (!result) {
return;
}
return {
code: result.code,
map: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client ? result.s.generateMap({ hires: true }) : void 0
};
}
}));
}
const SSR_RENDER_RE = /ssrRenderComponent/;
const PLACEHOLDER_EXACT_RE = /^(?:fallback|placeholder)$/;
const CLIENT_ONLY_NAME_RE = /^(?:_unref\()?(?:_component_)?(?:Lazy|lazy_)?(?:client_only|ClientOnly\)?)$/;
const TreeShakeTemplatePlugin = (options) => createUnplugin(() => {
const regexpMap = /* @__PURE__ */ new WeakMap();
return {
name: "nuxt:tree-shake-template",
enforce: "post",
transformInclude(id) {
const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href));
return pathname.endsWith(".vue");
},
transform(code, id) {
const components = options.getComponents();
if (!regexpMap.has(components)) {
const serverPlaceholderPath = resolve(distDir, "app/components/server-placeholder");
const clientOnlyComponents = components.filter((c) => c.mode === "client" && !components.some((other) => other.mode !== "client" && other.pascalName === c.pascalName && !other.filePath.startsWith(serverPlaceholderPath))).flatMap((c) => [c.pascalName, c.kebabName.replaceAll("-", "_")]).concat(["ClientOnly", "client_only"]);
regexpMap.set(components, [new RegExp(`(${clientOnlyComponents.join("|")})`), new RegExp(`^(${clientOnlyComponents.map((c) => `(?:(?:_unref\\()?(?:_component_)?(?:Lazy|lazy_)?${c}\\)?)`).join("|")})$`), clientOnlyComponents]);
}
const s = new MagicString(code);
const [COMPONENTS_RE, COMPONENTS_IDENTIFIERS_RE] = regexpMap.get(components);
if (!COMPONENTS_RE.test(code)) {
return;
}
const componentsToRemoveSet = /* @__PURE__ */ new Set();
const { program: ast } = parseAndWalk(code, id, (node) => {
if (!isSsrRender(node)) {
return;
}
const [componentCall, _, children] = node.arguments;
if (!componentCall) {
return;
}
if (componentCall.type === "Identifier" || componentCall.type === "MemberExpression" || componentCall.type === "CallExpression") {
const componentName = getComponentName(node);
if (!componentName || !COMPONENTS_IDENTIFIERS_RE.test(componentName) || children?.type !== "ObjectExpression") {
return;
}
const isClientOnlyComponent = CLIENT_ONLY_NAME_RE.test(componentName);
const slotsToRemove = isClientOnlyComponent ? children.properties.filter((prop) => prop.type === "Property" && prop.key.type === "Identifier" && !PLACEHOLDER_EXACT_RE.test(prop.key.name)) : children.properties;
for (const slot of slotsToRemove) {
s.remove(slot.start, slot.end + 1);
const removedCode = `({${code.slice(slot.start, slot.end + 1)}})`;
const currentState = s.toString();
parseAndWalk(removedCode, id, (node2) => {
if (!isSsrRender(node2)) {
return;
}
const name = getComponentName(node2);
if (!name) {
return;
}
const nameToRemove = isComponentNotCalledInSetup(currentState, id, name);
if (nameToRemove) {
componentsToRemoveSet.add(nameToRemove);
}
});
}
}
});
const componentsToRemove = [...componentsToRemoveSet];
const removedNodes = /* @__PURE__ */ new WeakSet();
for (const componentName of componentsToRemove) {
removeImportDeclaration(ast, componentName, s);
removeVariableDeclarator(ast, componentName, s, removedNodes);
removeFromSetupReturn(ast, componentName, s);
}
if (s.hasChanged()) {
return {
code: s.toString(),
map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
};
}
}
};
});
function removeFromSetupReturn(codeAst, name, magicString) {
let walkedInSetup = false;
walk(codeAst, {
enter(node) {
if (walkedInSetup) {
this.skip();
} else if (node.type === "Property" && node.key.type === "Identifier" && node.key.name === "setup" && (node.value.type === "FunctionExpression" || node.value.type === "ArrowFunctionExpression")) {
walkedInSetup = true;
if (node.value.body?.type === "BlockStatement") {
const returnStatement = node.value.body.body.find((statement) => statement.type === "ReturnStatement");
if (returnStatement && returnStatement.argument?.type === "ObjectExpression") {
removePropertyFromObject(returnStatement.argument, name, magicString);
}
const variableList = node.value.body.body.filter((statement) => statement.type === "VariableDeclaration");
const returnedVariableDeclaration = variableList.find((declaration) => declaration.declarations[0]?.id.type === "Identifier" && declaration.declarations[0]?.id.name === "__returned__" && declaration.declarations[0]?.init?.type === "ObjectExpression");
if (returnedVariableDeclaration) {
const init = returnedVariableDeclaration.declarations[0]?.init;
if (init) {
removePropertyFromObject(init, name, magicString);
}
}
}
}
}
});
}
function removePropertyFromObject(node, name, magicString) {
for (const property of node.properties) {
if (property.type === "Property" && property.key.type === "Identifier" && property.key.name === name) {
magicString.remove(property.start, property.end + 1);
return true;
}
}
return false;
}
function isSsrRender(node) {
return node.type === "CallExpression" && node.callee.type === "Identifier" && SSR_RENDER_RE.test(node.callee.name);
}
function removeImportDeclaration(ast, importName, magicString) {
for (const node of ast.body) {
if (node.type !== "ImportDeclaration" || !node.specifiers) {
continue;
}
const specifierIndex = node.specifiers.findIndex((s) => s.local.name === importName);
if (specifierIndex > -1) {
if (node.specifiers.length > 1) {
const specifier = node.specifiers[specifierIndex];
magicString.remove(specifier.start, specifier.end + 1);
node.specifiers.splice(specifierIndex, 1);
} else {
magicString.remove(node.start, node.end);
}
return true;
}
}
return false;
}
function isComponentNotCalledInSetup(code, id, name) {
if (!name) {
return;
}
let found = false;
parseAndWalk(code, id, function(node) {
if (node.type === "Property" && node.key.type === "Identifier" && node.value.type === "FunctionExpression" && node.key.name === "setup" || node.type === "FunctionDeclaration" && (node.id?.name === "_sfc_ssrRender" || node.id?.name === "ssrRender")) {
walk(node, {
enter(node2) {
if (found || node2.type === "VariableDeclaration") {
this.skip();
} else if (node2.type === "Identifier" && node2.name === name) {
found = true;
} else if (node2.type === "MemberExpression") {
found = node2.property.type === "Literal" && node2.property.value === name || node2.property.type === "Identifier" && node2.property.name === name;
}
}
});
}
});
if (!found) {
return name;
}
}
function getComponentName(ssrRenderNode) {
const componentCall = ssrRenderNode.arguments[0];
if (!componentCall) {
return;
}
if (componentCall.type === "Identifier") {
return componentCall.name;
} else if (componentCall.type === "MemberExpression") {
if (componentCall.property.type === "Literal") {
return componentCall.property.value;
}
} else if (componentCall.type === "CallExpression") {
return getComponentName(componentCall);
}
}
function removeVariableDeclarator(codeAst, name, magicString, removedNodes) {
walk(codeAst, {
enter(node) {
if (node.type !== "VariableDeclaration") {
return;
}
for (const declarator of node.declarations) {
const toRemove = findMatchingPatternToRemove(declarator.id, node, name, removedNodes);
if (toRemove) {
magicString.remove(toRemove.start, toRemove.end + 1);
removedNodes.add(toRemove);
}
}
}
});
}
function findMatchingPatternToRemove(node, toRemoveIfMatched, name, removedNodeSet) {
if (node.type === "Identifier") {
if (node.name === name) {
return toRemoveIfMatched;
}
} else if (node.type === "ArrayPattern") {
const elements = node.elements.filter((e) => e !== null && !removedNodeSet.has(e));
for (const element of elements) {
const matched = findMatchingPatternToRemove(element, elements.length > 1 ? element : toRemoveIfMatched, name, removedNodeSet);
if (matched) {
return matched;
}
}
} else if (node.type === "ObjectPattern") {
const properties = node.properties.filter((e) => e.type === "Property" && !removedNodeSet.has(e));
for (const [index, property] of properties.entries()) {
let nodeToRemove = property;
if (properties.length < 2) {
nodeToRemove = toRemoveIfMatched;
}
const matched = findMatchingPatternToRemove(property.value, nodeToRemove, name, removedNodeSet);
if (matched) {
if (matched === property) {
properties.splice(index, 1);
}
return matched;
}
}
} else if (node.type === "AssignmentPattern") {
const matched = findMatchingPatternToRemove(node.left, toRemoveIfMatched, name, removedNodeSet);
if (matched) {
return matched;
}
}
}
const FILENAME_RE = /([^/\\]+)\.\w+$/;
const ComponentNamePlugin = (options) => createUnplugin(() => {
return {
name: "nuxt:component-name-plugin",
enforce: "post",
transformInclude(id) {
return isVue(id) || !!id.match(SX_RE);
},
transform: {
filter: {
id: { include: FILENAME_RE }
},
handler(code, id) {
const filename = id.match(FILENAME_RE)?.[1];
if (!filename) {
return;
}
const component = options.getComponents().find((c) => c.filePath === id);
if (!component) {
return;
}
const NAME_RE = new RegExp(`__name:\\s*['"]${filename}['"]`);
const s = new MagicString(code);
s.replace(NAME_RE, `__name: ${JSON.stringify(component.pascalName)}`);
if (!s.hasChanged()) {
parseAndWalk(code, id, function(node) {
if (node.type !== "ExportDefaultDeclaration") {
return;
}
const { start, end } = node.declaration;
s.overwrite(start, end, `Object.assign(${code.slice(start, end)}, { __name: ${JSON.stringify(component.pascalName)} })`);
this.skip();
});
}
if (s.hasChanged()) {
return {
code: s.toString(),
map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
};
}
}
}
};
});
const SCRIPT_RE$1 = /(?<=<script[^>]*>)[\s\S]*?(?=<\/script>)/gi;
const TEMPLATE_RE = /<template>([\s\S]*)<\/template>/;
const hydrationStrategyMap = {
hydrateOnIdle: "Idle",
hydrateOnVisible: "Visible",
hydrateOnInteraction: "Interaction",
hydrateOnMediaQuery: "MediaQuery",
hydrateAfter: "Time",
hydrateWhen: "If",
hydrateNever: "Never"
};
const LAZY_HYDRATION_PROPS_RE = /\b(?:hydrate-on-idle|hydrateOnIdle|hydrate-on-visible|hydrateOnVisible|hydrate-on-interaction|hydrateOnInteraction|hydrate-on-media-query|hydrateOnMediaQuery|hydrate-after|hydrateAfter|hydrate-when|hydrateWhen|hydrate-never|hydrateNever)\b/;
const LazyHydrationTransformPlugin = (options) => createUnplugin(() => {
const exclude = options.transform?.exclude || [];
const include = options.transform?.include || [];
const nuxt = tryUseNuxt();
return {
name: "nuxt:components-loader-pre",
enforce: "pre",
transformInclude(id) {
if (exclude.some((pattern) => pattern.test(id))) {
return false;
}
if (include.some((pattern) => pattern.test(id))) {
return true;
}
return isVue(id);
},
transform: {
filter: {
code: { include: TEMPLATE_RE }
},
async handler(code, id) {
const scopeTracker = new ScopeTracker({ preserveExitedScopes: true });
for (const { 0: script } of code.matchAll(SCRIPT_RE$1)) {
if (!script) {
continue;
}
try {
parseAndWalk(script, id, {
scopeTracker
});
} catch {
}
}
const { 0: template, index: offset = 0 } = code.match(TEMPLATE_RE) || {};
if (!template || !LAZY_HYDRATION_PROPS_RE.test(template)) {
return;
}
const s = new MagicString(code);
try {
const ast = parse(template);
const components = new Set(options.getComponents().map((c) => c.pascalName));
await walk$1(ast, (node) => {
if (node.type !== 1) {
return;
}
if (scopeTracker.getDeclaration(node.name)) {
return;
}
const pascalName = pascalCase(node.name.replace(/^(?:Lazy|lazy-)/, ""));
if (!components.has(pascalName)) {
return;
}
let strategy;
for (const attr in node.attributes) {
const isDynamic = attr.startsWith(":");
const prop = camelCase(isDynamic ? attr.slice(1) : attr);
if (prop in hydrationStrategyMap) {
if (strategy) {
logger.warn(`Multiple hydration strategies are not supported in the same component`);
} else {
strategy = hydrationStrategyMap[prop];
}
}
}
if (strategy && !/^(?:Lazy|lazy-)/.test(node.name)) {
if (node.name !== "template" && (nuxt?.options.dev || nuxt?.options.test)) {
const relativePath = resolveToAlias(id, nuxt);
logger.warn(`Component \`<${node.name}>\` (used in \`${relativePath}\`) has lazy-hydration props but is not declared as a lazy component.
Rename it to \`<Lazy${pascalCase(node.name)} />\` or remove the lazy-hydration props to avoid unexpected behavior.`);
}
return;
}
if (strategy) {
const newName = "Lazy" + strategy + pascalName;
const chunk = template.slice(node.loc[0].start, node.loc.at(-1).end);
const chunkOffset = node.loc[0].start + offset;
const { 0: startingChunk, index: startingPoint = 0 } = chunk.match(new RegExp(`<${node.name}[^>]*>`)) || {};
s.overwrite(startingPoint + chunkOffset, startingPoint + chunkOffset + startingChunk.length, startingChunk.replace(node.name, newName));
const { 0: endingChunk, index: endingPoint } = chunk.match(new RegExp(`<\\/${node.name}[^>]*>$`)) || {};
if (endingChunk && endingPoint) {
s.overwrite(endingPoint + chunkOffset, endingPoint + chunkOffset + endingChunk.length, endingChunk.replace(node.name, newName));
}
}
});
} catch {
}
if (s.hasChanged()) {
return {
code: s.toString(),
map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
};
}
}
}
};
});
const LAZY_HYDRATION_MACRO_RE = /\bdefineLazyHydrationComponent\s*\(/;
const HYDRATION_TO_FACTORY = /* @__PURE__ */ new Map([
["visible", "createLazyVisibleComponent"],
["idle", "createLazyIdleComponent"],
["interaction", "createLazyInteractionComponent"],
["mediaQuery", "createLazyMediaQueryComponent"],
["if", "createLazyIfComponent"],
["time", "createLazyTimeComponent"],
["never", "createLazyNeverComponent"]
]);
const LazyHydrationMacroTransformPlugin = (options) => createUnplugin(() => {
const exclude = options.transform?.exclude || [];
const include = options.transform?.include || [];
return {
name: "nuxt:lazy-hydration-macro",
enforce: "post",
transformInclude(id) {
if (exclude.some((pattern) => pattern.test(id))) {
return false;
}
if (include.some((pattern) => pattern.test(id))) {
return true;
}
return isVue(id, { type: ["template", "script"] }) || isJS(id);
},
transform: {
filter: {
code: {
include: LAZY_HYDRATION_MACRO_RE
}
},
handler(code, id) {
const s = new MagicString(code);
const names = /* @__PURE__ */ new Set();
const edits = [];
parseAndWalk(code, id, (node, parent) => {
if (node.type !== "CallExpression") {
return;
}
if (node.callee?.type !== "Identifier") {
return;
}
if (node.callee.name !== "defineLazyHydrationComponent") {
return;
}
if (parent?.type !== "VariableDeclarator") {
return;
}
if (parent.id.type !== "Identifier") {
return;
}
if (node.arguments.length < 2) {
return;
}
const [strategyArgument, loaderArgument] = node.arguments;
if (!isStringLiteral(strategyArgument)) {
return;
}
const strategy = strategyArgument.value;
const functionName = HYDRATION_TO_FACTORY.get(strategy);
if (!functionName) {
return;
}
if (loaderArgument?.type !== "ArrowFunctionExpression") {
return;
}
const { importExpression, importLiteral } = findImportExpression(loaderArgument.body);
if (!importExpression || !isStringLiteral(importLiteral)) {
return;
}
const rawPath = importLiteral.value;
const filePath = resolveAlias(rawPath, options.alias || {});
const relativePath = relative(options.srcDir, filePath);
const originalLoader = code.slice(loaderArgument.start, loaderArgument.end);
const replacement = `__${functionName}(${JSON.stringify(relativePath)}, ${originalLoader})`;
edits.push({ start: node.start, end: node.end, replacement });
names.add(functionName);
});
for (const edit of edits) {
s.overwrite(edit.start, edit.end, edit.replacement);
}
if (names.size) {
const imports = genImport(options.clientDelayedComponentRuntime, [...names].map((name) => ({ name, as: `__${name}` })));
s.prepend(imports);
}
if (s.hasChanged()) {
return {
code: s.toString(),
map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
};
}
}
}
};
});
function isStringLiteral(node) {
return !!node && node.type === "Literal" && typeof node.value === "string";
}
function findImportExpression(node) {
if (node.type === "ImportExpression") {
return { importExpression: node, importLiteral: node.source };
}
if (node.type === "BlockStatement") {
const returnStmt = node.body.find((stmt) => stmt.type === "ReturnStatement");
if (returnStmt && returnStmt.argument) {
return findImportExpression(returnStmt.argument);
}
return {};
}
if (node.type === "ParenthesizedExpression") {
return findImportExpression(node.expression);
}
if (node.type === "AwaitExpression") {
return findImportExpression(node.argument);
}
if (node.type === "ConditionalExpression") {
return findImportExpression(node.consequent) || findImportExpression(node.alternate);
}
if (node.type === "MemberExpression") {
return findImportExpression(node.object);
}
if (node.type === "CallExpression") {
return findImportExpression(node.callee);
}
return {};
}
const isPureObjectOrString = (val) => !Array.isArray(val) && typeof val === "object" || typeof val === "string";
const isDirectory = (p) => {
try {
return statSync(p).isDirectory();
} catch {
return false;
}
};
const SLASH_SEPARATOR_RE = /[\\/]/;
function compareDirByPathLength({ path: pathA }, { path: pathB }) {
return pathB.split(SLASH_SEPARATOR_RE).filter(Boolean).length - pathA.split(SLASH_SEPARATOR_RE).filter(Boolean).length;
}
const DEFAULT_COMPONENTS_DIRS_RE = /\/components(?:\/(?:global|islands))?$/;
const STARTER_DOT_RE = /^\./g;
const componentsModule = defineNuxtModule({
meta: {
name: "nuxt:components",
configKey: "components"
},
defaults: {
dirs: []
},
async setup(componentOptions, nuxt) {
let componentDirs = [];
const context = {
components: []
};
const getComponents = (mode) => {
return mode && mode !== "all" ? context.components.filter((c) => c.mode === mode || c.mode === "all" || c.mode === "server" && !context.components.some((otherComponent) => otherComponent.mode !== "server" && otherComponent.pascalName === c.pascalName)) : context.components;
};
if (nuxt.options.experimental.normalizeComponentNames) {
addBuildPlugin(ComponentNamePlugin({ sourcemap: !!nuxt.options.sourcemap.client, getComponents }), { server: false });
addBuildPlugin(ComponentNamePlugin({ sourcemap: !!nuxt.options.sourcemap.server, getComponents }), { client: false });
}
const normalizeDirs = (dir, cwd, options) => {
if (Array.isArray(dir)) {
return dir.map((dir2) => normalizeDirs(dir2, cwd, options)).flat().sort(compareDirByPathLength);
}
if (dir === true || dir === void 0) {
return [
{ priority: options?.priority || 0, path: resolve(cwd, "components/islands"), island: true },
{ priority: options?.priority || 0, path: resolve(cwd, "components/global"), global: true },
{ priority: options?.priority || 0, path: resolve(cwd, "components") }
];
}
if (typeof dir === "string") {
return [
{ priority: options?.priority || 0, path: resolve(cwd, resolveAlias$1(dir)) }
];
}
if (!dir) {
return [];
}
const dirs = (dir.dirs || [dir]).map((dir2) => typeof dir2 === "string" ? { path: dir2 } : dir2).filter((_dir) => _dir.path);
return dirs.map((_dir) => ({
priority: options?.priority || 0,
..._dir,
path: resolve(cwd, resolveAlias$1(_dir.path))
}));
};
nuxt.hook("app:resolve", async () => {
const allDirs = nuxt.options._layers.map((layer) => normalizeDirs(layer.config.components, layer.config.srcDir, { priority: layer.config.srcDir === nuxt.options.srcDir ? 1 : 0 })).flat();
await nuxt.callHook("components:dirs", allDirs);
componentDirs = allDirs.filter(isPureObjectOrString).map((dir) => {
const dirOptions = typeof dir === "object" ? dir : { path: dir };
const dirPath = resolveAlias$1(dirOptions.path);
const transpile = typeof dirOptions.transpile === "boolean" ? dirOptions.transpile : "auto";
const extensions = (dirOptions.extensions || nuxt.options.extensions).map((e) => e.replace(STARTER_DOT_RE, ""));
const present = isDirectory(dirPath);
if (!present && !DEFAULT_COMPONENTS_DIRS_RE.test(dirOptions.path)) {
logger.warn("Components directory not found: `" + dirPath + "`");
}
return {
global: componentOptions.global,
...dirOptions,
// TODO: https://github.com/nuxt/framework/pull/251
enabled: true,
path: dirPath,
extensions,
pattern: dirOptions.pattern || `**/*.{${extensions.join(",")},}`,
ignore: [
"**/*{M,.m,-m}ixin.{js,ts,jsx,tsx}",
// ignore mixins
"**/*.d.{cts,mts,ts}",
// .d.ts files
...dirOptions.ignore || []
],
transpile: transpile === "auto" ? dirPath.includes("node_modules") : transpile
};
}).filter((d) => d.enabled);
componentDirs = [
...componentDirs.filter((dir) => !dir.path.includes("node_modules")),
...componentDirs.filter((dir) => dir.path.includes("node_modules"))
];
nuxt.options.build.transpile.push(...componentDirs.filter((dir) => dir.transpile).map((dir) => dir.path));
});
addTemplate(componentsDeclarationTemplate);
addTypeTemplate(componentsTypeTemplate);
addPluginTemplate(componentsPluginTemplate);
addTemplate(componentNamesTemplate);
addTemplate({ ...componentsIslandsTemplate, filename: "components.islands.mjs" });
if (componentOptions.generateMetadata) {
addTemplate(componentsMetadataTemplate);
}
const serverComponentRuntime = await findPath(join(distDir, "components/runtime/server-component")) ?? join(distDir, "components/runtime/server-component");
addBuildPlugin(TransformPlugin$1(nuxt, { getComponents, serverComponentRuntime, mode: "server" }), { server: true, client: false });
addBuildPlugin(TransformPlugin$1(nuxt, { getComponents, serverComponentRuntime, mode: "client" }), { server: false, client: true });
nuxt.hook("build:manifest", (manifest) => {
const sourceFiles = /* @__PURE__ */ new Set();
for (const c of getComponents()) {
if (c.global) {
sourceFiles.add(relative(nuxt.options.srcDir, c.filePath));
}
}
for (const chunk of Object.values(manifest)) {
if (chunk.isEntry) {
chunk.dynamicImports = chunk.dynamicImports?.filter((i) => !sourceFiles.has(i));
}
}
});
const restartEvents = /* @__PURE__ */ new Set(["addDir", "unlinkDir"]);
nuxt.hook("builder:watch", (event, relativePath) => {
if (!restartEvents.has(event)) {
return;
}
const path = resolve(nuxt.options.srcDir, relativePath);
if (componentDirs.some((dir) => dir.path === path)) {
logger.info(`Directory \`${relativePath}/\` ${event === "addDir" ? "created" : "removed"}`);
return nuxt.callHook("restart");
}
});
const serverPlaceholderPath = await findPath(join(distDir, "app/components/server-placeholder")) ?? join(distDir, "app/components/server-placeholder");
nuxt.hook("app:templates", async (app) => {
const newComponents = await scanComponents(componentDirs, nuxt.options.srcDir);
await nuxt.callHook("components:extend", newComponents);
for (const component of newComponents) {
if (!component._scanned && !(component.filePath in nuxt.vfs) && isAbsolute(component.filePath) && !existsSync(component.filePath)) {
component.filePath = resolveModulePath(resolveAlias$1(component.filePath), { try: true, extensions: nuxt.options.extensions }) ?? component.filePath;
}
if (component.mode === "client" && !newComponents.some((c) => c.pascalName === component.pascalName && c.mode === "server")) {
newComponents.push({
...component,
_raw: true,
mode: "server",
filePath: serverPlaceholderPath,
chunkName: "components/" + component.kebabName
});
}
if (component.mode === "server" && !nuxt.options.ssr && !newComponents.some((other) => other.pascalName === component.pascalName && other.mode === "client")) {
logger.warn(`Using server components with \`ssr: false\` is not supported with auto-detected component islands. If you need to use server component \`${component.pascalName}\`, set \`experimental.componentIslands\` to \`true\`.`);
}
}
context.components = newComponents;
app.components = newComponents;
});
nuxt.hook("prepare:types", ({ tsConfig }) => {
tsConfig.compilerOptions.paths["#components"] = [resolve(nuxt.options.buildDir, "components")];
});
if (nuxt.options.experimental.treeshakeClientOnly) {
addBuildPlugin(TreeShakeTemplatePlugin({ sourcemap: !!nuxt.options.sourcemap.server, getComponents }), { client: false });
}
const clientDelayedComponentRuntime = await findPath(join(distDir, "components/runtime/lazy-hydrated-component")) ?? join(distDir, "components/runtime/lazy-hydrated-component");
const sharedLoaderOptions = {
getComponents,
clientDelayedComponentRuntime,
serverComponentRuntime,
srcDir: nuxt.options.srcDir,
transform: typeof nuxt.options.components === "object" && !Array.isArray(nuxt.options.components) ? nuxt.options.components.transform : void 0,
experimentalComponentIslands: !!nuxt.options.experimental.componentIslands
};
addBuildPlugin(LoaderPlugin({ ...sharedLoaderOptions, sourcemap: !!nuxt.options.sourcemap.client, mode: "client" }), { server: false });
addBuildPlugin(LoaderPlugin({ ...sharedLoaderOptions, sourcemap: !!nuxt.options.sourcemap.server, mode: "server" }), { client: false });
if (nuxt.options.experimental.lazyHydration) {
addBuildPlugin(LazyHydrationTransformPlugin({
...sharedLoaderOptions,
sourcemap: !!(nuxt.options.sourcemap.server || nuxt.options.sourcemap.client)
}), { prepend: true });
addBuildPlugin(LazyHydrationMacroTransformPlugin({
...sharedLoaderOptions,
sourcemap: !!(nuxt.options.sourcemap.server || nuxt.options.sourcemap.client),
alias: nuxt.options.alias
}));
addImportsSources(lazyHydrationMacroPreset);
}
if (nuxt.options.experimental.componentIslands) {
const selectiveClient = typeof nuxt.options.experimental.componentIslands === "object" && nuxt.options.experimental.componentIslands.selectiveClient;
addVitePlugin({
name: "nuxt-server-component-hmr",
handleHotUpdate(ctx) {
const components = getComponents();
const filePath = normalize(ctx.file);
const comp = components.find((c) => c.filePath === filePath);
if (comp?.mode === "server") {
ctx.server.ws.send({
event: `nuxt-server-component:${comp.pascalName}`,
type: "custom"
});
}
}
}, { server: false });
addBuildPlugin(IslandsTransformPlugin({ getComponents, selectiveClient }), { client: false });
if (selectiveClient && nuxt.options.builder === "@nuxt/vite-builder") {
addVitePlugin(() => ComponentsChunkPlugin({ dev: nuxt.options.dev, getComponents }));
} else {
addTemplate({
filename: "component-chunk.mjs",
getContents: () => `export default {}`
});
}
}
}
});
const NODE_MODULES_RE$1 = /[\\/]node_modules[\\/]/;
const IMPORTS_RE = /(['"])#imports\1/;
const TransformPlugin = ({ ctx, options, sourcemap }) => createUnplugin(() => {
return {
name: "nuxt:imports-transform",
enforce: "post",
transformInclude(id) {
if (options.transform?.include?.some((pattern) => pattern.test(id))) {
return true;
}
if (options.transform?.exclude?.some((pattern) => pattern.test(id))) {
return false;
}
if (isVue(id, { type: ["script", "template"] })) {
return true;
}
return isJS(id);
},
async transform(code, id) {
id = normalize(id);
const isNodeModule = NODE_MODULES_RE$1.test(id) && !options.transform?.include?.some((pattern) => pattern.test(id));
if (isNodeModule && !IMPORTS_RE.test(code)) {
return;
}
const { s, imports } = await ctx.injectImports(code, id, { autoImport: options.autoImport && !isNodeModule });
if (imports.some((i) => i.from === "#app/composables/script-stubs") && tryUseNuxt()?.options.test === false) {
installNuxtModule("@nuxt/scripts");
}
if (s.hasChanged()) {
return {
code: s.toString(),
map: sourcemap ? s.generateMap({ hires: true }) : void 0
};
}
}
};
});
const importsModule = defineNuxtModule({
meta: {
name: "nuxt:imports",
configKey: "imports"
},
defaults: (nuxt) => ({
autoImport: true,
scan: true,
presets: defaultPresets,
global: false,
imports: [],
dirs: [],
transform: {
include: [
new RegExp("^" + escapeRE(nuxt.options.buildDir))
],
exclude: void 0
},
virtualImports: ["#imports"],
polyfills: true
}),
async setup(options, nuxt) {
const presets = JSON.parse(JSON.stringify(options.presets));
if (options.polyfills) {
presets.push(...appCompatPresets);
}
await nuxt.callHook("imports:sources", presets);
const { addons: inlineAddons, ...rest } = options;
const [addons, addonsOptions] = Array.isArray(inlineAddons) ? [inlineAddons] : [[], inlineAddons];
const ctx = createUnimport({
injectAtEnd: true,
...rest,
addons: {
addons,
vueTemplate: options.autoImport,
vueDirectives: options.autoImport === false ? void 0 : true,
...addonsOptions
},
presets
});
await nuxt.callHook("imports:context", ctx);
const isNuxtV4 = nuxt.options.future?.compatibilityVersion === 4;
let composablesDirs = [];
if (options.scan) {
for (const layer of nuxt.options._layers) {
if (layer.config?.imports?.scan === false) {
continue;
}
composablesDirs.push(resolve(layer.config.srcDir, "composables"));
composablesDirs.push(resolve(layer.config.srcDir, "utils"));
if (isNuxtV4) {
composablesDirs.push(resolve(layer.config.rootDir, layer.config.dir?.shared ?? "shared", "utils"));
composablesDirs.push(resolve(layer.config.rootDir, layer.config.dir?.shared ?? "shared", "types"));
}
for (const dir of layer.config.imports?.dirs ?? []) {
if (!dir) {
continue;
}
composablesDirs.push(resolve(layer.config.srcDir, dir));
}
}
await nuxt.callHook("imports:dirs", composablesDirs);
composablesDirs = composablesDirs.map((dir) => normalize(dir));
nuxt.hook("builder:watch", (event, relativePath) => {
if (!["addDir", "unlinkDir"].includes(event)) {
return;
}
const path = resolve(nuxt.options.srcDir, relativePath);
if (composablesDirs.includes(path)) {
logger.info(`Directory \`${relativePath}/\` ${event === "addDir" ? "created" : "removed"}`);
return nuxt.callHook("restart");
}
});
}
addTemplate({
filename: "imports.mjs",
getContents: async () => toExports(await ctx.getImports()) + '\nif (import.meta.dev) { console.warn("[nuxt] `#imports` should be transformed with real imports. There seems to be something wrong with the imports plugin.") }'
});
nuxt.options.alias["#imports"] = join(nuxt.options.buildDir, "imports");
addBuildPlugin(TransformPlugin({ ctx, options, sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client }));
const priorities = getLayerDirectories(nuxt).map((dirs, i) => [dirs.app, -i]).sort(([a], [b]) => b.length - a.length);
const IMPORTS_TEMPLATE_RE = /\/imports\.(?:d\.ts|mjs)$/;
function isImportsTemplate(template) {
return IMPORTS_TEMPLATE_RE.test(template.filename);
}
const isIgnored = createIsIgnored(nuxt);
const defaultImportSources = new Set(defaultPresets.flatMap((i) => i.from));
const defaultImports = new Set(presets.flatMap((p) => defaultImportSources.has(p.from) ? p.imports : []));
const regenerateImports = async () => {
await ctx.modifyDynamicImports(async (imports) => {
imports.length = 0;
if (options.scan) {
const scannedImports = await scanDirExports(composablesDirs, {
fileFilter: (file) => !isIgnored(file)
});
for (const i of scannedImports) {
i.priority ||= priorities.find(([dir]) => i.from.startsWith(dir))?.[1];
}
imports.push(...scannedImports);
}
await nuxt.callHook("imports:extend", imports);
for (const i of imports) {
if (!defaultImportSources.has(i.from)) {
const value = i.as || i.name;
if (defaultImports.has(value) && (!i.priority || i.priority >= 0)) {
const relativePath = isAbsolute(i.from) ? `${resolveToAlias(i.from, nuxt)}` : i.from;
logger.error(`\`${value}\` is an auto-imported function that is in use by Nuxt. Overriding it will likely cause issues. Please consider renaming \`${value}\` in \`${relativePath}\`.`);
}
}
}
return imports;
});
await updateTemplates({
filter: isImportsTemplate
});
};
await regenerateImports();
addDeclarationTemplates(ctx, options);
nuxt.hook("builder:watch", async (_, relativePath) => {
const path = resolve(nuxt.options.srcDir, relativePath);
if (options.scan && composablesDirs.some((dir) => dir === path || path.startsWith(dir + "/"))) {
await regenerateImports();
}
});
nuxt.hook("app:templatesGenerated", async (_app, templates) => {
if (templates.some((t) => !isImportsTemplate(t))) {
await regenerateImports();
}
});
}
});
function addDeclarationTemplates(ctx, options) {
const nuxt = useNuxt();
const resolvedImportPathMap = /* @__PURE__ */ new Map();
const r = ({ from }) => resolvedImportPathMap.get(from);
const SUPPORTED_EXTENSION_RE = new RegExp(`\\.(${nuxt.options.extensions.map((i) => i.replace(".", "")).join("|")})$`);
const importPaths = nuxt.options.modulesDir.map((dir) => directoryToURL(dir));
async function cacheImportPaths(imports) {
const importSource = Array.from(new Set(imports.map((i) => i.from)));
await Promise.all(importSource.map(async (from) => {
if (resolvedImportPathMap.has(from) || nuxt._dependencies?.has(from)) {
return;
}
let path = resolveAlias$1(from);
if (!isAbsolute(path)) {
path = await tryResolveModule(from, importPaths).then(async (r2) => {
if (!r2) {
return r2;
}
const { dir, name } = parseNodeModulePath(r2);
if (name && nuxt._dependencies?.has(name)) {
return from;
}
if (!dir || !name) {
return r2;
}
const subpath = await lookupNodeModuleSubpath(r2);
return join(dir, name, subpath || "");
}) ?? path;
}
if (existsSync(path) && !await isDirectory$1(path)) {
path = path.replace(SUPPORTED_EXTENSION_RE, "");
}
if (isAbsolute(path)) {
path = relative(join(nuxt.options.buildDir, "types"), path);
}
resolvedImportPathMap.set(from, path);
}));
}
addTypeTemplate({
filename: "imports.d.ts",
getContents: async ({ nuxt: nuxt2 }) => toExports(await ctx.getImports(), nuxt2.options.buildDir, true)
});
addTypeTemplate({
filename: "types/imports.d.ts",
getContents: async () => {
const imports = await ctx.getImports();
await cacheImportPaths(imports);
return "// Generated by auto imports\n" + (options.autoImport ? await ctx.generateTypeDeclarations({ resolvePath: r }) : "// Implicit auto importing is disabled, you can use explicitly import from `#imports` instead.");
}
});
}
const version = "3.19.0";
const 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.modules.filter((m) => typeof m === "string")) {
patterns.push([
new RegExp(`^${escapeRE(mod)}$`),
"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 TRANSFORM_MARKER = "/* _processed_nuxt_unctx_transform */\n";
const TRANSFORM_MARKER_RE = /^\/\* _processed_nuxt_unctx_transform \*\/\n/;
const UnctxTransformPlugin = (options) => createUnplugin(() => {
const transformer = createTransformer(options.transformerOptions);
return {
name: "unctx:transform",
enforce: "post",
transformInclude(id) {
return isVue(id, { type: ["template", "script"] }) || isJS(id);
},
transform: {
filter: {
code: { exclude: TRANSFORM_MARKER_RE }
},
handler(code) {
if (!transformer.shouldTransform(code)) {
return;
}
const result = transformer.transform(code);
if (result) {
return {
code: TRANSFORM_MARKER + result.code,
map: options.sourcemap ? result.magicString.generateMap({ hires: true }) : void 0
};
}
}
}
};
});
const TreeShakeComposablesPlugin = (options) => createUnplugin(() => {
const composableNames = Object.values(options.composables).flat();
const regexp = `(^\\s*)(${composableNames.join("|")})(?=\\((?!\\) \\{))`;
const COMPOSABLE_RE = new RegExp(regexp, "m");
const COMPOSABLE_RE_GLOBAL = new RegExp(regexp, "gm");
return {
name: "nuxt:tree-shake-composables:transform",
enforce: "post",
transformInclude(id) {
return isVue(id, { type: ["script"] }) || isJS(id);
},
transform: {
filter: {
code: { include: COMPOSABLE_RE }
},
handler(code) {
const s = new MagicString(code);
const strippedCode = stripLiteral(code);
for (const match of strippedCode.matchAll(COMPOSABLE_RE_GLOBAL)) {
s.overwrite(match.index, match.index + match[0].length, `${match[1]} false && /*@__PURE__*/ ${match[2]}`);
}
if (s.hasChanged()) {
return {
code: s.toString(),
map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
};
}
}
}
};
});
const DEVONLY_COMP_SINGLE_RE = /<(?:dev-only|DevOnly|lazy-dev-only|LazyDevOnly)>[\s\S]*?<\/(?:dev-only|DevOnly|lazy-dev-only|LazyDevOnly)>/;
const DEVONLY_COMP_RE = /<(?:dev-only|DevOnly|lazy-dev-only|LazyDevOnly)>[\s\S]*?<\/(?:dev-only|DevOnly|lazy-dev-only|LazyDevOnly)>/g;
const DevOnlyPlugin = (options) => createUnplugin(() => {
return {
name: "nuxt:server-devonly:transform",
enforce: "pre",
transformInclude(id) {
return isVue(id, { type: ["template"] });
},
transform: {
filter: {
code: { include: DEVONLY_COMP_SINGLE_RE }
},
handler(code) {
const s = new MagicString(code);
for (const match of code.matchAll(DEVONLY_COMP_RE)) {
const ast = parse(match[0]).children[0];
const fallback = ast.children?.find((n) => {
if (n.name !== "template") {
return false;
}
return "fallback" in n.attributes || "#fallback" in n.attributes;
});
const replacement = fallback ? match[0].slice(fallback.loc[0].end, fallback.loc[fallback.loc.length - 1].start) : "";
s.overwrite(match.index, match.index + match[0].length, replacement);
}
if (s.hasChanged()) {
return {
code: s.toString(),
map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
};
}
}
}
};
});
const ALIAS_RE = /(?<=['"])[~@]{1,2}(?=\/)/g;
const ALIAS_RE_SINGLE = /(?<=['"])[~@]{1,2}(?=\/)/;
const LayerAliasingPlugin = (options) => createUnplugin((_options, meta) => {
const aliases = {};
for (const layer of options.layers) {
const srcDir = layer.config.srcDir || layer.cwd;
const rootDir = layer.config.rootDir || layer.cwd;
aliases[srcDir] = {
"~": layer.config?.alias?.["~"] || srcDir,
"@": layer.config?.alias?.["@"] || srcDir,
"~~": layer.config?.alias?.["~~"] || rootDir,
"@@": layer.config?.alias?.["@@"] || rootDir
};
}
const layers = Object.keys(aliases).sort((a, b) => b.length - a.length);
return {
name: "nuxt:layer-aliasing",
enforce: "pre",
vite: {
resolveId: {
order: "pre",
async handler(id, importer) {
if (!importer) {
return;
}
const layer = layers.find((l) => importer.startsWith(l));
if (!layer) {
return;
}
const resolvedId = resolveAlias$1(id, aliases[layer]);
if (resolvedId !== id) {
return await this.resolve(resolvedId, importer, { skipSelf: true });
}
}
}
},
// webpack-only transform
transformInclude: (id) => {
if (meta.framework === "vite") {
return false;
}
const _id = normalize(id);
return layers.some((dir) => _id.startsWith(dir));
},
transform: {
filter: {
code: { include: ALIAS_RE_SINGLE }
},
handler(code, id) {
if (meta.framework === "vite") {
return;
}
const _id = normalize(id);
const layer = layers.find((l) => _id.startsWith(l));
if (!layer) {
return;
}
const s = new MagicString(code);
s.replace(ALIAS_RE, (r) => aliases[layer]?.[r] || r);
if (s.hasChanged()) {
return {
code: s.toString(),
map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
};
}
}
}
};
});
const addModuleTranspiles = (opts = {}) => {
const nuxt = useNuxt();
const modules = [
...opts.additionalModules || [],
...nuxt.options.modules,
...nuxt.options._modules
].map((m) => typeof m === "string" ? m : Array.isArray(m) ? m[0] : m.src).filter((m) => typeof m === "string").map(normalizeModuleTranspilePath);
nuxt.options.build.transpile = nuxt.options.build.transpile.map((m) => typeof m === "string" ? m.split("node_modules/").pop() : m).filter((x) => !!x);
function isTranspilePresent(mod) {
return nuxt.options.build.transpile.some((t) => !(t instanceof Function) && (t instanceof RegExp ? t.test(mod) : new RegExp(t).test(mod)));
}
for (const module of modules) {
if (!isTranspilePresent(module)) {
nuxt.options.build.transpile.push(module);
}
}
};
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>';
};
const logLevelMapReverse = {
silent: 0,
info: 3,
verbose: 3
};
const NODE_MODULES_RE = /(?<=\/)node_modules\/(.+)$/;
const PNPM_NODE_MODULES_RE = /\.pnpm\/.+\/node_modules\/(.+)$/;
async function initNitro(nuxt) {
const layerDirs = getLayerDirectories(nuxt);
const excludePaths = layerDirs.flatMap((dirs) => [
dirs.root.match(NODE_MODULES_RE)?.[1]?.replace(/\/$/, ""),
dirs.root.match(PNPM_NODE_MODULES_RE)?.[1]?.replace(/\/$/, "")
].filter((dir) => Boolean(dir)).map((dir) => escapeRE(dir)));
const excludePattern = excludePaths.length ? [new RegExp(`node_modules\\/(?!${excludePaths.join("|")})`)] : [/node_modules/];
const rootDirWithSlash = withTrailingSlash$1(nuxt.options.rootDir);
const moduleEntryPaths = [];
for (const m of nuxt.options._installedModules) {
const path = m.meta?.rawPath || m.entryPath;
if (path) {
moduleEntryPaths.push(path);
}
}
const modules = await resolveNuxtModule(rootDirWithSlash, moduleEntryPaths);
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"));
}
}
const mockProxy = resolveModulePath("mocked-exports/proxy", { 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: version
},
imports: {
autoImport: nuxt.options.imports.autoImport,
dirs: [...sharedDirs],
imports: [
{
as: "__buildAssetsURL",
name: "buildAssetsURL",
from: resolve(distDir, "core/runtime/nitro/utils/paths")
},
{
as: "__publicAssetsURL",
name: "publicAssetsURL",
from: resolve(distDir, "core/runtime/nitro/utils/paths")
},
{
// TODO: Remove after https://github.com/nitrojs/nitro/issues/1049
as: "defineAppConfig",
name: "defineAppConfig",
from: resolve(distDir, "core/runtime/nitro/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, "core/runtime/nitro/handlers/renderer"),
errorHandler: resolve(distDir, "core/runtime/nitro/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`
},
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")
];
}),
...getLayerDirectories(nuxt).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
},
...getLayerDirectories(nuxt).filter((dirs) => existsSync(dirs.public)).map((dirs) => ({ dir: dirs.public }))
],
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
...getLayerDirectories(nuxt).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, "core/runtime/nitro/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, "**/*")}`
);
nitroConfig.plugins = nitroConfig.plugins?.map((plugin) => plugin ? resolveAlias$1(plugin, nuxt.options.alias) : plugin);
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, "core/runtime/nitro/middleware/no-ssr"),
middleware: true
});
}
nitroConfig.rollupConfig.plugins = await nitroConfig.rollupConfig.plugins || [];
nitroConfig.rollupConfig.plugins = toArray(nitroConfig.rollupConfig.plugins);
const sharedDir = withTrailingSlash$1(resolve(nuxt.options.rootDir, nuxt.options.dir.shared));
const relativeSharedDir = withTrailingSlash$1(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)?[\\/]|core[\\/]runtime[\\/]nitro[\\/](?:handlers|utils)/, ...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, "core/runtime/nitro/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) {
nuxt.hook("vite:extendConfig", (config, { isClient }) => {
if (isClient) {
if (Array.isArray(config.resolve.alias)) {
config.resolve.alias.push({
find: "vue",
replacement: "vue/dist/vue.esm-bundler"
});
} else {
config.resolve.alias = {
...config.resolve.alias,
vue: "vue/dist/vue.esm-bundler"
};
}
}
});
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, "core/runtime/nitro/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$1(nitro);
}
await prerender(nitro);
logger$1.restoreAll();
await build$1(nitro);
logger$1.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$1.warn(`Could not load custom \`spaLoadingTemplate\` path as it does not exist: \`${nuxt.options.spaLoadingTemplate}\`.`);
}
return "";
}
const schemaModule = defineNuxtModule({
meta: {
name: "nuxt:nuxt-config-schema"
},
async setup(_, nuxt) {
if (!nuxt.options.experimental.configSchema) {
return;
}
const resolver = createResolver(import.meta.url);
const _resolveSchema = createJiti(fileURLToPath(import.meta.url), {
cache: false,
transformOptions: {
babel: {
plugins: [untypedPlugin]
}
}
});
nuxt.hook("prepare:types", async (ctx) => {
ctx.references.push({ path: "schema/nuxt.schema.d.ts" });
if (nuxt.options._prepare) {
await writeSchema(schema);
}
});
let schema;
nuxt.hook("modules:done", async () => {
schema = await resolveSchema$1();
});
nuxt.hooks.hook("build:done", () => writeSchema(schema));
const layerDirs = getLayerDirectories(nuxt);
if (nuxt.options.dev) {
const onChange = debounce(async () => {
schema = await resolveSchema$1();
await writeSchema(schema);
});
if (nuxt.options.experimental.watcher === "parcel") {
try {
const { subscribe } = await importModule("@parcel/watcher", {
url: [nuxt.options.rootDir, ...nuxt.options.modulesDir].map((dir) => directoryToURL(dir))
});
for (const dirs of layerDirs) {
const subscription = await subscribe(dirs.root, onChange, {
ignore: ["!nuxt.schema.*"]
});
nuxt.hook("close", () => subscription.unsubscribe());
}
return;
} catch {
logger.warn("Falling back to `chokidar` as `@parcel/watcher` cannot be resolved in your project.");
}
}
const isIgnored = createIsIgnored(nuxt);
const rootDirs = layerDirs.map((layer) => layer.root);
const SCHEMA_RE = /(?:^|\/)nuxt.schema.\w+$/;
const watcher = watch$1(rootDirs, {
...nuxt.options.watchers.chokidar,
depth: 1,
ignored: [
(path, stats) => stats && !stats.isFile() || !SCHEMA_RE.test(path),
isIgnored,
/[\\/]node_modules[\\/]/
],
ignoreInitial: true
});
watcher.on("all", onChange);
nuxt.hook("close", () => watcher.close());
}
async function resolveSchema$1() {
globalThis.defineNuxtSchema = (val) => val;
const schemaDefs = [nuxt.options.$schema];
for (const dirs of layerDirs) {
const filePath = await resolver.resolvePath(join(dirs.root, "nuxt.schema"));
if (filePath && existsSync(filePath)) {
let loadedConfig;
try {
loadedConfig = await _resolveSchema.import(filePath, { default: true });
} catch (err) {
logger.warn(
"Unable to load schema from",
filePath,
err
);
continue;
}
schemaDefs.push(loadedConfig);
}
}
await nuxt.hooks.callHook("schema:extend", schemaDefs);
const schemas = await Promise.all(
schemaDefs.map((schemaDef) => resolveSchema(schemaDef))
);
const schema2 = defu(...schemas);
await nuxt.hooks.callHook("schema:resolved", schema2);
return schema2;
}
async function writeSchema(schema2) {
await nuxt.hooks.callHook("schema:beforeWrite", schema2);
await mkdir(resolve(nuxt.options.buildDir, "schema"), { recursive: true });
await writeFile(
resolve(nuxt.options.buildDir, "schema/nuxt.schema.json"),
JSON.stringify(schema2, null, 2),
"utf8"
);
const _types = generateTypes(schema2, {
addExport: true,
interfaceName: "NuxtCustomSchema",
partial: true,
allowExtraKeys: false
});
const types = _types + `
export type CustomAppConfig = Exclude<NuxtCustomSchema['appConfig'], undefined>
type _CustomAppConfig = CustomAppConfig
declare module '@nuxt/schema' {
interface NuxtConfig extends Omit<NuxtCustomSchema, 'appConfig'> {}
interface NuxtOptions extends Omit<NuxtCustomSchema, 'appConfig'> {}
interface CustomAppConfig extends _CustomAppConfig {}
}
declare module 'nuxt/schema' {
interface NuxtConfig extends Omit<NuxtCustomSchema, 'appConfig'> {}
interface NuxtOptions extends Omit<NuxtCustomSchema, 'appConfig'> {}
interface CustomAppConfig extends _CustomAppConfig {}
}
`;
const typesPath = resolve(
nuxt.options.buildDir,
"schema/nuxt.schema.d.ts"
);
await writeFile(typesPath, types, "utf8");
await nuxt.hooks.callHook("schema:written");
}
}
});
const internalOrderMap = {
// -40: custom payload revivers (user)
"user-revivers": -40,
// -20: pre (user) <-- pre mapped to this
"user-pre": -20,
// 0: default (user) <-- default behavior
"user-default": 0,
// +20: post (user) <-- post mapped to this
"user-post": 20};
const orderMap = {
pre: internalOrderMap["user-pre"],
default: internalOrderMap["user-default"],
post: internalOrderMap["user-post"]
};
const metaCache = {};
function extractMetadata(code, loader = "ts") {
let meta = {};
if (metaCache[code]) {
return metaCache[code];
}
if (/defineNuxtPlugin\s*\([\w(]/.test(code)) {
return {};
}
parseAndWalk(code, `file.${loader}`, (node) => {
if (node.type !== "CallExpression" || node.callee.type !== "Identifier") {
return;
}
const name = "name" in node.callee && node.callee.name;
if (name !== "defineNuxtPlugin" && name !== "definePayloadPlugin") {
return;
}
if (name === "definePayloadPlugin") {
meta.order = internalOrderMap["user-revivers"];
}
const metaArg = node.arguments[1];
if (metaArg) {
if (metaArg.type !== "ObjectExpression") {
throw new Error("Invalid plugin metadata");
}
meta = extractMetaFromObject(metaArg.properties);
}
const plugin = node.arguments[0];
if (plugin?.type === "ObjectExpression") {
meta = defu(extractMetaFromObject(plugin.properties), meta);
}
meta.order ||= orderMap[meta.enforce || "default"] || orderMap.default;
delete meta.enforce;
});
metaCache[code] = meta;
return meta;
}
const keys = {
name: "name",
order: "order",
enforce: "enforce",
dependsOn: "dependsOn"
};
function isMetadataKey(key) {
return typeof key !== "string" ? key.name in keys : key in keys;
}
function extractMetaFromObject(properties) {
const meta = {};
for (const property of properties) {
if (property.type === "SpreadElement" || !("name" in property.key)) {
throw new Error("Invalid plugin metadata");
}
const propertyKey = property.key.name;
if (!isMetadataKey(propertyKey)) {
continue;
}
if (property.value.type === "Literal") {
meta[propertyKey] = property.value.value;
}
if (property.value.type === "UnaryExpression" && property.value.argument.type === "Literal") {
meta[propertyKey] = JSON.parse(property.value.operator + property.value.argument.raw);
}
if (propertyKey === "dependsOn" && property.value.type === "ArrayExpression") {
if (property.value.elements.some((e) => !e || e.type !== "Literal" || typeof e.value !== "string")) {
throw new Error("dependsOn must take an array of string literals");
}
meta[propertyKey] = property.value.elements.map((e) => e.value);
}
}
return meta;
}
const RemovePluginMetadataPlugin = (nuxt) => createUnplugin(() => {
return {
name: "nuxt:remove-plugin-metadata",
transform(code, id) {
id = normalize(id);
const plugin = nuxt.apps.default?.plugins.find((p) => p.src === id);
if (!plugin) {
return;
}
if (!code.trim()) {
logger.warn(`Plugin \`${plugin.src}\` has no content.`);
return {
code: "export default () => {}",
map: null
};
}
const exports = findExports(code);
const defaultExport = exports.find((e) => e.type === "default" || e.name === "default");
if (!defaultExport) {
logger.warn(`Plugin \`${plugin.src}\` has no default export and will be ignored at build time. Add \`export default defineNuxtPlugin(() => {})\` to your plugin.`);
return {
code: "export default () => {}",
map: null
};
}
const s = new MagicString(code);
let wrapped = false;
const wrapperNames = /* @__PURE__ */ new Set(["defineNuxtPlugin", "definePayloadPlugin"]);
try {
parseAndWalk(code, id, (node) => {
if (node.type === "ImportSpecifier" && node.imported.type === "Identifier" && (node.imported.name === "defineNuxtPlugin" || node.imported.name === "definePayloadPlugin")) {
wrapperNames.add(node.local.name);
}
if (node.type === "ExportDefaultDeclaration" && (node.declaration.type === "FunctionDeclaration" || node.declaration.type === "ArrowFunctionExpression")) {
if ("params" in node.declaration && node.declaration.params.length > 1) {
logger.warn(`Plugin \`${plugin.src}\` is in legacy Nuxt 2 format (context, inject) which is likely to be broken and will be ignored.`);
s.overwrite(0, code.length, "export default () => {}");
wrapped = true;
return;
}
}
if (node.type !== "CallExpression" || node.callee.type !== "Identifier") {
return;
}
const name = "name" in node.callee && node.callee.name;
if (!name || !wrapperNames.has(name)) {
return;
}
wrapped = true;
if (node.arguments[0] && node.arguments[0].type !== "ObjectExpression") {
if ("params" in node.arguments[0] && node.arguments[0].params.length > 1) {
logger.warn(`Plugin \`${plugin.src}\` is in legacy Nuxt 2 format (context, inject) which is likely to be broken and will be ignored.`);
s.overwrite(0, code.length, "export default () => {}");
return;
}
}
if (!("order" in plugin) && !("name" in plugin)) {
return;
}
for (const [argIndex, arg] of node.arguments.entries()) {
if (arg.type !== "ObjectExpression") {
continue;
}
for (const [propertyIndex, property] of arg.properties.entries()) {
if (property.type === "SpreadElement" || !("name" in property.key)) {
continue;
}
const propertyKey = property.key.name;
if (propertyKey === "order" || propertyKey === "enforce" || propertyKey === "name") {
const nextNode = arg.properties[propertyIndex + 1] || node.arguments[argIndex + 1];
const nextIndex = nextNode?.start || arg.end - 1;
s.remove(property.start, nextIndex);
}
}
}
});
} catch (e) {
logger.error(e);
return;
}
if (!wrapped) {
logger.warn(`Plugin \`${plugin.src}\` is not wrapped in \`defineNuxtPlugin\`. It is advised to wrap your plugins as in the future this may enable enhancements.`);
}
if (s.hasChanged()) {
return {
code: s.toString(),
map: nuxt.options.sourcemap.client || nuxt.options.sourcemap.server ? s.generateMap({ hires: true }) : null
};
}
}
};
});
const AsyncContextInjectionPlugin = (nuxt) => createUnplugin(() => {
return {
name: "nuxt:vue-async-context",
transformInclude(id) {
return isVue(id, { type: ["template", "script"] });
},
transform: {
filter: {
code: { include: /_withAsyncContext/ }
},
handler(code) {
const s = new MagicString(code);
s.prepend('import { withAsyncContext as _withAsyncContext } from "#app/composables/asyncContext";\n');
s.replace(/withAsyncContext as _withAsyncContext,?/, "");
if (s.hasChanged()) {
return {
code: s.toString(),
map: nuxt.options.sourcemap.client || nuxt.options.sourcemap.server ? s.generateMap({ hires: true }) : void 0
};
}
}
}
};
});
const stringTypes = ["Literal", "TemplateLiteral"];
const NUXT_LIB_RE = /node_modules\/(?:nuxt|nuxt3|nuxt-nightly)\//;
const SUPPORTED_EXT_RE = /\.(?:m?[jt]sx?|vue)/;
const SCRIPT_RE = /(?<=<script[^>]*>)[\s\S]*?(?=<\/script>)/i;
const ComposableKeysPlugin = (options) => createUnplugin(() => {
const composableMeta = {};
const composableLengths = /* @__PURE__ */ new Set();
const keyedFunctions = /* @__PURE__ */ new Set();
for (const { name, ...meta } of options.composables) {
composableMeta[name] = meta;
keyedFunctions.add(name);
composableLengths.add(meta.argumentLength);
}
const maxLength = Math.max(...composableLengths);
const KEYED_FUNCTIONS_RE = new RegExp(`\\b(${[...keyedFunctions].map((f) => escapeRE(f)).join("|")})\\b`);
return {
name: "nuxt:composable-keys",
enforce: "post",
transformInclude(id) {
const { pathname, search } = parseURL(decodeURIComponent(pathToFileURL(id).href));
return !NUXT_LIB_RE.test(pathname) && SUPPORTED_EXT_RE.test(pathname) && parseQuery(search).type !== "style" && !parseQuery(search).macro;
},
transform: {
filter: {
code: { include: KEYED_FUNCTIONS_RE }
},
handler(code, id) {
const { 0: script = code, index: codeIndex = 0 } = code.match(SCRIPT_RE) || { index: 0, 0: code };
const s = new MagicString(code);
let imports;
let count = 0;
const relativeID = isAbsolute(id) ? relative(options.rootDir, id) : id;
const { pathname: relativePathname } = parseURL(relativeID);
const scopeTracker = new ScopeTracker({
preserveExitedScopes: true
});
const parseResult = parseAndWalk(script, id, {
scopeTracker
});
scopeTracker.freeze();
walk(parseResult.program, {
scopeTracker,
enter(node) {
if (node.type !== "CallExpression" || node.callee.type !== "Identifier") {
return;
}
const name = node.callee.name;
if (!name || !keyedFunctions.has(name) || node.arguments.length >= maxLength) {
return;
}
imports ||= detectImportNames(script, composableMeta);
if (imports.has(name)) {
return;
}
const meta = composableMeta[name];
const declaration = scopeTracker.getDeclaration(name);
if (declaration && declaration.type !== "Import") {
let skip = true;
if (meta.source) {
skip = !matchWithStringOrRegex(relativePathname, meta.source);
}
if (skip) {
return;
}
}
if (node.arguments.length >= meta.argumentLength) {
return;
}
switch (name) {
case "useState":
if (stringTypes.includes(node.arguments[0]?.type)) {
return;
}
break;
case "useFetch":
case "useLazyFetch":
if (stringTypes.includes(node.arguments[1]?.type)) {
return;
}
break;
case "useAsyncData":
case "useLazyAsyncData":
if (stringTypes.includes(node.arguments[0]?.type) || stringTypes.includes(node.arguments[node.arguments.length - 1]?.type)) {
return;
}
break;
}
const newCode = code.slice(codeIndex + node.start, codeIndex + node.end - 1).trim();
const endsWithComma = newCode[newCode.length - 1] === ",";
s.appendLeft(
codeIndex + node.end - 1,
(node.arguments.length && !endsWithComma ? ", " : "") + "'$" + hash(`${relativeID}-${++count}`).slice(0, 10) + "'"
);
}
});
if (s.hasChanged()) {
return {
code: s.toString(),
map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
};
}
}
}
};
});
const NUXT_IMPORT_RE = /nuxt|#app|#imports/;
function detectImportNames(code, composableMeta) {
const names = /* @__PURE__ */ new Set();
function addName(name, specifier) {
const source = composableMeta[name]?.source;
if (source && matchWithStringOrRegex(specifier, source)) {
return;
}
names.add(name);
}
for (const i of findStaticImports(code)) {
if (NUXT_IMPORT_RE.test(i.specifier)) {
continue;
}
const { namedImports = {}, defaultImport, namespacedImport } = parseStaticImport(i);
for (const name in namedImports) {
addName(namedImports[name], i.specifier);
}
if (defaultImport) {
addName(defaultImport, i.specifier);
}
if (namespacedImport) {
addName(namespacedImport, i.specifier);
}
}
return names;
}
const VIRTUAL_RE = /^\0?virtual:(?:nuxt:)?/;
function ResolveDeepImportsPlugin(nuxt) {
const exclude = ["virtual:", "\0virtual:", "/__skip_vite", "@vitest/"];
let conditions;
return {
name: "nuxt:resolve-bare-imports",
enforce: "post",
configResolved(config) {
const resolvedConditions = /* @__PURE__ */ new Set([nuxt.options.dev ? "development" : "production", ...config.resolve.conditions]);
if (resolvedConditions.has("browser")) {
resolvedConditions.add("web");
resolvedConditions.add("import");
resolvedConditions.add("module");
resolvedConditions.add("default");
}
if (config.mode === "test") {
resolvedConditions.add("import");
resolvedConditions.add("require");
}
conditions = [...resolvedConditions];
},
async resolveId(id, importer) {
if (!importer || isAbsolute(id) || !isAbsolute(importer) && !VIRTUAL_RE.test(importer) || exclude.some((e) => id.startsWith(e))) {
return;
}
const normalisedId = resolveAlias$1(normalize(id), nuxt.options.alias);
const isNuxtTemplate = importer.startsWith("virtual:nuxt");
const normalisedImporter = (isNuxtTemplate ? decodeURIComponent(importer) : importer).replace(VIRTUAL_RE, "");
if (nuxt.options.experimental.templateImportResolution !== false && isNuxtTemplate) {
const template = nuxt.options.build.templates.find((t) => resolve(nuxt.options.buildDir, t.filename) === normalisedImporter);
if (template?._path) {
const res2 = await this.resolve?.(normalisedId, template._path, { skipSelf: true });
if (res2 !== void 0 && res2 !== null) {
return res2;
}
}
}
const dir = parseNodeModulePath(normalisedImporter).dir || pkgDir;
const res = await this.resolve?.(normalisedId, dir, { skipSelf: true });
if (res !== void 0 && res !== null) {
return res;
}
const path = resolveModulePath(id, {
from: [dir, ...nuxt.options.modulesDir].map((d) => directoryToURL(d)),
suffixes: ["", "index"],
conditions,
try: true
});
if (!path) {
logger.debug("Could not resolve id", id, importer);
return null;
}
return normalize(path);
}
};
}
const runtimeDependencies = [
// other deps
"devalue",
"klona",
// unjs ecosystem
"defu",
"ufo",
"h3",
"destr",
"consola",
"hookable",
"unctx",
"cookie-es",
"perfect-debounce",
"radix3",
"ohash",
"pathe",
"uncrypto"
];
function ResolveExternalsPlugin(nuxt) {
let external = /* @__PURE__ */ new Set();
return {
name: "nuxt:resolve-externals",
enforce: "pre",
async configResolved() {
if (!nuxt.options.dev) {
const runtimeNitroDependencies = await tryImportModule("nitropack/package.json", {
url: new URL(import.meta.url)
})?.then((r) => r?.dependencies ? Object.keys(r.dependencies) : []).catch(() => []) || [];
external = /* @__PURE__ */ new Set([
// explicit dependencies we use in our ssr renderer - these can be inlined (if necessary) in the nitro build
"unhead",
"@unhead/vue",
"@nuxt/devalue",
"rou3",
"unstorage",
// ensure we only have one version of vue if nitro is going to inline anyway
...nuxt._nitro.options.inlineDynamicImports ? ["vue", "@vue/server-renderer"] : [],
...runtimeDependencies,
// dependencies we might share with nitro - these can be inlined (if necessary) in the nitro build
...runtimeNitroDependencies
]);
}
},
async resolveId(id, importer) {
if (!external.has(id)) {
return;
}
const res = await this.resolve?.(id, importer, { skipSelf: true });
if (res !== void 0 && res !== null) {
if (res.id === id) {
res.id = resolveModulePath(res.id, {
try: true,
from: importer,
extensions: nuxt.options.extensions
}) || res.id;
}
return {
...res,
external: "absolute"
};
}
}
};
}
function transformAndMinify(input, options) {
const oxcOptions = tryUseNuxt()?.options.oxc;
const transformResult = transform("", input, { ...oxcOptions?.transform.options, ...options });
const minifyResult = minify("", transformResult.code, { compress: { target: oxcOptions?.transform.options.target || "esnext" } });
return {
...transformResult,
...minifyResult
};
}
function PrehydrateTransformPlugin(options = {}) {
return createUnplugin(() => ({
name: "nuxt:prehydrate-transform",
transformInclude(id) {
return isJS(id) || isVue(id, { type: ["script"] });
},
transform: {
filter: {
code: { include: /onPrehydrate\(/ }
},
handler(code, id) {
const s = new MagicString(code);
parseAndWalk(code, id, (node) => {
if (node.type !== "CallExpression" || node.callee.type !== "Identifier") {
return;
}
if (node.callee.name === "onPrehydrate") {
const callback = node.arguments[0];
if (!callback) {
return;
}
if (callback.type !== "ArrowFunctionExpression" && callback.type !== "FunctionExpression") {
return;
}
const needsAttr = callback.params.length > 0;
try {
const { code: result } = transformAndMinify(`forEach(${code.slice(callback.start, callback.end)})`, { lang: "ts" });
const cleaned = result.slice("forEach".length).replace(/;$/, "");
const args = [JSON.stringify(cleaned)];
if (needsAttr) {
args.push(JSON.stringify(hash(result).slice(0, 10)));
}
s.overwrite(callback.start, callback.end, args.join(", "));
} catch (e) {
console.error(`[nuxt] Could not transform onPrehydrate in \`${id}\`:`, e);
}
}
});
if (s.hasChanged()) {
return {
code: s.toString(),
map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
};
}
}
}
}));
}
const PREFIX = "virtual:nuxt:";
const PREFIX_RE = /^\/?virtual:nuxt:/;
const RELATIVE_ID_RE = /^\.{1,2}[\\/]/;
const VirtualFSPlugin = (nuxt, options) => createUnplugin((_, meta) => {
const extensions = ["", ...nuxt.options.extensions];
const alias = { ...nuxt.options.alias, ...options.alias };
const resolveWithExt = (id) => {
for (const suffix of ["", "." + options.mode]) {
for (const ext of extensions) {
const rId = id + suffix + ext;
if (rId in nuxt.vfs) {
return rId;
}
}
}
};
function resolveId(id, importer) {
id = resolveAlias$1(id, alias);
if (PREFIX_RE.test(id)) {
id = withoutPrefix(decodeURIComponent(id));
}
const search = id.match(QUERY_RE)?.[0] || "";
id = withoutQuery(id);
if (process.platform === "win32" && isAbsolute(id)) {
id = resolve(id);
}
const resolvedId = resolveWithExt(id);
if (resolvedId) {
return PREFIX + encodeURIComponent(resolvedId) + search;
}
if (importer && RELATIVE_ID_RE.test(id)) {
const path = resolve(dirname(withoutPrefix(decodeURIComponent(importer))), id);
const resolved = resolveWithExt(path);
if (resolved) {
return PREFIX + encodeURIComponent(resolved) + search;
}
}
}
return {
name: "nuxt:virtual",
resolveId: meta.framework === "vite" ? void 0 : { order: "pre", handler: resolveId },
vite: {
resolveId: {
order: "pre",
handler(id, importer) {
const res = resolveId(id, importer);
if (res) {
return res;
}
if (importer && PREFIX_RE.test(importer) && RELATIVE_ID_RE.test(id)) {
return this.resolve?.(id, withoutPrefix(decodeURIComponent(importer)), { skipSelf: true });
}
}
}
},
loadInclude(id) {
return PREFIX_RE.test(id) && withoutQuery(withoutPrefix(decodeURIComponent(id))) in nuxt.vfs;
},
load(id) {
const key = withoutQuery(withoutPrefix(decodeURIComponent(id)));
return {
code: nuxt.vfs[key] || "",
map: null
};
}
};
});
function withoutPrefix(id) {
return id.replace(PREFIX_RE, "");
}
const QUERY_RE = /\?.*$/;
function withoutQuery(id) {
return id.replace(QUERY_RE, "");
}
function createNuxt(options) {
const hooks = createHooks();
const { callHook, callHookParallel, callHookWith } = hooks;
hooks.callHook = (...args) => runWithNuxtContext(nuxt, () => callHook(...args));
hooks.callHookParallel = (...args) => runWithNuxtContext(nuxt, () => callHookParallel(...args));
hooks.callHookWith = (...args) => runWithNuxtContext(nuxt, () => callHookWith(...args));
const nuxt = {
__name: randomUUID(),
_version: version,
_asyncLocalStorageModule: options.experimental.debugModuleMutation ? new AsyncLocalStorage() : void 0,
hooks,
callHook: hooks.callHook,
addHooks: hooks.addHooks,
hook: hooks.hook,
ready: () => runWithNuxtContext(nuxt, () => initNuxt(nuxt)),
close: () => hooks.callHook("close", nuxt),
vfs: {},
apps: {},
runWithContext: (fn) => runWithNuxtContext(nuxt, fn),
options
};
if (options.experimental.debugModuleMutation) {
const proxiedOptions = /* @__PURE__ */ new WeakMap();
Object.defineProperty(nuxt, "options", {
get() {
const currentModule = nuxt._asyncLocalStorageModule.getStore();
if (!currentModule) {
return options;
}
if (proxiedOptions.has(currentModule)) {
return proxiedOptions.get(currentModule);
}
nuxt._debug ||= {};
nuxt._debug.moduleMutationRecords ||= [];
const proxied = onChange(options, (keys, newValue, previousValue, applyData) => {
if (newValue === previousValue && !applyData) {
return;
}
let value = applyData?.args ?? newValue;
if (Array.isArray(value)) {
value = [...value];
} else if (typeof value === "object") {
value = { ...value };
}
nuxt._debug.moduleMutationRecords.push({
module: currentModule,
keys,
target: "nuxt.options",
value,
timestamp: Date.now(),
method: applyData?.name
});
}, {
ignoreUnderscores: true,
ignoreSymbols: true,
pathAsArray: true
});
proxiedOptions.set(currentModule, proxied);
return proxied;
}
});
}
if (!nuxtCtx.tryUse()) {
nuxtCtx.set(nuxt);
nuxt.hook("close", () => {
nuxtCtx.unset();
});
}
hooks.hookOnce("close", () => {
hooks.removeAllHooks();
});
return nuxt;
}
const fallbackCompatibilityDate = "2024-04-03";
const nightlies = {
"nitropack": "nitropack-nightly",
"h3": "h3-nightly",
"nuxt": "nuxt-nightly",
"@nuxt/schema": "@nuxt/schema-nightly",
"@nuxt/kit": "@nuxt/kit-nightly"
};
const keyDependencies = [
"@nuxt/kit"
];
let warnedAboutCompatDate = false;
async function initNuxt(nuxt) {
const layerDirs = getLayerDirectories(nuxt);
for (const config of nuxt.options._layers.map((layer) => layer.config).reverse()) {
if (config.hooks) {
nuxt.hooks.addHooks(config.hooks);
}
}
nuxt.options.compatibilityDate = resolveCompatibilityDatesFromEnv(nuxt.options.compatibilityDate);
if (!nuxt.options.compatibilityDate.default) {
nuxt.options.compatibilityDate.default = fallbackCompatibilityDate;
if (nuxt.options.dev && hasTTY && !isCI && !nuxt.options.test && !warnedAboutCompatDate) {
warnedAboutCompatDate = true;
consola.warn(`We recommend adding \`compatibilityDate: '${formatDate("latest")}'\` to your \`nuxt.config\` file.
Using \`${fallbackCompatibilityDate}\` as fallback. More info at: ${colors.underline("https://nitro.build/deploy#compatibility-date")}`);
}
}
const layersDir = withTrailingSlash(resolve(nuxt.options.rootDir, "layers"));
nuxt.hook("builder:watch", (event, relativePath) => {
const path = resolve(nuxt.options.srcDir, relativePath);
if (event === "addDir" || event === "unlinkDir") {
if (path.startsWith(layersDir)) {
return nuxt.callHook("restart", { hard: true });
}
}
});
if (nuxt.options.typescript.builder !== false) {
const envMap = {
// defaults from `builder` based on package name
"@nuxt/rspack-builder": "@rspack/core/module",
"@nuxt/vite-builder": "vite/client",
"@nuxt/webpack-builder": "webpack/module",
// simpler overrides from `typescript.builder` for better DX
"rspack": "@rspack/core/module",
"vite": "vite/client",
"webpack": "webpack/module",
// default 'merged' builder environment for module authors
"shared": "@nuxt/schema/builder-env"
};
const overrideEnv = nuxt.options.typescript.builder && envMap[nuxt.options.typescript.builder];
const defaultEnv = typeof nuxt.options.builder === "string" ? envMap[nuxt.options.builder] : false;
const environmentTypes = overrideEnv || defaultEnv;
if (environmentTypes) {
nuxt.options.typescript.hoist.push(environmentTypes);
addTypeTemplate({
filename: "types/builder-env.d.ts",
getContents: () => genImport(environmentTypes)
});
}
}
const packageJSON = await readPackageJSON(nuxt.options.rootDir).catch(() => ({}));
nuxt._dependencies = /* @__PURE__ */ new Set([...Object.keys(packageJSON.dependencies || {}), ...Object.keys(packageJSON.devDependencies || {})]);
let paths;
nuxt.hook("nitro:config", async (nitroConfig) => {
paths ||= await resolveTypescriptPaths(nuxt);
nitroConfig.typescript = defu$1(nitroConfig.typescript, {
tsConfig: { compilerOptions: { paths: { ...paths } } }
});
});
nuxt.hook("prepare:types", async (opts) => {
opts.references.push({ types: "nuxt" });
opts.references.push({ path: resolve(nuxt.options.buildDir, "types/app-defaults.d.ts") });
opts.references.push({ path: resolve(nuxt.options.buildDir, "types/plugins.d.ts") });
if (nuxt.options.typescript.shim) {
opts.references.push({ path: resolve(nuxt.options.buildDir, "types/vue-shim.d.ts") });
}
opts.references.push({ path: resolve(nuxt.options.buildDir, "types/build.d.ts") });
opts.references.push({ path: resolve(nuxt.options.buildDir, "types/schema.d.ts") });
opts.references.push({ path: resolve(nuxt.options.buildDir, "types/app.config.d.ts") });
paths ||= await resolveTypescriptPaths(nuxt);
opts.tsConfig.compilerOptions = defu$1(opts.tsConfig.compilerOptions, { paths: { ...paths } });
for (const dirs of layerDirs) {
const declaration = join(dirs.root, "index.d.ts");
if (existsSync(declaration)) {
opts.references.push({ path: declaration });
}
}
});
if (nuxt.options.scripts) {
if (!nuxt.options._modules.some((m) => m === "@nuxt/scripts" || m === "@nuxt/scripts-nightly")) {
installNuxtModule("@nuxt/scripts");
}
}
addBuildPlugin(VirtualFSPlugin(nuxt, { mode: "server" }), { client: false });
addBuildPlugin(VirtualFSPlugin(nuxt, { mode: "client", alias: { "#internal/nitro": join(nuxt.options.buildDir, "nitro.client.mjs") } }), { server: false });
addBuildPlugin(RemovePluginMetadataPlugin(nuxt));
addBuildPlugin(ComposableKeysPlugin({
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
rootDir: nuxt.options.rootDir,
composables: nuxt.options.optimization.keyedComposables
}));
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))];
const sharedProtectionConfig = {
cwd: nuxt.options.rootDir,
include: sharedPatterns,
patterns: createImportProtectionPatterns(nuxt, { context: "shared" })
};
addVitePlugin(() => ImpoundPlugin.vite(sharedProtectionConfig), { server: false });
addWebpackPlugin(() => ImpoundPlugin.webpack(sharedProtectionConfig), { server: false });
const nuxtProtectionConfig = {
cwd: nuxt.options.rootDir,
// Exclude top-level resolutions by plugins
exclude: [relative(nuxt.options.rootDir, join(nuxt.options.srcDir, "index.html")), ...sharedPatterns],
patterns: createImportProtectionPatterns(nuxt, { context: "nuxt-app" })
};
addVitePlugin(() => Object.assign(ImpoundPlugin.vite({ ...nuxtProtectionConfig, error: false }), { name: "nuxt:import-protection" }), { client: false });
addVitePlugin(() => Object.assign(ImpoundPlugin.vite({ ...nuxtProtectionConfig, error: true }), { name: "nuxt:import-protection" }), { server: false });
addWebpackPlugin(() => ImpoundPlugin.webpack(nuxtProtectionConfig));
addVitePlugin(() => ResolveDeepImportsPlugin(nuxt), { client: false });
addVitePlugin(() => ResolveDeepImportsPlugin(nuxt), { server: false });
addVitePlugin(() => ResolveExternalsPlugin(nuxt), { client: false, prepend: true });
addBuildPlugin(PrehydrateTransformPlugin({ sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client }));
if (nuxt.options.experimental.localLayerAliases) {
addBuildPlugin(LayerAliasingPlugin({
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
dev: nuxt.options.dev,
root: nuxt.options.srcDir,
// skip top-level layer (user's project) as the aliases will already be correctly resolved
layers: nuxt.options._layers.slice(1)
}));
}
nuxt.hook("modules:done", () => {
addBuildPlugin(UnctxTransformPlugin({
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
transformerOptions: {
...nuxt.options.optimization.asyncTransforms,
helperModule: "unctx"
}
}));
if (Object.keys(nuxt.options.optimization.treeShake.composables.server).length) {
addBuildPlugin(TreeShakeComposablesPlugin({
sourcemap: !!nuxt.options.sourcemap.server,
composables: nuxt.options.optimization.treeShake.composables.server
}), { client: false });
}
if (Object.keys(nuxt.options.optimization.treeShake.composables.client).length) {
addBuildPlugin(TreeShakeComposablesPlugin({
sourcemap: !!nuxt.options.sourcemap.client,
composables: nuxt.options.optimization.treeShake.composables.client
}), { server: false });
}
});
if (!nuxt.options.dev) {
addBuildPlugin(DevOnlyPlugin({
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client
}));
}
if (nuxt.options.dev) {
addPlugin(resolve(nuxt.options.appDir, "plugins/check-if-layout-used"));
addPlugin(resolve(nuxt.options.appDir, "plugins/warn.dev.server"));
}
if (nuxt.options.dev && nuxt.options.features.devLogs) {
addPlugin(resolve(nuxt.options.appDir, "plugins/dev-server-logs"));
addServerPlugin(resolve(distDir, "core/runtime/nitro/plugins/dev-server-logs"));
nuxt.options.nitro = defu$1(nuxt.options.nitro, {
externals: {
inline: [/#internal\/dev-server-logs-options/]
},
virtual: {
"#internal/dev-server-logs-options": () => `export const rootDir = ${JSON.stringify(nuxt.options.rootDir)};`
}
});
}
if (nuxt.options.experimental.asyncContext) {
addBuildPlugin(AsyncContextInjectionPlugin(nuxt), { client: false });
}
if (nuxt.options.features.noScripts && !nuxt.options.dev) {
nuxt.hook("build:manifest", async (manifest) => {
for (const chunk of Object.values(manifest)) {
if (chunk.resourceType === "script") {
await rm(resolve(nuxt.options.buildDir, "dist/client", withoutLeadingSlash(nuxt.options.app.buildAssetsDir), chunk.file), { force: true });
chunk.file = "";
}
}
});
}
nuxt.options.build.transpile.push("nuxt/app");
nuxt.options.build.transpile.push(
...layerDirs.filter((i) => i.root.includes("node_modules")).map((i) => i.root.replace(/\/$/, ""))
);
const locallyScannedLayersDirs = layerDirs.map((l) => join(l.root, "layers/"));
const rootWithTrailingSlash = withTrailingSlash(nuxt.options.rootDir);
for (const dirs of layerDirs) {
if (dirs.root === rootWithTrailingSlash) {
continue;
}
if (locallyScannedLayersDirs.every((dir) => !dirs.root.startsWith(dir))) {
nuxt.options.modulesDir.push(join(dirs.root, "node_modules"));
}
}
await nuxt.callHook("modules:before");
const modulesToInstall = /* @__PURE__ */ new Map();
const watchedModulePaths = /* @__PURE__ */ new Set();
const specifiedModules = /* @__PURE__ */ new Set();
for (const _mod of nuxt.options.modules) {
const mod = Array.isArray(_mod) ? _mod[0] : _mod;
if (typeof mod !== "string") {
continue;
}
const modAlias = resolveAlias$1(mod);
const modPath = resolveModulePath(modAlias, {
try: true,
from: nuxt.options.modulesDir.map((m) => directoryToURL(m.replace(/\/node_modules\/?$/, "/"))),
suffixes: ["nuxt", "nuxt/index", "module", "module/index", "", "index"],
extensions: [".js", ".mjs", ".cjs", ".ts", ".mts", ".cts"]
});
specifiedModules.add(modPath || modAlias);
}
for (const config of nuxt.options._layers.map((layer) => layer.config).reverse()) {
const modulesDir = (config.rootDir === nuxt.options.rootDir ? nuxt.options.dir : config.dir)?.modules || "modules";
const layerModules = await resolveFiles(config.srcDir, [
`${modulesDir}/*{${nuxt.options.extensions.join(",")}}`,
`${modulesDir}/*/index{${nuxt.options.extensions.join(",")}}`
]);
for (const mod of layerModules) {
watchedModulePaths.add(mod);
if (specifiedModules.has(mod)) {
continue;
}
specifiedModules.add(mod);
modulesToInstall.set(mod, {});
}
}
nuxt.options.watch.push(...watchedModulePaths);
for (const key of ["modules", "_modules"]) {
for (const item of nuxt.options[key]) {
if (item) {
const [key2, options = {}] = Array.isArray(item) ? item : [item];
if (!modulesToInstall.has(key2)) {
modulesToInstall.set(key2, options);
}
}
}
}
const islandsConfig = nuxt.options.experimental.componentIslands;
if (nuxt.options.dev || !(typeof islandsConfig === "object" && islandsConfig.selectiveClient === "deep")) {
addComponent({
name: "NuxtWelcome",
priority: 10,
// built-in that we do not expect the user to override
filePath: resolve(nuxt.options.appDir, "components/welcome")
});
}
addComponent({
name: "NuxtLayout",
priority: 10,
// built-in that we do not expect the user to override
filePath: resolve(nuxt.options.appDir, "components/nuxt-layout")
});
addComponent({
name: "NuxtErrorBoundary",
priority: 10,
// built-in that we do not expect the user to override
filePath: resolve(nuxt.options.appDir, "components/nuxt-error-boundary")
});
addComponent({
name: "ClientOnly",
priority: 10,
// built-in that we do not expect the user to override
filePath: resolve(nuxt.options.appDir, "components/client-only")
});
addComponent({
name: "DevOnly",
priority: 10,
// built-in that we do not expect the user to override
filePath: resolve(nuxt.options.appDir, "components/dev-only")
});
addComponent({
name: "ServerPlaceholder",
priority: 10,
// built-in that we do not expect the user to override
filePath: resolve(nuxt.options.appDir, "components/server-placeholder")
});
addComponent({
name: "NuxtLink",
priority: 10,
// built-in that we do not expect the user to override
filePath: resolve(nuxt.options.appDir, "components/nuxt-link")
});
addComponent({
name: "NuxtLoadingIndicator",
priority: 10,
// built-in that we do not expect the user to override
filePath: resolve(nuxt.options.appDir, "components/nuxt-loading-indicator")
});
addComponent({
name: "NuxtTime",
priority: 10,
// built-in that we do not expect the user to override
filePath: resolve(nuxt.options.appDir, "components/nuxt-time.vue")
});
addComponent({
name: "NuxtRouteAnnouncer",
priority: 10,
// built-in that we do not expect the user to override
filePath: resolve(nuxt.options.appDir, "components/nuxt-route-announcer"),
mode: "client"
});
if (nuxt.options.experimental.clientFallback) {
addComponent({
name: "NuxtClientFallback",
_raw: true,
priority: 10,
// built-in that we do not expect the user to override
filePath: resolve(nuxt.options.appDir, "components/client-fallback.client"),
mode: "client"
});
addComponent({
name: "NuxtClientFallback",
_raw: true,
priority: 10,
// built-in that we do not expect the user to override
filePath: resolve(nuxt.options.appDir, "components/client-fallback.server"),
mode: "server"
});
}
for (const name of ["NuxtImg", "NuxtPicture"]) {
addComponent({
name,
export: name,
priority: -1,
filePath: resolve(nuxt.options.appDir, "components/nuxt-stubs"),
// @ts-expect-error TODO: refactor to @nuxt/cli
_internal_install: "@nuxt/image"
});
}
if (nuxt.options.builder === "@nuxt/webpack-builder") {
addPlugin(resolve(nuxt.options.appDir, "plugins/preload.server"));
}
if (nuxt.options.debug && nuxt.options.debug.hooks && (nuxt.options.debug.hooks === true || nuxt.options.debug.hooks.client)) {
addPlugin(resolve(nuxt.options.appDir, "plugins/debug-hooks"));
}
if (nuxt.options.experimental.browserDevtoolsTiming) {
addPlugin(resolve(nuxt.options.appDir, "plugins/browser-devtools-timing.client"));
}
await installModules(modulesToInstall, /* @__PURE__ */ new Set(), nuxt);
nuxt._ignore = ignore(nuxt.options.ignoreOptions);
nuxt._ignore.add(resolveIgnorePatterns());
await nuxt.callHook("modules:done");
if (nuxt.options.experimental.componentIslands) {
addComponent({
name: "NuxtIsland",
priority: 10,
// built-in that we do not expect the user to override
filePath: resolve(nuxt.options.appDir, "components/nuxt-island")
});
addServerTemplate({
filename: "#internal/nuxt/island-renderer.mjs",
getContents() {
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, "core/runtime/nitro/handlers/island")}'`;
}
return `import { defineEventHandler } from 'h3'; export default defineEventHandler(() => {});`;
}
});
addServerHandler({
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$1(nuxt.options.nitro.routeRules["/**"], { ssr: false });
}
}
if (!nuxt.options.dev && nuxt.options.experimental.payloadExtraction) {
addPlugin(resolve(nuxt.options.appDir, "plugins/payload.client"));
}
if (nuxt.options.experimental.crossOriginPrefetch) {
addPlugin(resolve(nuxt.options.appDir, "plugins/cross-origin-prefetch.client"));
}
if (nuxt.options.experimental.emitRouteChunkError === "automatic") {
addPlugin(resolve(nuxt.options.appDir, "plugins/chunk-reload.client"));
}
if (nuxt.options.experimental.emitRouteChunkError === "automatic-immediate") {
addPlugin(resolve(nuxt.options.appDir, "plugins/chunk-reload-immediate.client"));
}
if (nuxt.options.experimental.restoreState) {
addPlugin(resolve(nuxt.options.appDir, "plugins/restore-state.client"));
}
if (nuxt.options.experimental.viewTransition) {
addPlugin(resolve(nuxt.options.appDir, "plugins/view-transitions.client"));
}
if (nuxt.options.experimental.renderJsonPayloads) {
addPlugin(resolve(nuxt.options.appDir, "plugins/revive-payload.client"));
addPlugin(resolve(nuxt.options.appDir, "plugins/revive-payload.server"));
}
if (nuxt.options.experimental.appManifest) {
addRouteMiddleware({
name: "manifest-route-rule",
path: resolve(nuxt.options.appDir, "middleware/manifest-route-rule"),
global: true
});
if (nuxt.options.experimental.checkOutdatedBuildInterval !== false) {
addPlugin(resolve(nuxt.options.appDir, "plugins/check-outdated-build.client"));
}
}
if (nuxt.options.experimental.navigationRepaint) {
addPlugin({
src: resolve(nuxt.options.appDir, "plugins/navigation-repaint.client")
});
}
if (nuxt.options.vue.config && Object.values(nuxt.options.vue.config).some((v) => v !== null && v !== void 0)) {
addPluginTemplate({
filename: "vue-app-config.mjs",
getContents: () => `
import { defineNuxtPlugin } from '#app/nuxt'
export default defineNuxtPlugin({
name: 'nuxt:vue-app-config',
enforce: 'pre',
setup (nuxtApp) {
${Object.keys(nuxt.options.vue.config).map((k) => ` nuxtApp.vueApp.config[${JSON.stringify(k)}] = ${JSON.stringify(nuxt.options.vue.config[k])}`).join("\n")}
}
})`
});
}
nuxt.hooks.hook("builder:watch", (event, relativePath) => {
const path = resolve(nuxt.options.srcDir, relativePath);
if (watchedModulePaths.has(path)) {
return nuxt.callHook("restart", { hard: true });
}
const layerRelativePaths = new Set(getLayerDirectories(nuxt).map((l) => relative(l.app, path)));
for (const pattern of nuxt.options.watch) {
if (typeof pattern === "string") {
if (pattern === path || layerRelativePaths.has(pattern)) {
return nuxt.callHook("restart");
}
continue;
}
for (const p of layerRelativePaths) {
if (pattern.test(p)) {
return nuxt.callHook("restart");
}
}
}
if (event === "addDir" && path === resolve(nuxt.options.srcDir, "app")) {
logger.info(`\`${path}/\` ${event === "addDir" ? "created" : "removed"}`);
return nuxt.callHook("restart", { hard: true });
}
const isFileChange = ["add", "unlink"].includes(event);
if (isFileChange && RESTART_RE.test(path)) {
logger.info(`\`${path}\` ${event === "add" ? "created" : "removed"}`);
return nuxt.callHook("restart");
}
});
nuxt.options.build.transpile = nuxt.options.build.transpile.map((t) => typeof t === "string" ? normalize(t) : t);
addModuleTranspiles();
await initNitro(nuxt);
const nitro = useNitro();
if (nitro.options.static && nuxt.options.experimental.payloadExtraction === void 0) {
logger.warn("Using experimental payload extraction for full-static output. You can opt-out by setting `experimental.payloadExtraction` to `false`.");
nuxt.options.experimental.payloadExtraction = true;
}
nitro.options.replace["process.env.NUXT_PAYLOAD_EXTRACTION"] = String(!!nuxt.options.experimental.payloadExtraction);
nitro.options._config.replace["process.env.NUXT_PAYLOAD_EXTRACTION"] = String(!!nuxt.options.experimental.payloadExtraction);
if (!nuxt.options.dev && nuxt.options.experimental.payloadExtraction) {
addPlugin(resolve(nuxt.options.appDir, "plugins/payload.client"));
}
if (!satisfies(coerce(nuxt._version) ?? nuxt._version, nuxt.options.future.compatibilityVersion + ".x")) {
logger.info(`Running with compatibility version \`${nuxt.options.future.compatibilityVersion}\``);
}
await nuxt.callHook("ready", nuxt);
}
async function loadNuxt(opts) {
const options = await loadNuxtConfig(opts);
options.appDir = options.alias["#app"] = withTrailingSlash(resolve(distDir, "app"));
options._majorVersion = 3;
for (const key in options.app.head || {}) {
options.app.head[key] = deduplicateArray(options.app.head[key]);
}
const orderedCSS = /* @__PURE__ */ new Set();
const optionsCSS = new Set(options.css);
for (const config of options._layers.map((layer) => layer.config).reverse()) {
for (const style of config.css || []) {
if (typeof style === "string") {
orderedCSS.delete(style);
optionsCSS.delete(style);
orderedCSS.add(style);
}
}
}
options.css = [...orderedCSS, ...optionsCSS];
if (options.builder === "@nuxt/vite-builder") {
const isDevToolsEnabled = typeof options.devtools === "boolean" ? options.devtools : options.devtools?.enabled !== false;
if (isDevToolsEnabled) {
if (!options._modules.some((m) => m === "@nuxt/devtools" || m === "@nuxt/devtools-nightly" || m === "@nuxt/devtools-edge")) {
options._modules.push("@nuxt/devtools");
}
}
}
if (!options._modules.some((m) => m === "@nuxt/scripts" || m === "@nuxt/scripts-nightly")) {
options.imports = defu$1(options.imports, {
presets: [scriptsStubsPreset]
});
}
if (options.builder === "@nuxt/webpack-builder") {
if (!await Promise.resolve().then(function () { return features; }).then((r) => r.ensurePackageInstalled("@nuxt/webpack-builder", {
rootDir: options.rootDir,
searchPaths: options.modulesDir
}))) {
logger.warn("Failed to install `@nuxt/webpack-builder`, please install it manually, or change the `builder` option to vite in `nuxt.config`");
}
}
options._modules.push(pagesModule, metaModule, componentsModule);
options._modules.push([importsModule, {
transform: {
include: options._layers.filter((i) => i.cwd && i.cwd.includes("node_modules")).map((i) => new RegExp(`(^|\\/)${escapeRE(i.cwd.split("node_modules/").pop())}(\\/|$)(?!node_modules\\/)`))
}
}]);
options._modules.push(schemaModule);
options.modulesDir.push(resolve(options.workspaceDir, "node_modules"));
options.modulesDir.push(resolve(pkgDir, "node_modules"));
options.build.transpile.push(
"mocked-exports",
"std-env"
// we need to statically replace process.env when used in runtime code
);
options.alias["vue-demi"] = resolve(options.appDir, "compat/vue-demi");
options.alias["@vue/composition-api"] = resolve(options.appDir, "compat/capi");
if (options.telemetry !== false && !process.env.NUXT_TELEMETRY_DISABLED) {
options._modules.push("@nuxt/telemetry");
}
const allowedKeys = /* @__PURE__ */ new Set(["baseURL", "buildAssetsDir", "cdnURL", "buildId"]);
for (const key in options.runtimeConfig.app) {
if (!allowedKeys.has(key)) {
logger.warn(`The \`app\` namespace is reserved for Nuxt and is exposed to the browser. Please move \`runtimeConfig.app.${key}\` to a different namespace.`);
delete options.runtimeConfig.app[key];
}
}
createPortalProperties(options.nitro.runtimeConfig, options, ["nitro.runtimeConfig", "runtimeConfig"]);
createPortalProperties(options.nitro.routeRules, options, ["nitro.routeRules", "routeRules"]);
const nitroOptions = options.nitro;
Object.defineProperties(options, {
nitro: {
configurable: false,
enumerable: true,
get: () => nitroOptions,
set(value) {
Object.assign(nitroOptions, value);
}
}
});
const nuxt = createNuxt(options);
nuxt.runWithContext(() => {
if (nuxt.options.dev && !nuxt.options.test) {
nuxt.hooks.hookOnce("build:done", () => {
for (const dep of keyDependencies) {
checkDependencyVersion(dep, nuxt._version).catch((e) => logger.warn(`Problem checking \`${dep}\` version.`, e));
}
});
}
if (opts.overrides?.hooks) {
nuxt.hooks.addHooks(opts.overrides.hooks);
}
if (nuxt.options.debug && nuxt.options.debug.hooks && (nuxt.options.debug.hooks === true || nuxt.options.debug.hooks.server)) {
createDebugger(nuxt.hooks, { tag: "nuxt" });
}
});
if (opts.ready !== false) {
await nuxt.ready();
}
return nuxt;
}
async function checkDependencyVersion(name, nuxtVersion) {
const path = resolveModulePath(name, { try: true });
if (!path) {
return;
}
const { version: version2 } = await readPackageJSON(path);
if (version2 && gt(nuxtVersion, version2)) {
console.warn(`[nuxt] Expected \`${name}\` to be at least \`${nuxtVersion}\` but got \`${version2}\`. This might lead to unexpected behavior. Check your package.json or refresh your lockfile.`);
}
}
const RESTART_RE = /^(?:app|error|app\.config)\.(?:js|ts|mjs|jsx|tsx|vue)$/i;
function deduplicateArray(maybeArray) {
if (!Array.isArray(maybeArray)) {
return maybeArray;
}
const fresh = [];
const hashes = /* @__PURE__ */ new Set();
for (const item of maybeArray) {
const _hash = hash(item);
if (!hashes.has(_hash)) {
hashes.add(_hash);
fresh.push(item);
}
}
return fresh;
}
function createPortalProperties(sourceValue, options, paths) {
let sharedValue = sourceValue;
for (const path of paths) {
const segments = path.split(".");
const key = segments.pop();
let parent = options;
while (segments.length) {
const key2 = segments.shift();
parent = parent[key2] ||= {};
}
delete parent[key];
Object.defineProperties(parent, {
[key]: {
configurable: false,
enumerable: true,
get: () => sharedValue,
set(value) {
sharedValue = value;
}
}
});
}
}
const NESTED_PKG_RE = /^[^@]+\//;
async function resolveTypescriptPaths(nuxt) {
nuxt.options.typescript.hoist ||= [];
const paths = Object.fromEntries(await Promise.all(nuxt.options.typescript.hoist.map(async (pkg) => {
const [_pkg = pkg, _subpath] = NESTED_PKG_RE.test(pkg) ? pkg.split("/") : [pkg];
const subpath = _subpath ? "/" + _subpath : "";
if (nuxt._dependencies?.has(_pkg) && !(_pkg in nightlies)) {
return [];
}
if (_pkg in nightlies) {
const nightly = nightlies[_pkg];
const path2 = await resolveTypePath(nightly + subpath, subpath, nuxt.options.modulesDir);
if (path2) {
return [[pkg, [path2]], [nightly + subpath, [path2]]];
}
}
const path = await resolveTypePath(_pkg + subpath, subpath, nuxt.options.modulesDir);
if (path) {
return [[pkg, [path]]];
}
return [];
})).then((r) => r.flat()));
return paths;
}
function withTrailingSlash(dir) {
return dir.replace(/[^/]$/, "$&/");
}
const vueShim = {
filename: "types/vue-shim.d.ts",
getContents: ({ nuxt }) => {
if (!nuxt.options.typescript.shim) {
return "";
}
return [
"declare module '*.vue' {",
" import { DefineComponent } from 'vue'",
" const component: DefineComponent<{}, {}, any>",
" export default component",
"}"
].join("\n");
}
};
const appComponentTemplate = {
filename: "app-component.mjs",
getContents: (ctx) => genExport(ctx.app.mainComponent, ["default"])
};
const rootComponentTemplate = {
filename: "root-component.mjs",
// TODO: fix upstream in vite - this ensures that vite generates a module graph for islands
// but should not be necessary (and has a warmup performance cost). See https://github.com/nuxt/nuxt/pull/24584.
getContents: (ctx) => (ctx.nuxt.options.dev ? "import '#build/components.islands.mjs';\n" : "") + genExport(ctx.app.rootComponent, ["default"])
};
const errorComponentTemplate = {
filename: "error-component.mjs",
getContents: (ctx) => genExport(ctx.app.errorComponent, ["default"])
};
const testComponentWrapperTemplate = {
filename: "test-component-wrapper.mjs",
getContents: (ctx) => genExport(resolve(ctx.nuxt.options.appDir, "components/test-component-wrapper"), ["default"])
};
const cssTemplate = {
filename: "css.mjs",
getContents: (ctx) => ctx.nuxt.options.css.map((i) => genImport(i)).join("\n")
};
const PLUGIN_TEMPLATE_RE = /_(?:45|46|47)/g;
const clientPluginTemplate = {
filename: "plugins.client.mjs",
async getContents(ctx) {
const clientPlugins = await annotatePlugins(ctx.nuxt, ctx.app.plugins.filter((p) => !p.mode || p.mode !== "server"));
checkForCircularDependencies(clientPlugins);
const exports = [];
const imports = [];
for (const plugin of clientPlugins) {
const path = relative(ctx.nuxt.options.rootDir, plugin.src);
const variable = genSafeVariableName(filename(plugin.src) || path).replace(PLUGIN_TEMPLATE_RE, "_") + "_" + hash(path).replace(/-/g, "_");
exports.push(variable);
imports.push(genImport(plugin.src, variable));
}
return [
...imports,
`export default ${genArrayFromRaw(exports)}`
].join("\n");
}
};
const serverPluginTemplate = {
filename: "plugins.server.mjs",
async getContents(ctx) {
const serverPlugins = await annotatePlugins(ctx.nuxt, ctx.app.plugins.filter((p) => !p.mode || p.mode !== "client"));
checkForCircularDependencies(serverPlugins);
const exports = [];
const imports = [];
for (const plugin of serverPlugins) {
const path = relative(ctx.nuxt.options.rootDir, plugin.src);
const variable = genSafeVariableName(filename(plugin.src) || path).replace(PLUGIN_TEMPLATE_RE, "_") + "_" + hash(path).replace(/-/g, "_");
exports.push(variable);
imports.push(genImport(plugin.src, variable));
}
return [
...imports,
`export default ${genArrayFromRaw(exports)}`
].join("\n");
}
};
const appDefaults = {
filename: "types/app-defaults.d.ts",
getContents: (ctx) => {
const isV4 = ctx.nuxt.options.future.compatibilityVersion === 4;
return `
declare module 'nuxt/app/defaults' {
type DefaultAsyncDataErrorValue = ${ctx.nuxt.options.experimental.defaults.useAsyncData.errorValue}
type DefaultAsyncDataValue = ${ctx.nuxt.options.experimental.defaults.useAsyncData.value}
type DefaultErrorValue = ${isV4 ? "undefined" : "null"}
type DedupeOption = ${isV4 ? "'cancel' | 'defer'" : "boolean | 'cancel' | 'defer'"}
}`;
}
};
const TS_RE = /\.[cm]?tsx?$/;
const JS_LETTER_RE = /\.(?<letter>[cm])?jsx?$/;
const JS_RE = /\.[cm]jsx?$/;
const JS_CAPTURE_RE = /\.[cm](jsx?)$/;
const pluginsDeclaration = {
filename: "types/plugins.d.ts",
getContents: async ({ nuxt, app }) => {
const EXTENSION_RE2 = new RegExp(`(?<=\\w)(${nuxt.options.extensions.map((e) => escapeRE(e)).join("|")})$`, "g");
const typesDir = join(nuxt.options.buildDir, "types");
const tsImports = [];
const pluginNames = [];
function exists(path) {
return app.templates.some((t) => t.write && path === t.dst) || existsSync(path);
}
for (const plugin of await annotatePlugins(nuxt, app.plugins)) {
if (plugin.name) {
pluginNames.push(`'${plugin.name}'`);
}
const pluginPath = resolve(typesDir, plugin.src);
const relativePath = relative(typesDir, pluginPath);
const correspondingDeclaration = pluginPath.replace(JS_LETTER_RE, ".d.$<letter>ts");
if (correspondingDeclaration !== pluginPath && exists(correspondingDeclaration)) {
tsImports.push(relativePath);
continue;
}
const incorrectDeclaration = pluginPath.replace(JS_RE, ".d.ts");
if (incorrectDeclaration !== pluginPath && exists(incorrectDeclaration)) {
tsImports.push(relativePath.replace(JS_CAPTURE_RE, ".$1"));
continue;
}
if (exists(pluginPath)) {
if (TS_RE.test(pluginPath)) {
tsImports.push(relativePath.replace(EXTENSION_RE2, ""));
continue;
}
tsImports.push(relativePath);
}
}
return `// Generated by Nuxt'
import type { Plugin } from '#app'
type Decorate<T extends Record<string, any>> = { [K in keyof T as K extends string ? \`$\${K}\` : never]: T[K] }
type InjectionType<A extends Plugin> = A extends {default: Plugin<infer T>} ? Decorate<T> : unknown
type NuxtAppInjections =
${tsImports.map((p) => `InjectionType<typeof ${genDynamicImport(p, { wrapper: false })}>`).join(" &\n ")}
declare module '#app' {
interface NuxtApp extends NuxtAppInjections { }
interface NuxtAppLiterals {
pluginName: ${pluginNames.join(" | ")}
}
}
declare module 'vue' {
interface ComponentCustomProperties extends NuxtAppInjections { }
}
export { }
`;
}
};
const IMPORT_NAME_RE = /\.\w+$/;
const GIT_RE = /^git\+/;
const schemaTemplate = {
filename: "types/schema.d.ts",
getContents: async ({ nuxt }) => {
const relativeRoot = relative(resolve(nuxt.options.buildDir, "types"), nuxt.options.rootDir);
const getImportName = (name) => (name[0] === "." ? "./" + join(relativeRoot, name) : name).replace(IMPORT_NAME_RE, "");
const modules = [];
for (const m of nuxt.options._installedModules) {
if (!m.meta || !m.meta.configKey || !m.meta.name) {
continue;
}
if (m.meta.name.startsWith("nuxt:") || m.meta.name === "nuxt-config-schema") {
continue;
}
modules.push([genString(m.meta.configKey), getImportName(m.entryPath || m.meta.name), m]);
}
const privateRuntimeConfig = /* @__PURE__ */ Object.create(null);
for (const key in nuxt.options.runtimeConfig) {
if (key !== "public") {
privateRuntimeConfig[key] = nuxt.options.runtimeConfig[key];
}
}
const moduleOptionsInterface = (options) => [
...modules.flatMap(([configKey, importName, mod]) => {
let link;
if (!mod.meta?.rawPath) {
link = `https://www.npmjs.com/package/${importName}`;
}
if (typeof mod.meta?.docs === "string") {
link = mod.meta.docs;
} else if (mod.meta?.repository) {
if (typeof mod.meta.repository === "string") {
link = mod.meta.repository;
} else if (typeof mod.meta.repository === "object" && "url" in mod.meta.repository && typeof mod.meta.repository.url === "string") {
link = mod.meta.repository.url;
}
if (link) {
if (link.startsWith("git+")) {
link = link.replace(GIT_RE, "");
}
if (!link.startsWith("http")) {
link = "https://github.com/" + link;
}
}
}
return [
` /**`,
` * Configuration for \`${importName}\``,
...options.addJSDocTags && link ? [` * @see ${link}`] : [],
` */`,
` [${configKey}]${options.unresolved ? "?" : ""}: typeof ${genDynamicImport(importName, { wrapper: false })}.default extends NuxtModule<infer O, unknown, boolean> ? ${options.unresolved ? "Partial<O>" : "O"} : Record<string, any>`
];
}),
modules.length > 0 && options.unresolved ? ` modules?: (undefined | null | false | NuxtModule<any> | string | [NuxtModule | string, Record<string, any>] | ${modules.map(([configKey, importName, mod]) => `[${genString(mod.meta?.rawPath || importName)}, Exclude<NuxtConfig[${configKey}], boolean>]`).join(" | ")})[],` : ""
].filter(Boolean);
const moduleDependencies = modules.flatMap(([_configKey, importName]) => [
` [${genString(importName)}]?: ModuleDependencyMeta<typeof ${genDynamicImport(importName, { wrapper: false })}.default extends NuxtModule<infer O> ? O : Record<string, unknown>>`
]).join("\n");
return [
"import { NuxtModule, ModuleDependencyMeta, RuntimeConfig } from '@nuxt/schema'",
"declare module '@nuxt/schema' {",
" interface ModuleDependencies {",
moduleDependencies,
" }",
" interface NuxtOptions {",
...moduleOptionsInterface({ addJSDocTags: false, unresolved: false }),
" }",
" interface NuxtConfig {",
// TypeScript will duplicate the jsdoc tags if we augment it twice
// So here we only generate tags for `nuxt/schema`
...moduleOptionsInterface({ addJSDocTags: false, unresolved: true }),
" }",
"}",
"declare module 'nuxt/schema' {",
" interface ModuleDependencies {",
moduleDependencies,
" }",
" interface NuxtOptions {",
...moduleOptionsInterface({ addJSDocTags: true, unresolved: false }),
" }",
" interface NuxtConfig {",
...moduleOptionsInterface({ addJSDocTags: true, unresolved: true }),
" }",
generateTypes(
await resolveSchema(privateRuntimeConfig),
{
interfaceName: "RuntimeConfig",
addExport: false,
addDefaults: false,
allowExtraKeys: false,
indentation: 2
}
),
generateTypes(
await resolveSchema(nuxt.options.runtimeConfig.public),
{
interfaceName: "PublicRuntimeConfig",
addExport: false,
addDefaults: false,
allowExtraKeys: false,
indentation: 2
}
),
"}",
`declare module 'vue' {
interface ComponentCustomProperties {
$config: RuntimeConfig
}
}`
].join("\n");
}
};
const layoutTemplate = {
filename: "layouts.mjs",
getContents({ app }) {
const layoutsObject = genObjectFromRawEntries(Object.values(app.layouts).map(({ name, file }) => {
return [name, `defineAsyncComponent(${genDynamicImport(file, { interopDefault: true })})`];
}));
return [
`import { defineAsyncComponent } from 'vue'`,
`export default ${layoutsObject}`
].join("\n");
}
};
const middlewareTemplate = {
filename: "middleware.mjs",
getContents({ app }) {
const globalMiddleware = app.middleware.filter((mw) => mw.global);
const namedMiddleware = app.middleware.filter((mw) => !mw.global);
const namedMiddlewareObject = genObjectFromRawEntries(namedMiddleware.map((mw) => [mw.name, genDynamicImport(mw.path)]));
return [
...globalMiddleware.map((mw) => genImport(mw.path, genSafeVariableName(mw.name))),
`export const globalMiddleware = ${genArrayFromRaw(globalMiddleware.map((mw) => genSafeVariableName(mw.name)))}`,
`export const namedMiddleware = ${namedMiddlewareObject}`
].join("\n");
}
};
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 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>
}
}
`
);
}
};
const clientConfigTemplate = {
filename: "nitro.client.mjs",
getContents: ({ nuxt }) => {
const appId = JSON.stringify(nuxt.options.appId);
return [
"export const useRuntimeConfig = () => ",
(!nuxt.options.future.multiApp ? "window?.__NUXT__?.config || {}" : `window?.__NUXT__?.[${appId}]?.config || {}`) || {}
].join("\n");
}
};
const appConfigDeclarationTemplate = {
filename: "types/app.config.d.ts",
getContents({ app, nuxt }) {
const typesDir = join(nuxt.options.buildDir, "types");
const configPaths = app.configs.map((path) => relative(typesDir, path).replace(EXTENSION_RE, ""));
return `
import type { CustomAppConfig } from 'nuxt/schema'
import type { Defu } from 'defu'
${configPaths.map((id, index) => `import ${`cfg${index}`} from ${JSON.stringify(id)}`).join("\n")}
declare const inlineConfig = ${JSON.stringify(nuxt.options.appConfig, null, 2)}
type ResolvedAppConfig = Defu<typeof inlineConfig, [${app.configs.map((_id, index) => `typeof cfg${index}`).join(", ")}]>
type IsAny<T> = 0 extends 1 & T ? true : false
type MergedAppConfig<Resolved extends Record<string, unknown>, Custom extends Record<string, unknown>> = {
[K in keyof (Resolved & Custom)]: K extends keyof Custom
? unknown extends Custom[K]
? Resolved[K]
: IsAny<Custom[K]> extends true
? Resolved[K]
: Custom[K] extends Record<string, any>
? Resolved[K] extends Record<string, any>
? MergedAppConfig<Resolved[K], Custom[K]>
: Exclude<Custom[K], undefined>
: Exclude<Custom[K], undefined>
: Resolved[K]
}
declare module 'nuxt/schema' {
interface AppConfig extends MergedAppConfig<ResolvedAppConfig, CustomAppConfig> { }
}
declare module '@nuxt/schema' {
interface AppConfig extends MergedAppConfig<ResolvedAppConfig, CustomAppConfig> { }
}
`;
}
};
const appConfigTemplate = {
filename: "app.config.mjs",
write: true,
getContents({ app, nuxt }) {
return `
import { _replaceAppConfig } from '#app/config'
import { defuFn } from 'defu'
const inlineConfig = ${JSON.stringify(nuxt.options.appConfig, null, 2)}
// Vite - webpack is handled directly in #app/config
if (import.meta.hot) {
import.meta.hot.accept((newModule) => {
_replaceAppConfig(newModule.default)
})
}
${app.configs.map((id, index) => `import ${`cfg${index}`} from ${JSON.stringify(id)}`).join("\n")}
export default /*@__PURE__*/ defuFn(${app.configs.map((_id, index) => `cfg${index}`).concat(["inlineConfig"]).join(", ")})
`;
}
};
const publicPathTemplate = {
filename: "paths.mjs",
getContents({ nuxt }) {
return [
"import { joinRelativeURL } from 'ufo'",
!nuxt.options.dev && "import { useRuntimeConfig } from '#internal/nitro'",
nuxt.options.dev ? `const getAppConfig = () => (${JSON.stringify(nuxt.options.app)})` : "const getAppConfig = () => useRuntimeConfig().app",
"export const baseURL = () => getAppConfig().baseURL",
"export const buildAssetsDir = () => getAppConfig().buildAssetsDir",
"export const buildAssetsURL = (...path) => joinRelativeURL(publicAssetsURL(), buildAssetsDir(), ...path)",
"export const publicAssetsURL = (...path) => {",
" const appConfig = getAppConfig()",
" const publicBase = appConfig.cdnURL || appConfig.baseURL",
" return path.length ? joinRelativeURL(publicBase, ...path) : publicBase",
"}",
// On server these are registered directly in packages/nuxt/src/core/runtime/nitro/handlers/renderer.ts
"if (import.meta.client) {",
" globalThis.__buildAssetsURL = buildAssetsURL",
" globalThis.__publicAssetsURL = publicAssetsURL",
"}"
].filter(Boolean).join("\n");
}
};
const globalPolyfillsTemplate = {
filename: "global-polyfills.mjs",
getContents() {
return `
if (!("global" in globalThis)) {
globalThis.global = globalThis;
}`;
}
};
const dollarFetchTemplate = {
filename: "fetch.mjs",
getContents() {
return [
"import { $fetch } from 'ofetch'",
"import { baseURL } from '#internal/nuxt/paths'",
"if (!globalThis.$fetch) {",
" globalThis.$fetch = $fetch.create({",
" baseURL: baseURL()",
" })",
"}"
].join("\n");
}
};
const nuxtConfigTemplate = {
filename: "nuxt.config.mjs",
getContents: (ctx) => {
const fetchDefaults = {
...ctx.nuxt.options.experimental.defaults.useFetch,
baseURL: void 0,
headers: void 0
};
const shouldEnableComponentIslands = ctx.nuxt.options.experimental.componentIslands && (ctx.nuxt.options.dev || ctx.nuxt.options.experimental.componentIslands !== "auto" || ctx.app.pages?.some((p) => p.mode === "server") || ctx.app.components?.some((c) => c.mode === "server" && !ctx.app.components.some((other) => other.pascalName === c.pascalName && other.mode === "client")));
return [
...Object.entries(ctx.nuxt.options.app).map(([k, v]) => `export const ${camelCase("app-" + k)} = ${JSON.stringify(v)}`),
`export const renderJsonPayloads = ${!!ctx.nuxt.options.experimental.renderJsonPayloads}`,
`export const componentIslands = ${shouldEnableComponentIslands}`,
`export const payloadExtraction = ${!!ctx.nuxt.options.experimental.payloadExtraction}`,
`export const cookieStore = ${!!ctx.nuxt.options.experimental.cookieStore}`,
`export const appManifest = ${!!ctx.nuxt.options.experimental.appManifest}`,
`export const remoteComponentIslands = ${typeof ctx.nuxt.options.experimental.componentIslands === "object" && ctx.nuxt.options.experimental.componentIslands.remoteIsland}`,
`export const selectiveClient = ${typeof ctx.nuxt.options.experimental.componentIslands === "object" && Boolean(ctx.nuxt.options.experimental.componentIslands.selectiveClient)}`,
`export const devPagesDir = ${ctx.nuxt.options.dev ? JSON.stringify(ctx.nuxt.options.dir.pages) : "null"}`,
`export const devRootDir = ${ctx.nuxt.options.dev ? JSON.stringify(ctx.nuxt.options.rootDir) : "null"}`,
`export const devLogs = ${JSON.stringify(ctx.nuxt.options.features.devLogs)}`,
`export const nuxtLinkDefaults = ${JSON.stringify(ctx.nuxt.options.experimental.defaults.nuxtLink)}`,
`export const asyncDataDefaults = ${JSON.stringify({
...ctx.nuxt.options.experimental.defaults.useAsyncData,
value: ctx.nuxt.options.experimental.defaults.useAsyncData.value === "null" ? null : void 0,
errorValue: ctx.nuxt.options.experimental.defaults.useAsyncData.errorValue === "null" ? null : void 0
})}`,
`export const resetAsyncDataToUndefined = ${ctx.nuxt.options.experimental.resetAsyncDataToUndefined}`,
`export const nuxtDefaultErrorValue = ${ctx.nuxt.options.future.compatibilityVersion === 4 ? "undefined" : "null"}`,
`export const fetchDefaults = ${JSON.stringify(fetchDefaults)}`,
`export const vueAppRootContainer = ${ctx.nuxt.options.app.rootAttrs.id ? `'#${ctx.nuxt.options.app.rootAttrs.id}'` : `'body > ${ctx.nuxt.options.app.rootTag}'`}`,
`export const viewTransition = ${ctx.nuxt.options.experimental.viewTransition}`,
`export const appId = ${JSON.stringify(ctx.nuxt.options.appId)}`,
`export const outdatedBuildInterval = ${ctx.nuxt.options.experimental.checkOutdatedBuildInterval}`,
`export const multiApp = ${!!ctx.nuxt.options.future.multiApp}`,
`export const chunkErrorEvent = ${ctx.nuxt.options.experimental.emitRouteChunkError ? ctx.nuxt.options.builder === "@nuxt/vite-builder" ? '"vite:preloadError"' : '"nuxt:preloadError"' : "false"}`,
`export const crawlLinks = ${!!ctx.nuxt._nitro.options.prerender.crawlLinks}`,
`export const spaLoadingTemplateOutside = ${ctx.nuxt.options.experimental.spaLoadingTemplateLocation === "body"}`,
`export const purgeCachedData = ${!!ctx.nuxt.options.experimental.purgeCachedData}`,
`export const granularCachedData = ${!!ctx.nuxt.options.experimental.granularCachedData}`,
`export const pendingWhenIdle = ${!!ctx.nuxt.options.experimental.pendingWhenIdle}`,
`export const alwaysRunFetchOnKeyChange = ${!!ctx.nuxt.options.experimental.alwaysRunFetchOnKeyChange}`
].join("\n\n");
}
};
const TYPE_FILENAME_RE = /\.([cm])?[jt]s$/;
const DECLARATION_RE = /\.d\.[cm]?ts$/;
const buildTypeTemplate = {
filename: "types/build.d.ts",
getContents({ app }) {
let declarations = "";
for (const file of app.templates) {
if (file.write || !file.filename || DECLARATION_RE.test(file.filename)) {
continue;
}
if (TYPE_FILENAME_RE.test(file.filename)) {
const typeFilenames = /* @__PURE__ */ new Set([file.filename.replace(TYPE_FILENAME_RE, ".d.$1ts"), file.filename.replace(TYPE_FILENAME_RE, ".d.ts")]);
if (app.templates.some((f) => f.filename && typeFilenames.has(f.filename))) {
continue;
}
}
declarations += "declare module " + JSON.stringify(join("#build", file.filename)) + ";\n";
}
return declarations;
}
};
const defaultTemplates = {
__proto__: null,
appComponentTemplate: appComponentTemplate,
appConfigDeclarationTemplate: appConfigDeclarationTemplate,
appConfigTemplate: appConfigTemplate,
appDefaults: appDefaults,
buildTypeTemplate: buildTypeTemplate,
clientConfigTemplate: clientConfigTemplate,
clientPluginTemplate: clientPluginTemplate,
cssTemplate: cssTemplate,
dollarFetchTemplate: dollarFetchTemplate,
errorComponentTemplate: errorComponentTemplate,
globalPolyfillsTemplate: globalPolyfillsTemplate,
layoutTemplate: layoutTemplate,
middlewareTemplate: middlewareTemplate,
nitroSchemaTemplate: nitroSchemaTemplate,
nuxtConfigTemplate: nuxtConfigTemplate,
pluginsDeclaration: pluginsDeclaration,
publicPathTemplate: publicPathTemplate,
rootComponentTemplate: rootComponentTemplate,
schemaTemplate: schemaTemplate,
serverPluginTemplate: serverPluginTemplate,
testComponentWrapperTemplate: testComponentWrapperTemplate,
vueShim: vueShim
};
function createApp(nuxt, options = {}) {
return defu(options, {
dir: nuxt.options.srcDir,
extensions: nuxt.options.extensions,
plugins: [],
components: [],
templates: []
});
}
const postTemplates = /* @__PURE__ */ new Set([
clientPluginTemplate.filename,
serverPluginTemplate.filename,
pluginsDeclaration.filename
]);
async function generateApp(nuxt, app, options = {}) {
await resolveApp(nuxt, app);
app.templates = Object.values(defaultTemplates).concat(nuxt.options.build.templates);
await nuxt.callHook("app:templates", app);
app.templates = app.templates.map((tmpl) => normalizeTemplate(tmpl, nuxt.options.buildDir));
const filteredTemplates = {
pre: [],
post: []
};
for (const template of app.templates) {
if (options.filter && !options.filter(template)) {
continue;
}
const key = template.filename && postTemplates.has(template.filename) ? "post" : "pre";
filteredTemplates[key].push(template);
}
const templateContext = { utils: templateUtils, nuxt, app };
const compileTemplate$1 = nuxt.options.experimental.compileTemplate ? compileTemplate : futureCompileTemplate;
const writes = [];
const dirs = /* @__PURE__ */ new Set();
const changedTemplates = [];
const FORWARD_SLASH_RE = /\//g;
async function processTemplate(template) {
const fullPath = template.dst || resolve(nuxt.options.buildDir, template.filename);
const start = performance.now();
const oldContents = nuxt.vfs[fullPath];
const contents = await compileTemplate$1(template, templateContext).catch((e) => {
logger.error(`Could not compile template \`${template.filename}\`.`);
logger.error(e);
throw e;
});
template.modified = oldContents !== contents;
if (template.modified) {
nuxt.vfs[fullPath] = contents;
const aliasPath = "#build/" + template.filename;
nuxt.vfs[aliasPath] = contents;
if (process.platform === "win32") {
nuxt.vfs[fullPath.replace(FORWARD_SLASH_RE, "\\")] = contents;
}
changedTemplates.push(template);
}
const perf = performance.now() - start;
const setupTime = Math.round(perf * 100) / 100;
if (nuxt.options.debug && nuxt.options.debug.templates || setupTime > 500) {
logger.info(`Compiled \`${template.filename}\` in ${setupTime}ms`);
}
if (template.modified && template.write) {
dirs.add(dirname(fullPath));
writes.push(() => writeFileSync(fullPath, contents, "utf8"));
}
}
await Promise.allSettled(filteredTemplates.pre.map(processTemplate));
await Promise.allSettled(filteredTemplates.post.map(processTemplate));
for (const dir of dirs) {
mkdirSync(dir, { recursive: true });
}
for (const write of writes) {
write();
}
if (changedTemplates.length) {
await nuxt.callHook("app:templatesGenerated", app, changedTemplates, options);
}
}
async function futureCompileTemplate(template, ctx) {
delete ctx.utils;
if (template.src) {
try {
return await promises.readFile(template.src, "utf-8");
} catch (err) {
logger.error(`[nuxt] Error reading template from \`${template.src}\``);
throw err;
}
}
if (template.getContents) {
return template.getContents({ ...ctx, options: template.options });
}
throw new Error("[nuxt] Invalid template. Templates must have either `src` or `getContents`: " + JSON.stringify(template));
}
async function resolveApp(nuxt, app) {
const layerDirs = getLayerDirectories(nuxt);
const reversedLayerDirs = [...layerDirs].reverse();
app.mainComponent ||= await findPath(layerDirs.flatMap((d) => [join(d.app, "App"), join(d.app, "app")]));
app.mainComponent ||= resolve(nuxt.options.appDir, "components/welcome.vue");
app.rootComponent ||= await findPath(["~/app.root", resolve(nuxt.options.appDir, "components/nuxt-root.vue")]);
app.errorComponent ||= await findPath(layerDirs.map((d) => join(d.app, "error"))) ?? resolve(nuxt.options.appDir, "components/nuxt-error-page.vue");
const extensionGlob = nuxt.options.extensions.join(",");
const layouts = {};
for (const dirs of layerDirs) {
const layoutFiles = await resolveFiles(dirs.appLayouts, `**/*{${extensionGlob}}`);
for (const file of layoutFiles) {
const name = getNameFromPath(file, dirs.appLayouts);
if (!name) {
logger.warn(`No layout name could be resolved for \`${resolveToAlias(file, nuxt)}\`. Bear in mind that \`index\` is ignored for the purpose of creating a layout name.`);
continue;
}
layouts[name] ||= { name, file };
}
}
let middleware = [];
for (const dirs of reversedLayerDirs) {
const middlewareFiles = await resolveFiles(dirs.appMiddleware, [
`*{${extensionGlob}}`,
...nuxt.options.future.compatibilityVersion === 4 ? [`*/index{${extensionGlob}}`] : []
]);
for (const file of middlewareFiles) {
const name = getNameFromPath(file);
if (!name) {
logger.warn(`No middleware name could be resolved for \`${resolveToAlias(file, nuxt)}\`. Bear in mind that \`index\` is ignored for the purpose of creating a middleware name.`);
continue;
}
middleware.push({ name, path: file, global: hasSuffix(file, ".global") });
}
}
const reversedLayers = nuxt.options._layers.slice().reverse();
let plugins = [];
for (let i = 0; i < reversedLayerDirs.length; i++) {
const config = reversedLayers[i].config;
const dirs = reversedLayerDirs[i];
plugins.push(...[
...config.plugins || [],
...await resolveFiles(dirs.appPlugins, [
`*{${extensionGlob}}`,
`*/index{${extensionGlob}}`
])
].map((plugin) => normalizePlugin(plugin)));
}
for (const p of [...nuxt.options.plugins].reverse()) {
const plugin = normalizePlugin(p);
if (!plugins.some((p2) => p2.src === plugin.src)) {
plugins.unshift(plugin);
}
}
middleware = uniqueBy(await resolvePaths(nuxt, [...middleware].reverse(), "path"), "name").reverse();
plugins = uniqueBy(await resolvePaths(nuxt, plugins, "src"), "src");
const configs = [];
for (const dirs of layerDirs) {
const appConfigPath = await findPath(join(dirs.app, "app.config"));
if (appConfigPath) {
configs.push(appConfigPath);
}
}
Object.assign(app, { middleware, plugins, configs, layouts });
await nuxt.callHook("app:resolve", app);
app.middleware = uniqueBy(await resolvePaths(nuxt, app.middleware, "path"), "name");
app.plugins = uniqueBy(await resolvePaths(nuxt, app.plugins, "src"), "src");
app.configs = [...new Set(app.configs)];
}
function resolvePaths(nuxt, items, key) {
return Promise.all(items.map(async (item) => {
if (!item[key]) {
return item;
}
return {
...item,
[key]: await resolvePath(item[key], {
alias: nuxt.options.alias,
extensions: nuxt.options.extensions,
fallbackToOriginal: true,
virtual: true
})
};
}));
}
const IS_TSX = /\.[jt]sx$/;
async function annotatePlugins(nuxt, plugins) {
const _plugins = [];
for (const plugin of plugins) {
try {
const code = plugin.src in nuxt.vfs ? nuxt.vfs[plugin.src] : await promises.readFile(plugin.src, "utf-8");
_plugins.push({
...await extractMetadata(code, IS_TSX.test(plugin.src) ? "tsx" : "ts"),
...plugin
});
} catch (e) {
const relativePluginSrc = relative(nuxt.options.rootDir, plugin.src);
if (e.message === "Invalid plugin metadata") {
logger.warn(`Failed to parse static properties from plugin \`${relativePluginSrc}\`, falling back to non-optimized runtime meta. Learn more: https://nuxt.com/docs/guide/directory-structure/plugins#object-syntax-plugins`);
} else {
logger.warn(`Failed to parse static properties from plugin \`${relativePluginSrc}\`.`, e);
}
_plugins.push(plugin);
}
}
return _plugins.sort((a, b) => (a.order ?? orderMap.default) - (b.order ?? orderMap.default));
}
function checkForCircularDependencies(_plugins) {
const deps = /* @__PURE__ */ Object.create(null);
const pluginNames = new Set(_plugins.map((plugin) => plugin.name));
for (const plugin of _plugins) {
if (plugin.dependsOn && plugin.dependsOn.some((name) => !pluginNames.has(name))) {
console.error(`Plugin \`${plugin.name}\` depends on \`${plugin.dependsOn.filter((name) => !pluginNames.has(name)).join(", ")}\` but they are not registered.`);
}
if (plugin.name) {
deps[plugin.name] = plugin.dependsOn || [];
}
}
const checkDeps = (name, visited = []) => {
if (visited.includes(name)) {
console.error(`Circular dependency detected in plugins: ${visited.join(" -> ")} -> ${name}`);
return [];
}
visited.push(name);
return deps[name]?.length ? deps[name].flatMap((dep) => checkDeps(dep, [...visited])) : [];
};
for (const name in deps) {
checkDeps(name);
}
}
async function checkForExternalConfigurationFiles() {
const checkResults = await Promise.all([checkViteConfig(), checkWebpackConfig(), checkNitroConfig(), checkPostCSSConfig()]);
const warningMessages = checkResults.filter(Boolean);
if (!warningMessages.length) {
return;
}
const foundOneExternalConfig = warningMessages.length === 1;
if (foundOneExternalConfig) {
logger.warn(warningMessages[0]);
} else {
const warningsAsList = warningMessages.map((message) => `- ${message}`).join("\n");
const warning = `Found multiple external configuration files:
${warningsAsList}`;
logger.warn(warning);
}
}
async function checkViteConfig() {
return await checkAndWarnAboutConfigFileExistence({
fileName: "vite.config",
extensions: [".js", ".mjs", ".ts", ".cjs", ".mts", ".cts"],
createWarningMessage: (foundFile) => `Using \`${foundFile}\` is not supported together with Nuxt. Use \`options.vite\` instead. You can read more in \`https://nuxt.com/docs/api/nuxt-config#vite\`.`
});
}
async function checkWebpackConfig() {
return await checkAndWarnAboutConfigFileExistence({
fileName: "webpack.config",
extensions: [".js", ".mjs", ".ts", ".cjs", ".mts", ".cts", "coffee"],
createWarningMessage: (foundFile) => `Using \`${foundFile}\` is not supported together with Nuxt. Use \`options.webpack\` instead. You can read more in \`https://nuxt.com/docs/api/nuxt-config#webpack-1\`.`
});
}
async function checkNitroConfig() {
return await checkAndWarnAboutConfigFileExistence({
fileName: "nitro.config",
extensions: [".ts", ".mts"],
createWarningMessage: (foundFile) => `Using \`${foundFile}\` is not supported together with Nuxt. Use \`options.nitro\` instead. You can read more in \`https://nuxt.com/docs/api/nuxt-config#nitro\`.`
});
}
async function checkPostCSSConfig() {
return await checkAndWarnAboutConfigFileExistence({
fileName: "postcss.config",
extensions: [".js", ".cjs"],
createWarningMessage: (foundFile) => `Using \`${foundFile}\` is not supported together with Nuxt. Use \`options.postcss\` instead. You can read more in \`https://nuxt.com/docs/api/nuxt-config#postcss\`.`
});
}
async function checkAndWarnAboutConfigFileExistence(options) {
const { fileName, extensions, createWarningMessage } = options;
const configFile = await findPath(fileName, { extensions }).catch(() => null);
if (configFile) {
return createWarningMessage(basename(configFile));
}
}
async function getVueHash(nuxt) {
const id = "vue";
const { hash: hash2 } = await getHashes(nuxt, {
id,
cwd: (layer) => layer.config.srcDir || layer.cwd,
patterns: (layer) => {
const srcDir = layer.config.srcDir || layer.cwd;
return [
"**",
`!${relative(srcDir, layer.config.serverDir || join(layer.cwd, "server"))}/**`,
`!${relative(srcDir, resolve$1(layer.cwd, layer.config.dir?.public || "public"))}/**`,
// eslint-disable-next-line @typescript-eslint/no-deprecated
`!${relative(srcDir, resolve$1(layer.cwd, layer.config.dir?.static || "public"))}/**`,
"!node_modules/**",
"!nuxt.config.*"
];
},
configOverrides: {
buildId: void 0,
serverDir: void 0,
nitro: void 0,
devServer: void 0,
runtimeConfig: void 0,
logLevel: void 0,
devServerHandlers: void 0,
generate: void 0,
devtools: void 0
}
});
const cacheFile = join(getCacheDir(nuxt), id, hash2 + ".tar");
return {
hash: hash2,
async collectCache() {
const start = Date.now();
await writeCache(nuxt.options.buildDir, nuxt.options.buildDir, cacheFile);
const elapsed = Date.now() - start;
consola$1.success(`Cached Vue client and server builds in \`${elapsed}ms\`.`);
},
async restoreCache() {
const start = Date.now();
const res = await restoreCache(nuxt.options.buildDir, cacheFile);
const elapsed = Date.now() - start;
if (res) {
consola$1.success(`Restored Vue client and server builds from cache in \`${elapsed}ms\`.`);
}
return res;
}
};
}
async function cleanupCaches(nuxt) {
const start = Date.now();
const caches = await glob(["*/*.tar"], {
cwd: getCacheDir(nuxt),
absolute: true
});
if (caches.length >= 10) {
const cachesWithMeta = await Promise.all(caches.map(async (cache) => {
return [cache, await stat(cache).then((r) => r.mtime.getTime()).catch(() => 0)];
}));
cachesWithMeta.sort((a, b) => a[1] - b[1]);
for (const [cache] of cachesWithMeta.slice(0, cachesWithMeta.length - 10)) {
await unlink(cache);
}
const elapsed = Date.now() - start;
consola$1.success(`Cleaned up old build caches in \`${elapsed}ms\`.`);
}
}
async function getHashes(nuxt, options) {
if (nuxt[`_${options.id}BuildHash`]) {
return nuxt[`_${options.id}BuildHash`];
}
const start = Date.now();
const hashSources = [];
let layerCtr = 0;
for (const layer of nuxt.options._layers) {
if (layer.cwd.includes("node_modules")) {
continue;
}
const layerName = `layer#${layerCtr++}`;
hashSources.push({
name: `${layerName}:config`,
data: serialize({
...layer.config,
...options.configOverrides || {}
})
});
const normalizeFiles = (files) => files.map((f) => ({
name: f.name,
size: f.attrs?.size,
data: hash(f.data)
})).sort((a, b) => a.name.localeCompare(b.name));
const isIgnored = createIsIgnored(nuxt);
const sourceFiles = await readFilesRecursive(options.cwd(layer), {
shouldIgnore: isIgnored,
// TODO: Validate if works with absolute paths
cwd: nuxt.options.rootDir,
patterns: options.patterns(layer)
});
hashSources.push({
name: `${layerName}:src`,
data: normalizeFiles(sourceFiles)
});
const rootFiles = await readFilesRecursive(layer.config?.rootDir || layer.cwd, {
shouldIgnore: isIgnored,
// TODO: Validate if works with absolute paths
cwd: nuxt.options.rootDir,
patterns: [
".nuxtrc",
".npmrc",
"package.json",
"package-lock.json",
"yarn.lock",
"pnpm-lock.yaml",
"tsconfig.json",
"bun.lock",
"bun.lockb"
]
});
hashSources.push({
name: `${layerName}:root`,
data: normalizeFiles(rootFiles)
});
}
hashSources.sort((a, b) => a.name.localeCompare(b.name));
const res = nuxt[`_${options.id}BuildHash`] = {
hash: hash(hashSources),
sources: hashSources
};
const elapsed = Date.now() - start;
consola$1.debug(`Computed \`${options.id}\` build hash in \`${elapsed}ms\`.`);
return res;
}
async function readFilesRecursive(dir, opts) {
if (Array.isArray(dir)) {
return (await Promise.all(dir.map((d) => readFilesRecursive(d, opts)))).flat();
}
const files = await glob(opts.patterns, { cwd: dir });
const fileEntries = await Promise.all(files.map(async (fileName) => {
if (!opts.shouldIgnore?.(fileName)) {
const file = await readFileWithMeta(dir, fileName);
if (!file) {
return;
}
return {
...file,
name: relative(opts.cwd, join(dir, file.name))
};
}
}));
return fileEntries.filter(Boolean);
}
async function readFileWithMeta(dir, fileName, count = 0) {
let fd = void 0;
try {
fd = await open(resolve$1(dir, fileName));
const stats = await fd.stat();
if (!stats?.isFile()) {
return;
}
const mtime = stats.mtime.getTime();
const data = await fd.readFile();
if ((await fd.stat()).mtime.getTime() !== mtime) {
if (count < 5) {
return readFileWithMeta(dir, fileName, count + 1);
}
console.warn(`Failed to read file \`${fileName}\` as it changed during read.`);
return;
}
return {
name: fileName,
data,
attrs: {
mtime,
size: stats.size
}
};
} catch (err) {
console.warn(`Failed to read file \`${fileName}\`:`, err);
} finally {
await fd?.close();
}
}
async function restoreCache(cwd, cacheFile) {
if (!existsSync(cacheFile)) {
return false;
}
const files = parseTar(await readFile(cacheFile));
for (const file of files) {
let fd = void 0;
try {
const filePath = resolve$1(cwd, file.name);
await mkdir(dirname(filePath), { recursive: true });
fd = await open(filePath, "w");
const stats = await fd.stat().catch(() => null);
if (stats?.isFile() && stats.size) {
const lastModified = Number.parseInt(file.attrs?.mtime?.toString().padEnd(13, "0") || "0");
if (stats.mtime.getTime() >= lastModified) {
consola$1.debug(`Skipping \`${file.name}\` (up to date or newer than cache)`);
continue;
}
}
await fd.writeFile(file.data);
} catch (err) {
console.error(err);
} finally {
await fd?.close();
}
}
return true;
}
async function writeCache(cwd, sources, cacheFile) {
const fileEntries = await readFilesRecursive(sources, {
patterns: ["**/*", "!analyze/**"],
cwd
});
const tarData = createTar(fileEntries);
await mkdir(dirname(cacheFile), { recursive: true });
await writeFile(cacheFile, tarData);
}
function getCacheDir(nuxt) {
let cacheDir = join(nuxt.options.workspaceDir, "node_modules");
if (!existsSync(cacheDir)) {
for (const dir of [...nuxt.options.modulesDir].sort((a, b) => a.length - b.length)) {
if (existsSync(dir)) {
cacheDir = dir;
break;
}
}
}
return join(cacheDir, ".cache/nuxt/builds");
}
async function build(nuxt) {
const app = createApp(nuxt);
nuxt.apps.default = app;
const generateApp$1 = debounce(() => generateApp(nuxt, app), void 0, { leading: true });
await generateApp$1();
if (nuxt.options.dev) {
watch(nuxt);
nuxt.hook("builder:watch", async (event, relativePath) => {
if (event === "add" || event === "unlink") {
const path = resolve(nuxt.options.srcDir, relativePath);
for (const dirs of getLayerDirectories(nuxt)) {
const relativePath2 = relative(dirs.app, path);
if (/^app\./i.test(relativePath2)) {
app.mainComponent = void 0;
break;
}
if (/^error\./i.test(relativePath2)) {
app.errorComponent = void 0;
break;
}
}
}
await generateApp$1();
});
nuxt.hook("builder:generateApp", (options) => {
if (options) {
return generateApp(nuxt, app, options);
}
return generateApp$1();
});
}
if (!nuxt.options._prepare && !nuxt.options.dev && nuxt.options.experimental.buildCache) {
const { restoreCache, collectCache } = await getVueHash(nuxt);
if (await restoreCache()) {
await nuxt.callHook("build:done");
return await nuxt.callHook("close", nuxt);
}
nuxt.hooks.hookOnce("nitro:build:before", () => collectCache());
nuxt.hooks.hookOnce("close", () => cleanupCaches(nuxt));
}
await nuxt.callHook("build:before");
if (nuxt.options._prepare) {
nuxt.hook("prepare:types", () => nuxt.close());
return;
}
if (nuxt.options.dev && !nuxt.options.test) {
nuxt.hooks.hookOnce("build:done", () => {
checkForExternalConfigurationFiles().catch((e) => logger.warn("Problem checking for external configuration files.", e));
});
}
await bundle(nuxt);
await nuxt.callHook("build:done");
if (!nuxt.options.dev) {
await nuxt.callHook("close", nuxt);
}
}
const watchEvents = {
create: "add",
delete: "unlink",
update: "change"
};
async function watch(nuxt) {
if (nuxt.options.experimental.watcher === "parcel") {
const success = await createParcelWatcher();
if (success) {
return;
}
}
if (nuxt.options.experimental.watcher === "chokidar") {
return createWatcher();
}
return createGranularWatcher();
}
function createWatcher() {
const nuxt = useNuxt();
const isIgnored2 = createIsIgnored(nuxt);
const watcher = watch$1(getLayerDirectories(nuxt).map((dirs) => dirs.app), {
...nuxt.options.watchers.chokidar,
ignoreInitial: true,
ignored: [isIgnored2, /[\\/]node_modules[\\/]/]
});
const restartPaths = /* @__PURE__ */ new Set();
const srcDir = nuxt.options.srcDir.replace(/\/?$/, "/");
for (const pattern of nuxt.options.watch) {
if (typeof pattern !== "string") {
continue;
}
const path = resolve(nuxt.options.srcDir, pattern);
if (!path.startsWith(srcDir)) {
restartPaths.add(path);
}
}
watcher.add([...restartPaths]);
watcher.on("all", (event, path) => {
if (event === "all" || event === "ready" || event === "error" || event === "raw") {
return;
}
nuxt.callHook("builder:watch", event, nuxt.options.experimental.relativeWatchPaths ? normalize(relative(nuxt.options.srcDir, path)) : normalize(path));
});
nuxt.hook("close", () => watcher?.close());
}
function createGranularWatcher() {
const nuxt = useNuxt();
const isIgnored2 = createIsIgnored(nuxt);
if (nuxt.options.debug && nuxt.options.debug.watchers) {
console.time("[nuxt] builder:chokidar:watch");
}
let pending = 0;
const ignoredDirs = /* @__PURE__ */ new Set([...nuxt.options.modulesDir, nuxt.options.buildDir]);
const pathsToWatch = resolvePathsToWatch(nuxt);
for (const dir of pathsToWatch) {
pending++;
const watcher = watch$1(dir, { ...nuxt.options.watchers.chokidar, ignoreInitial: false, depth: 0, ignored: [isIgnored2, /[\\/]node_modules[\\/]/] });
const watchers = {};
watcher.on("all", (event, path) => {
if (event === "all" || event === "ready" || event === "error" || event === "raw") {
return;
}
path = normalize(path);
if (!pending) {
nuxt.callHook("builder:watch", event, nuxt.options.experimental.relativeWatchPaths ? relative(nuxt.options.srcDir, path) : path);
}
if (event === "unlinkDir" && path in watchers) {
watchers[path]?.close();
delete watchers[path];
}
if (event === "addDir" && path !== dir && !ignoredDirs.has(path) && !pathsToWatch.has(path) && !(path in watchers) && !isIgnored2(path)) {
const pathWatcher = watchers[path] = watch$1(path, { ...nuxt.options.watchers.chokidar, ignored: [isIgnored2] });
pathWatcher.on("all", (event2, p) => {
if (event2 === "all" || event2 === "ready" || event2 === "error" || event2 === "raw") {
return;
}
nuxt.callHook("builder:watch", event2, nuxt.options.experimental.relativeWatchPaths ? normalize(relative(nuxt.options.srcDir, p)) : normalize(p));
});
nuxt.hook("close", () => watchers[path]?.close());
}
});
watcher.on("ready", () => {
pending--;
if (nuxt.options.debug && nuxt.options.debug.watchers && !pending) {
console.timeEnd("[nuxt] builder:chokidar:watch");
}
});
nuxt.hook("close", () => watcher?.close());
}
}
async function createParcelWatcher() {
const nuxt = useNuxt();
if (nuxt.options.debug && nuxt.options.debug.watchers) {
console.time("[nuxt] builder:parcel:watch");
}
try {
const { subscribe } = await importModule("@parcel/watcher", { url: [nuxt.options.rootDir, ...nuxt.options.modulesDir].map((d) => directoryToURL(d)) });
const pathsToWatch = resolvePathsToWatch(nuxt, { parentDirectories: true });
for (const dir of pathsToWatch) {
if (!await isDirectory$1(dir)) {
continue;
}
const watcher = subscribe(dir, (err, events) => {
if (err) {
return;
}
for (const event of events) {
if (isIgnored(event.path)) {
continue;
}
nuxt.callHook("builder:watch", watchEvents[event.type], nuxt.options.experimental.relativeWatchPaths ? normalize(relative(nuxt.options.srcDir, event.path)) : normalize(event.path));
}
}, {
ignore: [
...nuxt.options.ignore,
"node_modules"
]
});
watcher.then((subscription) => {
if (nuxt.options.debug && nuxt.options.debug.watchers) {
console.timeEnd("[nuxt] builder:parcel:watch");
}
nuxt.hook("close", () => subscription.unsubscribe());
});
}
return true;
} catch {
logger.warn("Falling back to `chokidar-granular` as `@parcel/watcher` cannot be resolved in your project.");
return false;
}
}
async function bundle(nuxt) {
try {
const { bundle: bundle2 } = typeof nuxt.options.builder === "string" ? await loadBuilder(nuxt, nuxt.options.builder) : nuxt.options.builder;
await bundle2(nuxt);
} catch (error) {
await nuxt.callHook("build:error", error);
if (error.toString().includes("Cannot find module '@nuxt/webpack-builder'")) {
throw new Error("Could not load `@nuxt/webpack-builder`. You may need to add it to your project dependencies, following the steps in `https://github.com/nuxt/framework/pull/2812`.");
}
throw error;
}
}
async function loadBuilder(nuxt, builder) {
try {
return await importModule(builder, { url: [directoryToURL(nuxt.options.rootDir), new URL(import.meta.url)] });
} catch (err) {
throw new Error(`Loading \`${builder}\` builder failed. You can read more about the nuxt \`builder\` option at: \`https://nuxt.com/docs/api/nuxt-config#builder\``, { cause: err });
}
}
function resolvePathsToWatch(nuxt, opts = {}) {
const pathsToWatch = /* @__PURE__ */ new Set();
for (const dirs of getLayerDirectories(nuxt)) {
if (!dirs.app || isIgnored(dirs.app)) {
continue;
}
pathsToWatch.add(dirs.app);
}
for (const pattern of nuxt.options.watch) {
if (typeof pattern !== "string") {
continue;
}
const path = opts?.parentDirectories ? join(dirname(resolve(nuxt.options.srcDir, pattern)), "") : resolve(nuxt.options.srcDir, pattern);
let shouldAdd = true;
for (const w of [...pathsToWatch]) {
if (w.startsWith(path)) {
pathsToWatch.delete(w);
}
if (path.startsWith(w)) {
shouldAdd = false;
}
}
if (shouldAdd) {
pathsToWatch.add(path);
}
}
return pathsToWatch;
}
export { build, createNuxt, loadNuxt };