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

2115 lines
63 KiB
JavaScript

import { builtinModules, createRequire } from 'node:module';
import { pathToFileURL, fileURLToPath } from 'node:url';
import { isAbsolute as isAbsolute$1 } from 'node:path';
import alias from '@rollup/plugin-alias';
import commonjs from '@rollup/plugin-commonjs';
import inject from '@rollup/plugin-inject';
import json from '@rollup/plugin-json';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import { defu } from 'defu';
import { parseNodeModulePath as parseNodeModulePath$1, isValidNodeImport, normalizeid, lookupNodeModuleSubpath, resolvePath, sanitizeFilePath } from 'mlly';
import { resolveModuleURL, resolveModulePath } from 'exsolve';
import { runtimeDir, runtimeDependencies } from 'nitropack/runtime/meta';
import { hash } from 'ohash';
import { resolve, dirname, extname, relative, join, normalize, isAbsolute } from 'pathe';
import { visualizer } from 'rollup-plugin-visualizer';
import { isWindows, isTest } from 'std-env';
import { defineEnv } from 'unenv';
import unimportPlugin from 'unimport/unplugin';
import { rollup } from 'unwasm/plugin';
import { genImport, genSafeVariableName } from 'knitwork';
import { connectors } from 'db0';
import { camelCase } from 'scule';
import { globby } from 'globby';
import { createFilter } from 'unplugin-utils';
import { transform } from 'esbuild';
import { promises, existsSync } from 'node:fs';
import { platform } from 'node:os';
import { nodeFileTrace } from '@vercel/nft';
import { isDirectory } from 'nitropack/kit';
import { readPackageJSON, writePackageJSON } from 'pkg-types';
import semver from 'semver';
import { consola } from 'consola';
import { readFile } from 'node:fs/promises';
import createEtag from 'etag';
import mime from 'mime';
import { withTrailingSlash } from 'ufo';
import _replace from '@rollup/plugin-replace';
import { normalizeKey, builtinDrivers } from 'unstorage';
const PREFIX = "\0virtual:";
function virtual(modules, cache = {}) {
const _modules = /* @__PURE__ */ new Map();
for (const [id, mod] of Object.entries(modules)) {
cache[id] = mod;
_modules.set(id, mod);
_modules.set(resolve(id), mod);
}
return {
name: "virtual",
resolveId(id, importer) {
if (id in modules) {
return PREFIX + id;
}
if (importer) {
const importerNoPrefix = importer.startsWith(PREFIX) ? importer.slice(PREFIX.length) : importer;
const resolved = resolve(dirname(importerNoPrefix), id);
if (_modules.has(resolved)) {
return PREFIX + resolved;
}
}
return null;
},
async load(id) {
if (!id.startsWith(PREFIX)) {
return null;
}
const idNoPrefix = id.slice(PREFIX.length);
if (!_modules.has(idNoPrefix)) {
return null;
}
let m = _modules.get(idNoPrefix);
if (typeof m === "function") {
m = await m();
}
if (!m) {
return null;
}
cache[id.replace(PREFIX, "")] = m;
return {
code: m,
map: null
};
}
};
}
function appConfig(nitro) {
return virtual(
{
"#nitro-internal-virtual/app-config": () => `
import { defuFn } from 'defu';
const inlineAppConfig = ${JSON.stringify(nitro.options.appConfig, null, 2)};
${nitro.options.appConfigFiles.map((file, i) => genImport(file, "appConfig" + i) + ";").join("\n")}
export const appConfig = defuFn(${[
...nitro.options.appConfigFiles.map((_, i) => "appConfig" + i),
"inlineAppConfig"
].join(", ")});
`
},
nitro.vfs
);
}
function database(nitro) {
if (!nitro.options.experimental.database) {
return virtual(
{
"#nitro-internal-virtual/database": () => {
return (
/* js */
`export const connectionConfigs = {};`
);
}
},
nitro.vfs
);
}
const dbConfigs = nitro.options.dev && nitro.options.devDatabase || nitro.options.database;
const connectorsNames = [
...new Set(
Object.values(dbConfigs || {}).map((config) => config?.connector)
)
].filter(Boolean);
for (const name of connectorsNames) {
if (!connectors[name]) {
throw new Error(`Database connector "${name}" is invalid.`);
}
}
return virtual(
{
"#nitro-internal-virtual/database": () => {
return `
${connectorsNames.map(
(name) => `import ${camelCase(name)}Connector from "${connectors[name]}";`
).join("\n")}
export const connectionConfigs = {
${Object.entries(dbConfigs || {}).map(
([name, { connector, options }]) => `${name}: {
connector: ${camelCase(connector)}Connector,
options: ${JSON.stringify(options)}
}`
).join(",\n")}
};
`;
}
},
nitro.vfs
);
}
const PLUGIN_NAME = "dynamic-require";
const HELPER_DYNAMIC = `\0${PLUGIN_NAME}.mjs`;
const DYNAMIC_REQUIRE_RE = /import\((?:.*\+\s*)?"\.\/" ?\+(.*)\).then/g;
function dynamicRequire({ dir, ignore, inline }) {
return {
name: PLUGIN_NAME,
transform(code, _id) {
return {
code: code.replace(
DYNAMIC_REQUIRE_RE,
`import('${HELPER_DYNAMIC}').then(r => r.default || r).then(dynamicRequire => dynamicRequire($1)).then`
),
map: null
};
},
resolveId(id) {
return id === HELPER_DYNAMIC ? id : null;
},
// TODO: Async chunk loading over network!
// renderDynamicImport () {
// return {
// left: 'fetch(', right: ')'
// }
// },
async load(_id) {
if (_id !== HELPER_DYNAMIC) {
return null;
}
let files = [];
try {
const wpManifest = resolve(dir, "./server.manifest.json");
files = await import(pathToFileURL(wpManifest).href).then(
(r) => Object.keys(r.files).filter((file) => !ignore.includes(file))
);
} catch {
files = await globby("**/*.{cjs,mjs,js}", {
cwd: dir,
absolute: false,
ignore
});
}
const chunks = (await Promise.all(
files.map(async (id) => ({
id,
src: resolve(dir, id).replace(/\\/g, "/"),
name: genSafeVariableName(id),
meta: await getWebpackChunkMeta(resolve(dir, id))
}))
)).filter((chunk) => chunk.meta);
return inline ? TMPL_INLINE({ chunks }) : TMPL_LAZY({ chunks });
}
};
}
async function getWebpackChunkMeta(src) {
const chunk = await import(pathToFileURL(src).href).then(
(r) => r.default || r || {}
);
const {
__webpack_id__,
__webpack_ids__,
__webpack_modules__,
id = __webpack_id__,
ids = __webpack_ids__,
modules = __webpack_modules__
} = chunk;
if (!id && !ids) {
return null;
}
return {
id,
ids,
moduleIds: Object.keys(modules || {})
};
}
function TMPL_INLINE({ chunks }) {
return `${chunks.map((i) => `import * as ${i.name} from '${i.src}'`).join("\n")}
const dynamicChunks = {
${chunks.map((i) => ` ['${i.id}']: ${i.name}`).join(",\n")}
};
export default function dynamicRequire(id) {
return Promise.resolve(dynamicChunks[id]);
};`;
}
function TMPL_LAZY({ chunks }) {
return `
const dynamicChunks = {
${chunks.map((i) => ` ['${i.id}']: () => import('${i.src}')`).join(",\n")}
};
export default function dynamicRequire(id) {
return dynamicChunks[id]();
};`;
}
const defaultLoaders = {
".ts": "ts",
".js": "js",
".tsx": "tsx",
".jsx": "jsx"
};
function esbuild(options) {
const {
include,
exclude,
sourceMap,
loaders: loadersConfig,
minify,
...transformOptions
} = options;
const loaders = { ...defaultLoaders };
if (loadersConfig) {
for (const key of Object.keys(loadersConfig)) {
const value = loadersConfig[key];
if (typeof value === "string") {
loaders[key] = value;
} else if (value === false) {
delete loaders[key];
}
}
}
const extensions = Object.keys(loaders);
const INCLUDE_REGEXP = new RegExp(
`\\.(${extensions.map((ext) => ext.slice(1)).join("|")})$`
);
const EXCLUDE_REGEXP = /node_modules/;
const filter = createFilter(
include || INCLUDE_REGEXP,
exclude || EXCLUDE_REGEXP
);
return {
name: "esbuild",
async transform(code, id) {
if (!filter(id)) {
return null;
}
const ext = extname(id);
const loader = loaders[ext];
if (!loader) {
return null;
}
const result = await transform(code, {
sourcemap: sourceMap === "hidden" ? "external" : sourceMap,
...transformOptions,
loader,
sourcefile: id
});
printWarnings(id, result, this);
return result.code && {
code: result.code,
map: result.map || null
};
},
async renderChunk(code) {
if (minify) {
const result = await transform(code, {
loader: "js",
minify: true,
target: transformOptions.target
});
if (result.code) {
return {
code: result.code,
map: result.map || null
};
}
}
return null;
}
};
}
function printWarnings(id, result, plugin) {
if (result.warnings) {
for (const warning of result.warnings) {
let message = "[esbuild]";
if (warning.location) {
message += ` (${relative(process.cwd(), id)}:${warning.location.line}:${warning.location.column})`;
}
message += ` ${warning.text}`;
plugin.warn(message);
}
}
}
function externals$1(opts) {
const trackedExternals = /* @__PURE__ */ new Set();
const tryResolve = (id, importer) => {
if (id.startsWith("\0")) {
return id;
}
const res = resolveModuleURL(id, {
try: true,
conditions: opts.exportConditions,
from: importer && isAbsolute(importer) ? [pathToFileURL(importer), ...opts.moduleDirectories] : opts.moduleDirectories,
suffixes: ["", "/index"],
extensions: [".mjs", ".cjs", ".js", ".mts", ".cts", ".ts", ".json"]
});
return res?.startsWith("file://") ? fileURLToPath(res) : res;
};
const inlineMatchers = (opts.inline || []).map((p) => normalizeMatcher(p)).sort((a, b) => (b.score || 0) - (a.score || 0));
const externalMatchers = (opts.external || []).map((p) => normalizeMatcher(p)).sort((a, b) => (b.score || 0) - (a.score || 0));
const isExplicitInline = (id, importer) => {
if (id.startsWith("\0")) {
return true;
}
const inlineMatch = inlineMatchers.find((m) => m(id, importer));
const externalMatch = externalMatchers.find((m) => m(id, importer));
if (inlineMatch && (!externalMatch || externalMatch && (inlineMatch.score || 0) > (externalMatch.score || 0))) {
return true;
}
};
return {
name: "node-externals",
async resolveId(originalId, importer, options) {
if (!originalId || originalId.startsWith("\0") || originalId.includes("?") || originalId.startsWith("#")) {
return null;
}
if (originalId.startsWith(".")) {
return null;
}
const id = normalize(originalId);
if (isExplicitInline(id, importer)) {
return null;
}
const resolved = await this.resolve(originalId, importer, options) || {
id
};
if (isExplicitInline(resolved.id, importer)) {
return null;
}
if (!isAbsolute(resolved.id) || !existsSync(resolved.id) || await isDirectory(resolved.id)) {
resolved.id = tryResolve(resolved.id, importer) || resolved.id;
}
if (!await isValidNodeImport(resolved.id).catch(() => false)) {
return null;
}
if (opts.trace === false) {
return {
...resolved,
id: isAbsolute(resolved.id) ? normalizeid(resolved.id) : resolved.id,
external: true
};
}
const { name: pkgName } = parseNodeModulePath$1(resolved.id);
if (!pkgName) {
return null;
}
if (pkgName !== originalId) {
if (!isAbsolute(originalId)) {
const fullPath = tryResolve(originalId, importer);
if (fullPath) {
trackedExternals.add(fullPath);
return {
id: originalId,
external: true
};
}
}
const packageEntry = tryResolve(pkgName, importer);
if (packageEntry !== id) {
const guessedSubpath = await lookupNodeModuleSubpath(id).catch(() => null);
const resolvedGuess = guessedSubpath && tryResolve(join(pkgName, guessedSubpath), importer);
if (resolvedGuess === id) {
trackedExternals.add(resolvedGuess);
return {
id: join(pkgName, guessedSubpath),
external: true
};
}
return null;
}
}
trackedExternals.add(resolved.id);
return {
id: pkgName,
external: true
};
},
async buildEnd() {
if (opts.trace === false) {
return;
}
for (const pkgName of opts.traceInclude || []) {
const path = await this.resolve(pkgName);
if (path?.id) {
trackedExternals.add(path.id.replace(/\?.+/, ""));
}
}
const _fileTrace = await nodeFileTrace([...trackedExternals], {
// https://github.com/nitrojs/nitro/pull/1562
conditions: (opts.exportConditions || []).filter(
(c) => !["require", "import", "default"].includes(c)
),
...opts.traceOptions
});
const _resolveTracedPath = (p) => promises.realpath(resolve(opts.traceOptions?.base || ".", p));
const tracedFiles = Object.fromEntries(
await Promise.all(
[..._fileTrace.reasons.entries()].map(async ([_path, reasons]) => {
if (reasons.ignored) {
return;
}
const path = await _resolveTracedPath(_path);
if (!path.includes("node_modules")) {
return;
}
if (!await isFile$1(path)) {
return;
}
const {
dir: baseDir,
name: pkgName,
subpath
} = parseNodeModulePath$1(path);
if (!baseDir || !pkgName) {
return;
}
const pkgPath = join(baseDir, pkgName);
const parents = await Promise.all(
[...reasons.parents].map((p) => _resolveTracedPath(p))
);
const tracedFile = {
path,
parents,
subpath,
pkgName,
pkgPath
};
return [path, tracedFile];
})
).then((r) => r.filter(Boolean))
);
const tracedPackages = {};
for (const tracedFile of Object.values(tracedFiles)) {
const pkgName = tracedFile.pkgName;
let tracedPackage = tracedPackages[pkgName];
let pkgJSON = await readPackageJSON(tracedFile.pkgPath, {
cache: true
}).catch(
() => {
}
// TODO: Only catch ENOENT
);
if (!pkgJSON) {
pkgJSON = { name: pkgName, version: "0.0.0" };
}
if (!tracedPackage) {
tracedPackage = {
name: pkgName,
versions: {}
};
tracedPackages[pkgName] = tracedPackage;
}
let tracedPackageVersion = tracedPackage.versions[pkgJSON.version || "0.0.0"];
if (!tracedPackageVersion) {
tracedPackageVersion = {
path: tracedFile.pkgPath,
files: [],
pkgJSON
};
tracedPackage.versions[pkgJSON.version || "0.0.0"] = tracedPackageVersion;
}
tracedPackageVersion.files.push(tracedFile.path);
tracedFile.pkgName = pkgName;
if (pkgJSON.version) {
tracedFile.pkgVersion = pkgJSON.version;
}
}
const usedAliases = {};
const writePackage = async (name, version, _pkgPath) => {
const pkg = tracedPackages[name];
const pkgPath = _pkgPath || pkg.name;
for (const src of pkg.versions[version].files) {
const { subpath } = parseNodeModulePath$1(src);
if (!subpath) {
continue;
}
const dst = join(opts.outDir, "node_modules", pkgPath, subpath);
await promises.mkdir(dirname(dst), { recursive: true });
await promises.copyFile(src, dst);
if (opts.chmod) {
await promises.chmod(dst, opts.chmod === true ? 420 : opts.chmod);
}
}
const pkgJSON = pkg.versions[version].pkgJSON;
applyProductionCondition(pkgJSON.exports);
const pkgJSONPath = join(
opts.outDir,
"node_modules",
pkgPath,
"package.json"
);
await promises.mkdir(dirname(pkgJSONPath), { recursive: true });
await promises.writeFile(
pkgJSONPath,
JSON.stringify(pkgJSON, null, 2),
"utf8"
);
if (opts.traceAlias && pkgPath in opts.traceAlias) {
usedAliases[opts.traceAlias[pkgPath]] = version;
await linkPackage(pkgPath, opts.traceAlias[pkgPath]);
}
};
const isWindows = platform() === "win32";
const linkPackage = async (from, to) => {
const src = join(opts.outDir, "node_modules", from);
const dst = join(opts.outDir, "node_modules", to);
const dstStat = await promises.lstat(dst).catch(() => null);
const exists = dstStat?.isSymbolicLink();
if (exists) {
return;
}
await promises.mkdir(dirname(dst), { recursive: true });
await promises.symlink(
relative(dirname(dst), src),
dst,
isWindows ? "junction" : "dir"
).catch((error) => {
console.error("Cannot link", from, "to", to, error);
});
};
const findPackageParents = (pkg, version) => {
const versionFiles = pkg.versions[version].files.map(
(path) => tracedFiles[path]
);
const parentPkgs = [
...new Set(
versionFiles.flatMap(
(file) => file.parents.map((parentPath) => {
const parentFile = tracedFiles[parentPath];
if (parentFile.pkgName === pkg.name) {
return null;
}
return `${parentFile.pkgName}@${parentFile.pkgVersion}`;
}).filter(Boolean)
)
)
];
return parentPkgs;
};
const multiVersionPkgs = {};
const singleVersionPackages = [];
for (const tracedPackage of Object.values(tracedPackages)) {
const versions = Object.keys(tracedPackage.versions);
if (versions.length === 1) {
singleVersionPackages.push(tracedPackage.name);
continue;
}
multiVersionPkgs[tracedPackage.name] = {};
for (const version of versions) {
multiVersionPkgs[tracedPackage.name][version] = findPackageParents(
tracedPackage,
version
);
}
}
await Promise.all(
singleVersionPackages.map((pkgName) => {
const pkg = tracedPackages[pkgName];
const version = Object.keys(pkg.versions)[0];
return writePackage(pkgName, version);
})
);
for (const [pkgName, pkgVersions] of Object.entries(multiVersionPkgs)) {
const versionEntries = Object.entries(pkgVersions).sort(
([v1, p1], [v2, p2]) => {
if (p1.length === 0) {
return -1;
}
if (p2.length === 0) {
return 1;
}
return compareVersions(v1, v2);
}
);
for (const [version, parentPkgs] of versionEntries) {
await writePackage(pkgName, version, `.nitro/${pkgName}@${version}`);
await linkPackage(`.nitro/${pkgName}@${version}`, `${pkgName}`);
for (const parentPkg of parentPkgs) {
const parentPkgName = parentPkg.replace(/@[^@]+$/, "");
await (multiVersionPkgs[parentPkgName] ? linkPackage(
`.nitro/${pkgName}@${version}`,
`.nitro/${parentPkg}/node_modules/${pkgName}`
) : linkPackage(
`.nitro/${pkgName}@${version}`,
`${parentPkgName}/node_modules/${pkgName}`
));
}
}
}
const userPkg = await readPackageJSON(
opts.rootDir || process.cwd()
).catch(() => ({}));
await writePackageJSON(resolve(opts.outDir, "package.json"), {
name: (userPkg.name || "server") + "-prod",
version: userPkg.version || "0.0.0",
type: "module",
private: true,
dependencies: Object.fromEntries(
[
...Object.values(tracedPackages).map((pkg) => [
pkg.name,
Object.keys(pkg.versions)[0]
]),
...Object.entries(usedAliases)
].sort(([a], [b]) => a.localeCompare(b))
)
});
}
};
}
function compareVersions(v1 = "0.0.0", v2 = "0.0.0") {
try {
return semver.lt(v1, v2, { loose: true }) ? 1 : -1;
} catch {
return v1.localeCompare(v2);
}
}
function applyProductionCondition(exports) {
if (!exports || typeof exports === "string" || Array.isArray(exports)) {
return;
}
if ("production" in exports) {
if (typeof exports.production === "string") {
exports.default = exports.production;
} else {
Object.assign(exports, exports.production);
}
}
for (const key in exports) {
applyProductionCondition(exports[key]);
}
}
async function isFile$1(file) {
try {
const stat = await promises.stat(file);
return stat.isFile();
} catch (error) {
if (error?.code === "ENOENT") {
return false;
}
throw error;
}
}
function normalizeMatcher(input) {
if (typeof input === "function") {
input.score = input.score ?? 1e4;
return input;
}
if (input instanceof RegExp) {
const matcher = ((id) => input.test(id));
matcher.score = input.toString().length;
Object.defineProperty(matcher, "name", { value: `match(${input})` });
return matcher;
}
if (typeof input === "string") {
const pattern = normalize(input);
const matcher = ((id) => {
const idWithoutNodeModules = id.split("node_modules/").pop();
return id.startsWith(pattern) || idWithoutNodeModules?.startsWith(pattern);
});
matcher.score = input.length;
if (!isAbsolute(input) && input[0] !== ".") {
matcher.score += 1e3;
}
Object.defineProperty(matcher, "name", { value: `match(${pattern})` });
return matcher;
}
throw new Error(`Invalid matcher or pattern: ${input}`);
}
function externals(opts) {
const trackedExternals = /* @__PURE__ */ new Set();
const _resolveCache = /* @__PURE__ */ new Map();
const _resolve = async (id) => {
let resolved = _resolveCache.get(id);
if (resolved) {
return resolved;
}
resolved = await resolvePath(id, {
conditions: opts.exportConditions,
url: opts.moduleDirectories
});
_resolveCache.set(id, resolved);
return resolved;
};
const inlineMatchers = (opts.inline || []).map((p) => normalizeMatcher(p));
const externalMatchers = (opts.external || []).map(
(p) => normalizeMatcher(p)
);
return {
name: "node-externals",
async resolveId(originalId, importer, resolveOpts) {
if (!originalId || originalId.startsWith("\0") || originalId.includes("?") || originalId.startsWith("#")) {
return null;
}
if (originalId.startsWith(".")) {
return null;
}
const id = normalize(originalId);
for (const matcher of inlineMatchers) {
if (matcher(id, importer)) {
return null;
}
}
for (const matcher of externalMatchers) {
if (matcher(id, importer)) {
return { id, external: true };
}
}
const resolved = await this.resolve(
originalId,
importer,
resolveOpts
) || {
id
};
if (!isAbsolute(resolved.id) || !existsSync(resolved.id) || await isDirectory(resolved.id)) {
resolved.id = await _resolve(resolved.id).catch(() => resolved.id);
}
if (!await isValidNodeImport(resolved.id).catch(() => false)) {
return null;
}
if (opts.trace === false) {
return {
...resolved,
id: isAbsolute(resolved.id) ? normalizeid(resolved.id) : resolved.id,
external: true
};
}
const { pkgName, subpath } = parseNodeModulePath(resolved.id);
if (!pkgName) {
return null;
}
if (pkgName !== originalId) {
if (!isAbsolute(originalId)) {
const fullPath = await _resolve(originalId);
trackedExternals.add(fullPath);
return {
id: originalId,
external: true
};
}
const packageEntry = await _resolve(pkgName).catch(() => null);
if (packageEntry !== originalId) {
const guessedSubpath = pkgName + subpath.replace(/\.[a-z]+$/, "");
const resolvedGuess = await _resolve(guessedSubpath).catch(
() => null
);
if (resolvedGuess === originalId) {
trackedExternals.add(resolvedGuess);
return {
id: guessedSubpath,
external: true
};
}
return null;
}
}
trackedExternals.add(resolved.id);
return {
id: pkgName,
external: true
};
},
async buildEnd() {
if (opts.trace === false) {
return;
}
for (const pkgName of opts.traceInclude || []) {
const path = await this.resolve(pkgName);
if (path?.id) {
trackedExternals.add(path.id.replace(/\?.+/, ""));
}
}
let tracedFiles = await nodeFileTrace(
[...trackedExternals],
opts.traceOptions
).then(
(r) => [...r.fileList].map((f) => resolve(opts.traceOptions.base, f))
).then((r) => r.filter((file) => file.includes("node_modules")));
tracedFiles = await Promise.all(
tracedFiles.map((file) => promises.realpath(file))
);
const packageJSONCache = /* @__PURE__ */ new Map();
const getPackageJson = async (pkgDir) => {
if (packageJSONCache.has(pkgDir)) {
return packageJSONCache.get(pkgDir);
}
const pkgJSON = JSON.parse(
await promises.readFile(resolve(pkgDir, "package.json"), "utf8")
);
packageJSONCache.set(pkgDir, pkgJSON);
return pkgJSON;
};
const tracedPackages = /* @__PURE__ */ new Map();
const ignoreDirs = [];
const ignoreWarns = /* @__PURE__ */ new Set();
for (const file of tracedFiles) {
const { baseDir, pkgName } = parseNodeModulePath(file);
if (!pkgName) {
continue;
}
let pkgDir = resolve(baseDir, pkgName);
const existingPkgDir = tracedPackages.get(pkgName);
if (existingPkgDir && existingPkgDir !== pkgDir) {
const v1 = await getPackageJson(existingPkgDir).then(
(r) => r.version
);
const v2 = await getPackageJson(pkgDir).then((r) => r.version);
const isNewer = semver.gt(v2, v1);
const getMajor = (v) => v.split(".").find((s) => s !== "0");
if (getMajor(v1) !== getMajor(v2)) {
const warn = `Multiple major versions of package \`${pkgName}\` are being externalized. Picking latest version:
` + [
` ${isNewer ? "-" : "+"} ` + existingPkgDir + "@" + v1,
` ${isNewer ? "+" : "-"} ` + pkgDir + "@" + v2
].join("\n");
if (!ignoreWarns.has(warn)) {
consola.warn(warn);
ignoreWarns.add(warn);
}
}
const [newerDir, olderDir] = isNewer ? [pkgDir, existingPkgDir] : [existingPkgDir, pkgDir];
if (getMajor(v1) === getMajor(v2)) {
tracedFiles = tracedFiles.map(
(f) => f.startsWith(olderDir + "/") ? f.replace(olderDir, newerDir) : f
);
}
ignoreDirs.push(olderDir + "/");
pkgDir = newerDir;
}
tracedPackages.set(pkgName, pkgDir);
}
tracedFiles = tracedFiles.filter(
(f) => !ignoreDirs.some((d) => f.startsWith(d))
);
tracedFiles = [...new Set(tracedFiles)];
for (const pkgDir of tracedPackages.values()) {
const pkgJSON = join(pkgDir, "package.json");
if (!tracedFiles.includes(pkgJSON)) {
tracedFiles.push(pkgJSON);
}
}
const writeFile = async (file) => {
if (!await isFile(file)) {
return;
}
const src = resolve(opts.traceOptions.base, file);
const { pkgName, subpath } = parseNodeModulePath(file);
const dst = resolve(opts.outDir, `node_modules/${pkgName + subpath}`);
await promises.mkdir(dirname(dst), { recursive: true });
try {
await promises.copyFile(src, dst);
} catch {
consola.warn(`Could not resolve \`${src}\`. Skipping.`);
}
};
await Promise.all(
tracedFiles.map((file) => retry(() => writeFile(file), 3))
);
await promises.writeFile(
resolve(opts.outDir, "package.json"),
JSON.stringify(
{
name: "nitro-output",
version: "0.0.0",
private: true,
bundledDependencies: [...tracedPackages.keys()].sort()
},
null,
2
),
"utf8"
);
}
};
}
function parseNodeModulePath(path) {
if (!path) {
return {};
}
const match = /^(.+\/node_modules\/)([^/@]+|@[^/]+\/[^/]+)(\/?.*?)?$/.exec(
normalize(path)
);
if (!match) {
return {};
}
const [, baseDir, pkgName, subpath] = match;
return {
baseDir,
pkgName,
subpath
};
}
async function isFile(file) {
try {
const stat = await promises.stat(file);
return stat.isFile();
} catch (error) {
if (error.code === "ENOENT") {
return false;
}
throw error;
}
}
async function retry(fn, retries) {
let retry2 = 0;
let lastError;
while (retry2++ < retries) {
try {
return await fn();
} catch (error) {
lastError = error;
await new Promise((resolve2) => setTimeout(resolve2, 2));
}
}
throw lastError;
}
function handlers(nitro) {
const getHandlers = () => {
const handlers2 = [
...nitro.scannedHandlers,
...nitro.options.handlers
];
const envConditions = new Set(
[
nitro.options.dev ? "dev" : "prod",
nitro.options.preset,
nitro.options.preset === "nitro-prerender" ? "prerender" : void 0
].filter(Boolean)
);
return handlers2.filter((h) => {
const envs = (Array.isArray(h.env) ? h.env : [h.env]).filter(
Boolean
);
return envs.length === 0 || envs.some((env) => envConditions.has(env));
});
};
return virtual(
{
"#nitro-internal-virtual/server-handlers": () => {
const handlers2 = getHandlers();
if (nitro.options.serveStatic) {
handlers2.unshift({
middleware: true,
handler: join(runtimeDir, "internal/static")
});
}
if (nitro.options.renderer) {
handlers2.push({
route: "/**",
lazy: true,
handler: nitro.options.renderer
});
}
extendMiddlewareWithRuleOverlaps(handlers2, nitro.options.routeRules);
const imports = unique(
handlers2.filter((h) => !h.lazy).map((h) => h.handler)
);
const lazyImports = unique(
handlers2.filter((h) => h.lazy).map((h) => h.handler)
);
const code = (
/* js */
`
${imports.map((handler) => `import ${getImportId(handler)} from '${handler}';`).join("\n")}
${lazyImports.map(
(handler) => `const ${getImportId(handler, true)} = () => import('${handler}');`
).join("\n")}
export const handlers = [
${handlers2.map(
(h) => ` { route: '${h.route || ""}', handler: ${getImportId(
h.handler,
h.lazy
)}, lazy: ${!!h.lazy}, middleware: ${!!h.middleware}, method: ${JSON.stringify(
h.method?.toLowerCase()
)} }`
).join(",\n")}
];
`.trim()
);
return code;
},
"#nitro-internal-virtual/server-handlers-meta": () => {
const handlers2 = getHandlers();
const imports = unique(handlers2.map((h) => h.handler));
return (
/* js */
`
${imports.map(
(handler) => `import ${getImportId(handler)}Meta from "${handler}?meta";`
).join("\n")}
export const handlersMeta = [
${handlers2.map(
(h) => (
/* js */
`{ route: ${JSON.stringify(h.route)}, method: ${JSON.stringify(
h.method?.toLowerCase()
)}, meta: ${getImportId(h.handler)}Meta }`
)
).join(",\n")}
];
`
);
}
},
nitro.vfs
);
}
function unique(arr) {
return [...new Set(arr)];
}
function getImportId(p, lazy) {
return (lazy ? "_lazy_" : "_") + hash(p).replace(/-/g, "").slice(0, 6);
}
const WILDCARD_PATH_RE = /\/\*\*.*$/;
function extendMiddlewareWithRuleOverlaps(handlers2, routeRules) {
const rules = Object.entries(routeRules);
for (const [path, rule] of rules) {
if (!rule.cache) {
const isNested = rules.some(
([p, r]) => r.cache && WILDCARD_PATH_RE.test(p) && path.startsWith(p.replace(WILDCARD_PATH_RE, ""))
);
if (!isNested) {
continue;
}
}
for (const [index, handler] of handlers2.entries()) {
if (!handler.route || handler.middleware) {
continue;
}
if (handler.route === path) {
break;
}
if (!WILDCARD_PATH_RE.test(handler.route)) {
continue;
}
if (!path.startsWith(handler.route.replace(WILDCARD_PATH_RE, ""))) {
continue;
}
handlers2.splice(index, 0, {
...handler,
route: path
});
break;
}
}
}
const virtualPrefix = "\0nitro-handler-meta:";
const esbuildLoaders = {
".ts": "ts",
".js": "js",
".tsx": "tsx",
".jsx": "jsx"
};
function handlersMeta(nitro) {
return {
name: "nitro:handlers-meta",
async resolveId(id, importer, resolveOpts) {
if (id.startsWith("\0")) {
return;
}
if (id.endsWith(`?meta`)) {
const resolved = await this.resolve(
id.replace(`?meta`, ``),
importer,
resolveOpts
);
if (!resolved) {
return;
}
return virtualPrefix + resolved.id;
}
},
async load(id) {
if (id.startsWith(virtualPrefix)) {
const fullPath = id.slice(virtualPrefix.length);
if (fullPath.startsWith("\0")) {
const { code } = await this.load({ id: fullPath });
return code;
}
return readFile(fullPath, { encoding: "utf8" });
}
},
async transform(code, id) {
if (!id.startsWith(virtualPrefix)) {
return;
}
let meta = null;
try {
const ext = extname(id);
const jsCode = await transform(code, {
loader: esbuildLoaders[ext]
}).then((r) => r.code);
const ast = this.parse(jsCode);
for (const node of ast.body) {
if (node.type === "ExpressionStatement" && node.expression.type === "CallExpression" && node.expression.callee.type === "Identifier" && node.expression.callee.name === "defineRouteMeta" && node.expression.arguments.length === 1) {
meta = astToObject(node.expression.arguments[0]);
break;
}
}
} catch (error) {
nitro.logger.warn(
`[handlers-meta] Cannot extra route meta for: ${id}: ${error}`
);
}
return {
code: `export default ${JSON.stringify(meta)};`,
map: null
};
}
};
}
function astToObject(node) {
switch (node.type) {
case "ObjectExpression": {
const obj = {};
for (const prop of node.properties) {
if (prop.type === "Property") {
const key = prop.key.name ?? prop.key.value;
obj[key] = astToObject(prop.value);
}
}
return obj;
}
case "ArrayExpression": {
return node.elements.map((el) => astToObject(el)).filter(Boolean);
}
case "Literal": {
return node.value;
}
}
}
const ImportMetaRe = /import\.meta|globalThis._importMeta_/;
function importMeta(nitro) {
return {
name: "import-meta",
renderChunk(code, chunk) {
const isEntry = chunk.isEntry;
if (!isEntry && (!ImportMetaRe.test(code) || code.includes("ROLLUP_NO_REPLACE"))) {
return;
}
const url = nitro.options.node && isEntry && !code.includes("ROLLUP_NO_REPLACE") ? "_import_meta_url_" : '"file:///_entry.js"';
const envImport = nitro.options.node ? "import process from 'node:process';" : "";
const env = nitro.options.node ? "process.env" : "{}";
const ref = "globalThis._importMeta_";
const stub = `{url:${url},env:${env}}`;
const stubInit = isEntry ? `${ref}=${stub};` : `${ref}=${ref}||${stub};`;
return {
code: envImport + stubInit + code,
map: null
};
}
};
}
const readAssetHandler = {
true: "node",
node: "node",
false: "null",
deno: "deno",
inline: "inline"
};
function publicAssets(nitro) {
return virtual(
{
// #nitro-internal-virtual/public-assets-data
"#nitro-internal-virtual/public-assets-data": async () => {
const assets = {};
const files = await globby("**", {
cwd: nitro.options.output.publicDir,
absolute: false,
dot: true
});
for (const id of files) {
let mimeType = mime.getType(id.replace(/\.(gz|br)$/, "")) || "text/plain";
if (mimeType.startsWith("text")) {
mimeType += "; charset=utf-8";
}
const fullPath = resolve(nitro.options.output.publicDir, id);
const assetData = await promises.readFile(fullPath);
const etag = createEtag(assetData);
const stat = await promises.stat(fullPath);
const assetId = "/" + decodeURIComponent(id);
let encoding;
if (id.endsWith(".gz")) {
encoding = "gzip";
} else if (id.endsWith(".br")) {
encoding = "br";
}
assets[assetId] = {
type: nitro._prerenderMeta?.[assetId]?.contentType || mimeType,
encoding,
etag,
mtime: stat.mtime.toJSON(),
size: stat.size,
path: relative(nitro.options.output.serverDir, fullPath),
data: nitro.options.serveStatic === "inline" ? assetData.toString("base64") : void 0
};
}
return `export default ${JSON.stringify(assets, null, 2)};`;
},
// #nitro-internal-virtual/public-assets-node
"#nitro-internal-virtual/public-assets-node": () => {
return `
import { promises as fsp } from 'node:fs'
import { fileURLToPath } from 'node:url'
import { resolve, dirname } from 'pathe'
import assets from '#nitro-internal-virtual/public-assets-data'
export function readAsset (id) {
const serverDir = dirname(fileURLToPath(import.meta.url))
return fsp.readFile(resolve(serverDir, assets[id].path))
}`;
},
// #nitro-internal-virtual/public-assets-deno
"#nitro-internal-virtual/public-assets-deno": () => {
return `
import assets from '#nitro-internal-virtual/public-assets-data'
export function readAsset (id) {
// https://deno.com/deploy/docs/serve-static-assets
const path = '.' + decodeURIComponent(new URL(\`../public\${id}\`, 'file://').pathname)
return Deno.readFile(path);
}`;
},
// #nitro-internal-virtual/public-assets-null
"#nitro-internal-virtual/public-assets-null": () => {
return `
export function readAsset (id) {
return Promise.resolve(null);
}`;
},
// #nitro-internal-virtual/public-assets-inline
"#nitro-internal-virtual/public-assets-inline": () => {
return `
import assets from '#nitro-internal-virtual/public-assets-data'
export function readAsset (id) {
if (!assets[id]) { return undefined }
if (assets[id]._data) { return assets[id]._data }
if (!assets[id].data) { return assets[id].data }
assets[id]._data = Uint8Array.from(atob(assets[id].data), (c) => c.charCodeAt(0))
return assets[id]._data
}`;
},
// #nitro-internal-virtual/public-assets
"#nitro-internal-virtual/public-assets": () => {
const publicAssetBases = Object.fromEntries(
nitro.options.publicAssets.filter((dir) => !dir.fallthrough && dir.baseURL !== "/").map((dir) => [
withTrailingSlash(dir.baseURL),
{ maxAge: dir.maxAge }
])
);
const handlerName = readAssetHandler[nitro.options.serveStatic] || "null";
const readAssetImport = `#nitro-internal-virtual/public-assets-${handlerName}`;
return `
import assets from '#nitro-internal-virtual/public-assets-data'
export { readAsset } from "${readAssetImport}"
export const publicAssetBases = ${JSON.stringify(publicAssetBases)}
export function isPublicAssetURL(id = '') {
if (assets[id]) {
return true
}
for (const base in publicAssetBases) {
if (id.startsWith(base)) { return true }
}
return false
}
export function getPublicAssetMeta(id = '') {
for (const base in publicAssetBases) {
if (id.startsWith(base)) { return publicAssetBases[base] }
}
return {}
}
export function getAsset (id) {
return assets[id]
}
`;
}
},
nitro.vfs
);
}
const HELPER_ID = "\0raw-helpers";
function raw(opts = {}) {
const extensions = /* @__PURE__ */ new Set([
".md",
".mdx",
".txt",
".css",
".htm",
".html",
".sql",
...opts.extensions || []
]);
return {
name: "raw",
async resolveId(id, importer, resolveOpts) {
if (id === HELPER_ID) {
return id;
}
if (id[0] === "\0") {
return;
}
const withRawSpecifier = id.startsWith("raw:");
if (withRawSpecifier) {
id = id.slice(4);
}
if (!withRawSpecifier && !extensions.has(extname(id))) {
return;
}
const resolvedId = (await this.resolve(id, importer, resolveOpts))?.id;
if (!resolvedId || resolvedId.startsWith("\0")) {
return resolvedId;
}
if (!withRawSpecifier && !extensions.has(extname(resolvedId))) {
return;
}
return { id: "\0raw:" + resolvedId };
},
load(id) {
if (id === HELPER_ID) {
return getHelpers();
}
if (id.startsWith("\0raw:")) {
return promises.readFile(id.slice(5), isBinary(id) ? "binary" : "utf8");
}
},
transform(code, id) {
if (!id.startsWith("\0raw:")) {
return;
}
if (isBinary(id)) {
const serialized = Buffer.from(code, "binary").toString("base64");
return {
code: `// ROLLUP_NO_REPLACE
import {base64ToUint8Array } from "${HELPER_ID}"
export default base64ToUint8Array("${serialized}")`,
map: null
};
}
return {
code: `// ROLLUP_NO_REPLACE
export default ${JSON.stringify(code)}`,
map: null
};
}
};
}
function isBinary(id) {
const idMime = mime.getType(id) || "";
if (idMime.startsWith("text/")) {
return false;
}
if (/application\/(json|sql|xml|yaml)/.test(idMime)) {
return false;
}
return true;
}
function getHelpers() {
const js = String.raw;
return js`
export function base64ToUint8Array(str) {
const data = atob(str);
const size = data.length;
const bytes = new Uint8Array(size);
for (let i = 0; i < size; i++) {
bytes[i] = data.charCodeAt(i);
}
return bytes;
}
`;
}
const NO_REPLACE_RE = /ROLLUP_NO_REPLACE/;
function replace(options) {
const _plugin = _replace(options);
return {
..._plugin,
// https://github.com/rollup/plugins/blob/master/packages/replace/src/index.js#L94
renderChunk(code, chunk, options2) {
if (!NO_REPLACE_RE.test(code)) {
return _plugin.renderChunk.call(this, code, chunk, options2);
}
}
};
}
function serverAssets(nitro) {
if (nitro.options.dev || nitro.options.preset === "nitro-prerender") {
return virtual(
{ "#nitro-internal-virtual/server-assets": getAssetsDev(nitro) },
nitro.vfs
);
}
return virtual(
{
"#nitro-internal-virtual/server-assets": async () => {
const assets = {};
for (const asset of nitro.options.serverAssets) {
const files = await globby(asset.pattern || "**/*", {
cwd: asset.dir,
absolute: false,
ignore: asset.ignore
});
for (const _id of files) {
const fsPath = resolve(asset.dir, _id);
const id = asset.baseName + "/" + _id;
assets[id] = { fsPath, meta: {} };
let type = mime.getType(id) || "text/plain";
if (type.startsWith("text")) {
type += "; charset=utf-8";
}
const etag = createEtag(await promises.readFile(fsPath));
const mtime = await promises.stat(fsPath).then((s) => s.mtime.toJSON());
assets[id].meta = { type, etag, mtime };
}
}
return getAssetProd(assets);
}
},
nitro.vfs
);
}
function getAssetsDev(nitro) {
return `
import { createStorage } from 'unstorage'
import fsDriver from 'unstorage/drivers/fs'
const serverAssets = ${JSON.stringify(nitro.options.serverAssets)}
export const assets = createStorage()
for (const asset of serverAssets) {
assets.mount(asset.baseName, fsDriver({ base: asset.dir, ignore: (asset?.ignore || []) }))
}`;
}
function getAssetProd(assets) {
return `
const _assets = {
${Object.entries(assets).map(
([id, asset]) => ` [${JSON.stringify(
normalizeKey(id)
)}]: {
import: () => import(${JSON.stringify(
"raw:" + asset.fsPath
)}).then(r => r.default || r),
meta: ${JSON.stringify(
asset.meta
)}
}`
).join(",\n")}
}
const normalizeKey = ${normalizeKey.toString()}
export const assets = {
getKeys() {
return Promise.resolve(Object.keys(_assets))
},
hasItem (id) {
id = normalizeKey(id)
return Promise.resolve(id in _assets)
},
getItem (id) {
id = normalizeKey(id)
return Promise.resolve(_assets[id] ? _assets[id].import() : null)
},
getMeta (id) {
id = normalizeKey(id)
return Promise.resolve(_assets[id] ? _assets[id].meta : {})
}
}
`;
}
function sourcemapMininify() {
return {
name: "nitro:sourcemap-minify",
generateBundle(_options, bundle) {
for (const [key, asset] of Object.entries(bundle)) {
if (!key.endsWith(".map") || !("source" in asset) || typeof asset.source !== "string") {
continue;
}
const sourcemap = JSON.parse(asset.source);
if (!(sourcemap.sources || []).some((s) => s.includes("node_modules"))) {
continue;
}
sourcemap.mappings = "";
asset.source = JSON.stringify(sourcemap);
}
}
};
}
function storage(nitro) {
const mounts = [];
const isDevOrPrerender = nitro.options.dev || nitro.options.preset === "nitro-prerender";
const storageMounts = isDevOrPrerender ? { ...nitro.options.storage, ...nitro.options.devStorage } : nitro.options.storage;
for (const path in storageMounts) {
const mount = storageMounts[path];
mounts.push({
path,
driver: builtinDrivers[mount.driver] || mount.driver,
opts: mount
});
}
const driverImports = [...new Set(mounts.map((m) => m.driver))];
const bundledStorageCode = `
import { prefixStorage } from 'unstorage'
import overlay from 'unstorage/drivers/overlay'
import memory from 'unstorage/drivers/memory'
const bundledStorage = ${JSON.stringify(nitro.options.bundledStorage)}
for (const base of bundledStorage) {
storage.mount(base, overlay({
layers: [
memory(),
// TODO
// prefixStorage(storage, base),
prefixStorage(storage, 'assets:nitro:bundled:' + base)
]
}))
}`;
return virtual(
{
"#nitro-internal-virtual/storage": `
import { createStorage } from 'unstorage'
import { assets } from '#nitro-internal-virtual/server-assets'
${driverImports.map((i) => genImport(i, genSafeVariableName(i))).join("\n")}
export const storage = createStorage({})
storage.mount('/assets', assets)
${mounts.map(
(m) => `storage.mount('${m.path}', ${genSafeVariableName(
m.driver
)}(${JSON.stringify(m.opts)}))`
).join("\n")}
${!isDevOrPrerender && nitro.options.bundledStorage.length > 0 ? bundledStorageCode : ""}
`
},
nitro.vfs
);
}
const TIMING = "globalThis.__timing__";
const iife = (code) => `(function() { ${code.trim()} })();`.replace(/\n/g, "");
const HELPERIMPORT = "import './timing.js';";
function timing(opts = {}) {
const HELPER_DEBUG = opts.silent ? "" : `if (t > 0) { console.debug('>', id + ' (' + t + 'ms)'); }`;
const HELPER = iife(
/* js */
`
const start = () => Date.now();
const end = s => Date.now() - s;
const _s = {};
const metrics = [];
const logStart = id => { _s[id] = Date.now(); };
const logEnd = id => { const t = end(_s[id]); delete _s[id]; metrics.push([id, t]); ${HELPER_DEBUG} };
${TIMING} = { start, end, metrics, logStart, logEnd };
`
);
return {
name: "timing",
generateBundle() {
this.emitFile({
type: "asset",
fileName: "timing.js",
source: HELPER
});
},
renderChunk(code, chunk) {
let name = chunk.fileName || "";
name = name.replace(extname(name), "");
const logName = name === "index" ? "Nitro Start" : "Load " + name;
return {
code: (chunk.isEntry ? HELPERIMPORT : "") + `${TIMING}.logStart('${logName}');` + code + `;${TIMING}.logEnd('${logName}');`,
map: null
};
}
};
}
function errorHandler(nitro) {
return virtual(
{
"#nitro-internal-virtual/error-handler": () => {
const errorHandlers = Array.isArray(nitro.options.errorHandler) ? nitro.options.errorHandler : [nitro.options.errorHandler];
const builtinHandler = join(
runtimeDir,
`internal/error/${nitro.options.dev ? "dev" : "prod"}`
);
return (
/* js */
`
${errorHandlers.map((h, i) => `import errorHandler$${i} from "${h}";`).join("\n")}
const errorHandlers = [${errorHandlers.map((_, i) => `errorHandler$${i}`).join(", ")}];
import { defaultHandler } from "${builtinHandler}";
export default async function(error, event) {
for (const handler of errorHandlers) {
try {
await handler(error, event, { defaultHandler });
if (event.handled) {
return; // Response handled
}
} catch(error) {
// Handler itself thrown, log and continue
console.error(error);
}
}
// H3 will handle fallback
}
`
);
}
},
nitro.vfs
);
}
function resolveAliases(_aliases) {
const aliases = Object.fromEntries(
Object.entries(_aliases).sort(
([a], [b]) => b.split("/").length - a.split("/").length || b.length - a.length
)
);
for (const key in aliases) {
for (const alias in aliases) {
if (!["~", "@", "#"].includes(alias[0])) {
continue;
}
if (alias === "@" && !aliases[key].startsWith("@/")) {
continue;
}
if (aliases[key].startsWith(alias)) {
aliases[key] = aliases[alias] + aliases[key].slice(alias.length);
}
}
}
return aliases;
}
const getRollupConfig = (nitro) => {
const extensions = [
".ts",
".mjs",
".js",
".json",
".node",
".tsx",
".jsx"
];
const isNodeless = nitro.options.node === false;
const { env } = defineEnv({
nodeCompat: isNodeless,
npmShims: true,
resolve: true,
presets: nitro.options.unenv,
overrides: {
alias: nitro.options.alias
}
});
const buildServerDir = join(nitro.options.buildDir, "dist/server");
const presetsDir = resolve(runtimeDir, "../presets");
const chunkNamePrefixes = [
[nitro.options.buildDir, "build"],
[buildServerDir, "app"],
[runtimeDir, "nitro"],
[presetsDir, "nitro"],
["\0raw:", "raw"],
["\0nitro-wasm:", "wasm"],
["\0", "virtual"]
];
function getChunkGroup(id) {
if (id.startsWith(runtimeDir) || id.startsWith(presetsDir)) {
return "nitro";
}
}
function getChunkName(id) {
for (const [dir, name] of chunkNamePrefixes) {
if (id.startsWith(dir)) {
return `chunks/${name}/[name].mjs`;
}
}
const routeHandler = nitro.options.handlers.find((h) => id.startsWith(h.handler)) || nitro.scannedHandlers.find((h) => id.startsWith(h.handler));
if (routeHandler?.route) {
const path = routeHandler.route.replace(/:([^/]+)/g, "_$1").replace(/\/[^/]+$/g, "") || "/";
return `chunks/routes${path}/[name].mjs`;
}
const taskHandler = Object.entries(nitro.options.tasks).find(
([_, task]) => task.handler === id
);
if (taskHandler) {
return `chunks/tasks/[name].mjs`;
}
return `chunks/_/[name].mjs`;
}
const rollupConfig = defu(nitro.options.rollupConfig, {
input: nitro.options.entry,
output: {
dir: nitro.options.output.serverDir,
entryFileNames: "index.mjs",
chunkFileNames(chunk) {
const lastModule = normalize(chunk.moduleIds.at(-1) || "");
return getChunkName(lastModule);
},
manualChunks(id) {
return getChunkGroup(id);
},
inlineDynamicImports: nitro.options.inlineDynamicImports,
format: "esm",
exports: "auto",
intro: "",
outro: "",
generatedCode: {
constBindings: true
},
sanitizeFileName: sanitizeFilePath,
sourcemap: nitro.options.sourceMap,
sourcemapExcludeSources: true,
sourcemapIgnoreList(relativePath, sourcemapPath) {
return relativePath.includes("node_modules");
}
},
external: [...env.external],
plugins: [],
onwarn(warning, rollupWarn) {
if (!["CIRCULAR_DEPENDENCY", "EVAL"].includes(warning.code || "") && !warning.message.includes("Unsupported source map comment")) {
rollupWarn(warning);
}
},
treeshake: {
moduleSideEffects(id) {
const normalizedId = normalize(id);
const idWithoutNodeModules = normalizedId.split("node_modules/").pop();
if (!idWithoutNodeModules) {
return false;
}
if (normalizedId.startsWith(runtimeDir) || idWithoutNodeModules.startsWith(runtimeDir)) {
return true;
}
return nitro.options.moduleSideEffects.some(
(m) => normalizedId.startsWith(m) || idWithoutNodeModules.startsWith(m)
);
}
}
});
if (rollupConfig.output.inlineDynamicImports) {
delete rollupConfig.output.manualChunks;
}
if (nitro.options.timing) {
rollupConfig.plugins.push(
timing({
silent: isTest
})
);
}
if (nitro.options.imports) {
rollupConfig.plugins.push(
unimportPlugin.rollup(nitro.options.imports)
);
}
rollupConfig.plugins.push(raw());
if (nitro.options.experimental.wasm) {
rollupConfig.plugins.push(rollup(nitro.options.wasm || {}));
}
let NODE_ENV = nitro.options.dev ? "development" : "production";
if (nitro.options.preset === "nitro-prerender") {
NODE_ENV = "prerender";
}
const buildEnvVars = {
NODE_ENV,
prerender: nitro.options.preset === "nitro-prerender",
server: true,
client: false,
dev: String(nitro.options.dev),
DEBUG: nitro.options.dev
};
const staticFlags = {
dev: nitro.options.dev,
preset: nitro.options.preset,
prerender: nitro.options.preset === "nitro-prerender",
server: true,
client: false,
nitro: true,
baseURL: nitro.options.baseURL,
// @ts-expect-error
"versions.nitro": "",
"versions?.nitro": "",
// Internal
_asyncContext: nitro.options.experimental.asyncContext,
_websocket: nitro.options.experimental.websocket,
_tasks: nitro.options.experimental.tasks
};
rollupConfig.plugins.push(importMeta(nitro));
rollupConfig.plugins.push(
replace({
preventAssignment: true,
values: {
"typeof window": '"undefined"',
_import_meta_url_: "import.meta.url",
"globalThis.process.": "process.",
"process.env.RUNTIME_CONFIG": () => JSON.stringify(nitro.options.runtimeConfig, null, 2),
...Object.fromEntries(
[".", ";", ")", "[", "]", "}", " "].map((d) => [
`import.meta${d}`,
`globalThis._importMeta_${d}`
])
),
...Object.fromEntries(
Object.entries(buildEnvVars).map(([key, val]) => [
`process.env.${key}`,
JSON.stringify(val)
])
),
...Object.fromEntries(
Object.entries(buildEnvVars).map(([key, val]) => [
`import.meta.env.${key}`,
JSON.stringify(val)
])
),
...Object.fromEntries(
Object.entries(staticFlags).map(([key, val]) => [
`process.${key}`,
JSON.stringify(val)
])
),
...Object.fromEntries(
Object.entries(staticFlags).map(([key, val]) => [
`import.meta.${key}`,
JSON.stringify(val)
])
),
...nitro.options.replace
}
})
);
rollupConfig.plugins.push(
esbuild({
target: "es2019",
sourceMap: nitro.options.sourceMap,
...nitro.options.esbuild?.options
})
);
rollupConfig.plugins.push(
dynamicRequire({
dir: resolve(nitro.options.buildDir, "dist/server"),
inline: nitro.options.node === false || nitro.options.inlineDynamicImports,
ignore: [
"client.manifest.mjs",
"server.js",
"server.cjs",
"server.mjs",
"server.manifest.mjs"
]
})
);
rollupConfig.plugins.push(serverAssets(nitro));
rollupConfig.plugins.push(publicAssets(nitro));
rollupConfig.plugins.push(storage(nitro));
rollupConfig.plugins.push(database(nitro));
rollupConfig.plugins.push(appConfig(nitro));
rollupConfig.plugins.push(handlers(nitro));
if (nitro.options.experimental.openAPI) {
rollupConfig.plugins.push(handlersMeta(nitro));
}
rollupConfig.plugins.push(errorHandler(nitro));
rollupConfig.plugins.push(
virtual(
{
"#nitro-internal-pollyfills": env.polyfill.map((p) => `import '${p}';`).join("\n") || `/* No polyfills */`
},
nitro.vfs
)
);
rollupConfig.plugins.push(virtual(nitro.options.virtual, nitro.vfs));
const nitroPlugins = [...new Set(nitro.options.plugins)];
rollupConfig.plugins.push(
virtual(
{
"#nitro-internal-virtual/plugins": `
${nitroPlugins.map(
(plugin) => `import _${hash(plugin).replace(/-/g, "")} from '${plugin}';`
).join("\n")}
export const plugins = [
${nitroPlugins.map((plugin) => `_${hash(plugin).replace(/-/g, "")}`).join(",\n")}
]
`
},
nitro.vfs
)
);
let buildDir = nitro.options.buildDir;
if (isWindows && nitro.options.externals?.trace === false && nitro.options.dev) {
buildDir = pathToFileURL(buildDir).href;
}
rollupConfig.plugins.push(
alias({
entries: resolveAliases({
"#build": buildDir,
"#internal/nitro": runtimeDir,
"nitro/runtime": runtimeDir,
"nitropack/runtime": runtimeDir,
"~": nitro.options.srcDir,
"@/": nitro.options.srcDir,
"~~": nitro.options.rootDir,
"@@/": nitro.options.rootDir,
...env.alias
})
})
);
if (nitro.options.noExternals) {
rollupConfig.plugins.push({
name: "no-externals",
async resolveId(id, importer, resolveOpts) {
if (nitro.options.node && (id.startsWith("node:") || builtinModules.includes(id))) {
return { id, external: true };
}
const resolved = await this.resolve(id, importer, resolveOpts);
if (!resolved) {
const _resolved = resolveModulePath(id, {
try: true,
from: importer && isAbsolute$1(importer) ? [pathToFileURL(importer), ...nitro.options.nodeModulesDirs] : nitro.options.nodeModulesDirs,
suffixes: ["", "/index"],
extensions: [".mjs", ".cjs", ".js", ".mts", ".cts", ".ts", ".json"],
conditions: [
"default",
nitro.options.dev ? "development" : "production",
"node",
"import",
"require"
]
});
if (_resolved) {
return { id: _resolved, external: false };
}
}
if (!resolved || resolved.external && !id.endsWith(".wasm")) {
throw new Error(
`Cannot resolve ${JSON.stringify(id)} from ${JSON.stringify(
importer
)} and externals are not allowed!`
);
}
}
});
} else {
const externalsPlugin = nitro.options.experimental.legacyExternals ? externals : externals$1;
rollupConfig.plugins.push(
externalsPlugin(
defu(nitro.options.externals, {
outDir: nitro.options.output.serverDir,
moduleDirectories: nitro.options.nodeModulesDirs,
external: [
...nitro.options.dev ? [nitro.options.buildDir] : [],
...nitro.options.nodeModulesDirs
],
inline: [
"#",
"~",
"@/",
"~~",
"@@/",
"virtual:",
"nitro/runtime",
"nitropack/runtime",
dirname(nitro.options.entry),
...nitro.options.experimental.wasm ? [(id) => id?.endsWith(".wasm")] : [],
runtimeDir,
nitro.options.srcDir,
...nitro.options.handlers.map((m) => m.handler).filter((i) => typeof i === "string"),
...nitro.options.dev || nitro.options.preset === "nitro-prerender" || nitro.options.experimental.bundleRuntimeDependencies === false ? [] : runtimeDependencies
],
traceOptions: {
base: "/",
processCwd: nitro.options.rootDir,
exportsOnly: true
},
traceAlias: {
"h3-nightly": "h3",
...nitro.options.externals?.traceAlias
},
exportConditions: nitro.options.exportConditions
})
)
);
}
rollupConfig.plugins.push(
nodeResolve({
extensions,
preferBuiltins: !!nitro.options.node,
rootDir: nitro.options.rootDir,
modulePaths: nitro.options.nodeModulesDirs,
// 'module' is intentionally not supported because of externals
mainFields: ["main"],
exportConditions: nitro.options.exportConditions
})
);
rollupConfig.plugins.push(
commonjs({
strictRequires: "auto",
// TODO: set to true (default) in v3
esmExternals: (id) => !id.startsWith("unenv/"),
requireReturnsDefault: "auto",
...nitro.options.commonJS
})
);
rollupConfig.plugins.push(json());
rollupConfig.plugins.push(inject(env.inject));
if (nitro.options.minify) {
const _terser = createRequire(import.meta.url)("@rollup/plugin-terser");
const terser = _terser.default || _terser;
rollupConfig.plugins.push(
terser({
mangle: {
keep_fnames: true,
keep_classnames: true
},
format: {
comments: false
}
})
);
}
if (nitro.options.sourceMap && !nitro.options.dev && nitro.options.experimental.sourcemapMinify !== false) {
rollupConfig.plugins.push(sourcemapMininify());
}
if (nitro.options.analyze) {
rollupConfig.plugins.push(
visualizer({
...nitro.options.analyze,
filename: (nitro.options.analyze.filename || "stats.html").replace(
"{name}",
"nitro"
),
title: "Nitro Server bundle stats"
})
);
}
return rollupConfig;
};
export { getRollupConfig };