import { Worker } from 'worker_threads'; import { promises, existsSync, createWriteStream } from 'fs'; import { debounce } from 'perfect-debounce'; import { eventHandler, createError, createApp, fromNodeMiddleware, toNodeListener } from 'h3'; import httpProxy from 'http-proxy'; import { listen } from 'listhen'; import { servePlaceholder } from 'serve-placeholder'; import serveStatic from 'serve-static'; import { resolve, dirname, relative, normalize, isAbsolute, join, extname } from 'pathe'; import { withLeadingSlash, withoutTrailingSlash, withBase, joinURL, withoutLeadingSlash, withTrailingSlash, withoutBase, parseURL } from 'ufo'; import { watch } from 'chokidar'; import { fileURLToPath, pathToFileURL } from 'url'; import chalk from 'chalk'; import { toRouteMatcher, createRouter } from 'radix3'; import { defu } from 'defu'; import { createHooks, createDebugger } from 'hookable'; import { createUnimport } from 'unimport'; import consola from 'consola'; import { loadConfig } from 'c12'; import { klona } from 'klona/full'; import { camelCase } from 'scule'; import { isValidNodeImport, normalizeid, resolvePath as resolvePath$1, sanitizeFilePath, resolveModuleExportNames } from 'mlly'; import { isTest, provider, isWindows, isDebug } from 'std-env'; import { readPackageJSON, findWorkspaceDir } from 'pkg-types'; import { createRequire } from 'module'; import fse, { move } from 'fs-extra'; import 'jiti'; import { getProperty } from 'dot-prop'; import archiver from 'archiver'; import { globby } from 'globby'; import fsp$1 from 'fs/promises'; import { normalizeKey, builtinDrivers, createStorage as createStorage$1 } from 'unstorage'; import { resolveAlias } from 'pathe/utils'; import * as rollup from 'rollup'; import prettyBytes from 'pretty-bytes'; import { gzipSize } from 'gzip-size'; import { terser } from 'rollup-plugin-terser'; import commonjs from '@rollup/plugin-commonjs'; import { nodeResolve } from '@rollup/plugin-node-resolve'; import alias from '@rollup/plugin-alias'; import json from '@rollup/plugin-json'; import wasmPlugin from '@rollup/plugin-wasm'; import inject from '@rollup/plugin-inject'; import { visualizer } from 'rollup-plugin-visualizer'; import * as unenv from 'unenv'; import unimportPlugin from 'unimport/unplugin'; import { hash } from 'ohash'; import _replace from '@rollup/plugin-replace'; import { genSafeVariableName, genImport } from 'knitwork'; import { nodeFileTrace } from '@vercel/nft'; import semver from 'semver'; import createEtag from 'etag'; import mime from 'mime'; import { transform } from 'esbuild'; import { createFilter } from '@rollup/pluginutils'; import zlib from 'node:zlib'; import fsp from 'node:fs/promises'; import { existsSync as existsSync$1 } from 'node:fs'; async function printFSTree(dir) { if (isTest) { return; } const files = await globby("**/*.*", { cwd: dir, ignore: ["*.map"] }); const items = (await Promise.all(files.map(async (file) => { const path = resolve(dir, file); const src = await promises.readFile(path); const size = src.byteLength; const gzip = await gzipSize(src); return { file, path, size, gzip }; }))).sort((a, b) => b.path.localeCompare(a.path)); let totalSize = 0; let totalGzip = 0; let totalNodeModulesSize = 0; let totalNodeModulesGzip = 0; items.forEach((item, index) => { dirname(item.file); const rpath = relative(process.cwd(), item.path); const treeChar = index === items.length - 1 ? "\u2514\u2500" : "\u251C\u2500"; const isNodeModules = item.file.includes("node_modules"); if (isNodeModules) { totalNodeModulesSize += item.size; totalNodeModulesGzip += item.gzip; return; } process.stdout.write(chalk.gray(` ${treeChar} ${rpath} (${prettyBytes(item.size)}) (${prettyBytes(item.gzip)} gzip) `)); totalSize += item.size; totalGzip += item.gzip; }); process.stdout.write(`${chalk.cyan("\u03A3 Total size:")} ${prettyBytes(totalSize + totalNodeModulesSize)} (${prettyBytes(totalGzip + totalNodeModulesGzip)} gzip) `); } function hl(str) { return chalk.cyan(str); } function prettyPath(p, highlight = true) { p = relative(process.cwd(), p); return highlight ? hl(p) : p; } function compileTemplate(contents) { return (params) => contents.replace(/{{ ?([\w.]+) ?}}/g, (_, match) => { const val = getProperty(params, match); if (!val) { consola.warn(`cannot resolve template param '${match}' in ${contents.slice(0, 20)}`); } return val || `${match}`; }); } async function writeFile$1(file, contents, log = false) { await fse.mkdirp(dirname(file)); await fse.writeFile(file, contents, typeof contents === "string" ? "utf-8" : void 0); if (log) { consola.info("Generated", prettyPath(file)); } } function resolvePath(path, nitroOptions, base) { if (typeof path !== "string") { throw new TypeError("Invalid path: " + path); } path = compileTemplate(path)(nitroOptions); for (const base2 in nitroOptions.alias) { if (path.startsWith(base2)) { path = nitroOptions.alias[base2] + path.substring(base2.length); } } return resolve(base || nitroOptions.srcDir, path); } const autodetectableProviders = { azure_static: "azure", cloudflare_pages: "cloudflare_pages", netlify: "netlify", stormkit: "stormkit", vercel: "vercel", cleavr: "cleavr" }; function detectTarget() { return autodetectableProviders[provider]; } async function isDirectory(path) { try { return (await fse.stat(path)).isDirectory(); } catch (_err) { return false; } } createRequire(import.meta.url); 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; } async function retry(fn, retries) { let retry2 = 0; let error; while (retry2++ < retries) { try { return await fn(); } catch (err) { error = err; await new Promise((resolve2) => setTimeout(resolve2, 2)); } } throw error; } let distDir = dirname(fileURLToPath(import.meta.url)); if (/(chunks|shared)$/.test(distDir)) { distDir = dirname(distDir); } const pkgDir = resolve(distDir, ".."); const runtimeDir = resolve(distDir, "runtime"); const NO_REPLACE_RE = /ROLLUP_NO_REPLACE/; function replace(options) { const _plugin = _replace(options); return { ..._plugin, renderChunk(code, chunk, options2) { if (!NO_REPLACE_RE.test(code)) { return _plugin.renderChunk.call(this, code, chunk, options2); } } }; } 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(); } cache[id.replace(PREFIX, "")] = m; return { code: m, map: null }; } }; } const PLUGIN_NAME = "dynamic-require"; const HELPER_DYNAMIC = `\0${PLUGIN_NAME}.mjs`; const DYNAMIC_REQUIRE_RE = /import\("\.\/" ?\+(.*)\).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; }, 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 { id, ids, 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](); };`; } 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$1(id, { conditions: opts.exportConditions, url: opts.moduleDirectories }); _resolveCache.set(id, resolved); return resolved; }; 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); const idWithoutNodeModules = id.split("node_modules/").pop(); if (opts.inline.find((i) => id.startsWith(i) || idWithoutNodeModules.startsWith(i))) { return null; } if (opts.external.find((i) => id.startsWith(i) || idWithoutNodeModules.startsWith(i))) { return { id, external: true }; } const resolved = await this.resolve(originalId, importer, { ...options, skipSelf: true }) || { 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(Array.from(trackedExternals), opts.traceOptions).then((r) => Array.from(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(".").filter((s) => s !== "0")[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 = Array.from(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 (err) { 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: Array.from(tracedPackages.keys()) }, 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 (err) { if (err.code === "ENOENT") { return false; } throw err; } } const TIMING = "globalThis.__timing__"; const iife = (code) => `(function() { ${code.trim()} })();`.replace(/\n/g, ""); const HELPER = iife(` 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]); console.debug('>', id + ' (' + t + 'ms)'); }; ${TIMING} = { start, end, metrics, logStart, logEnd }; `); const HELPERIMPORT = "import './timing.js';"; function timing(_opts = {}) { 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 publicAssets(nitro) { return virtual({ "#internal/nitro/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); assets[assetId] = { type: mimeType, encoding: id.endsWith(".gz") ? "gzip" : id.endsWith(".br") ? "br" : void 0, etag, mtime: stat.mtime.toJSON(), size: stat.size, path: relative(nitro.options.output.serverDir, fullPath) }; } return `export default ${JSON.stringify(assets, null, 2)};`; }, "#internal/nitro/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 '#internal/nitro/virtual/public-assets-data' export function readAsset (id) { const serverDir = dirname(fileURLToPath(import.meta.url)) return fsp.readFile(resolve(serverDir, assets[id].path)) }`; }, "#internal/nitro/virtual/public-assets-deno": () => { return ` import assets from '#internal/nitro/virtual/public-assets-data' export function readAsset (id) { // https://deno.com/deploy/docs/serve-static-assets const path = '.' + new URL('../public/test.txt', 'file://').pathname return Deno.readFile(path); }`; }, "#internal/nitro/virtual/public-assets": () => { const publicAssetBases = nitro.options.publicAssets.filter((dir) => !dir.fallthrough && dir.baseURL !== "/").map((dir) => dir.baseURL); return ` import assets from '#internal/nitro/virtual/public-assets-data' ${nitro.options.serveStatic ? `export * from "#internal/nitro/virtual/public-assets-${nitro.options.serveStatic === "deno" ? "deno" : "node"}"` : "export const readAsset = () => Promise(null)"} export const publicAssetBases = ${JSON.stringify(publicAssetBases)} export function isPublicAssetURL(id = '') { if (assets[id]) { return true } for (const base of publicAssetBases) { if (id.startsWith(base)) { return true } } return false } export function getAsset (id) { return assets[id] } `; } }, nitro.vfs); } function serverAssets(nitro) { if (nitro.options.dev || nitro.options.preset === "nitro-prerender") { return virtual({ "#internal/nitro/virtual/server-assets": getAssetsDev(nitro) }, nitro.vfs); } return virtual({ "#internal/nitro/virtual/server-assets": async () => { const assets = {}; for (const asset of nitro.options.serverAssets) { const files = await globby("**/*.*", { cwd: asset.dir, absolute: false }); 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 })) }`; } 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")} } ${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 : {}) } } `; } const unique = (arr) => Array.from(new Set(arr)); function handlers(nitro) { const getImportId = (p, lazy) => (lazy ? "_lazy_" : "_") + hash(p).slice(0, 6); return virtual({ "#internal/nitro/virtual/server-handlers": () => { const handlers2 = [ ...nitro.scannedHandlers, ...nitro.options.handlers ]; if (nitro.options.serveStatic) { handlers2.unshift({ middleware: true, handler: "#internal/nitro/static" }); } if (nitro.options.renderer) { handlers2.push({ route: "/**", lazy: true, handler: nitro.options.renderer }); } 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 = ` ${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)} }`).join(",\n")} ]; `.trim(); return code; } }, nitro.vfs); } const defaultLoaders = { ".ts": "ts", ".js": "js" }; function esbuild(options) { const loaders = { ...defaultLoaders }; if (options.loaders) { for (const key of Object.keys(options.loaders)) { const value = options.loaders[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( options.include || INCLUDE_REGEXP, options.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, { loader, target: options.target, define: options.define, sourcemap: options.sourceMap, sourcefile: id }); printWarnings(id, result, this); return result.code && { code: result.code, map: result.map || null }; }, async renderChunk(code) { if (options.minify) { const result = await transform(code, { loader: "js", minify: true, target: options.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 raw(opts = {}) { const extensions = new Set([".md", ".mdx", ".yml", ".txt", ".css", ".htm", ".html"].concat(opts.extensions || [])); return { name: "raw", resolveId(id) { if (id[0] === "\0") { return; } let isRawId = id.startsWith("raw:"); if (isRawId) { id = id.substring(4); } else if (extensions.has(extname(id))) { isRawId = true; } if (isRawId) { return { id: "\0raw:" + id }; } }, load(id) { if (id.startsWith("\0raw:")) { return promises.readFile(id.substring(5), "utf8"); } }, transform(code, id) { if (id.startsWith("\0raw:")) { return { code: `// ROLLUP_NO_REPLACE export default ${JSON.stringify(code)}`, map: null }; } } }; } 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 = Array.from(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({ "#internal/nitro/virtual/storage": ` import { createStorage } from 'unstorage' import { assets } from '#internal/nitro/virtual/server-assets' ${driverImports.map((i) => genImport(i, genSafeVariableName(i))).join("\n")} const storage = createStorage({}) export const useStorage = () => storage 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 ? bundledStorageCode : ""} ` }, nitro.vfs); } function importMeta(nitro) { const ImportMetaRe = /import\.meta|globalThis._importMeta_/; 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 ? "_import_meta_url_" : '"file:///_entry.js"'; 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: stubInit + code, map: null }; } }; } const getRollupConfig = (nitro) => { const extensions = [".ts", ".mjs", ".js", ".json", ".node"]; const nodePreset = nitro.options.node === false ? unenv.nodeless : unenv.node; const builtinPreset = { alias: { debug: "unenv/runtime/npm/debug", consola: "unenv/runtime/npm/consola", ...nitro.options.alias } }; const env = unenv.env(nodePreset, builtinPreset, nitro.options.unenv); if (nitro.options.sourceMap) { env.polyfill.push("source-map-support/register.js"); } const buildServerDir = join(nitro.options.buildDir, "dist/server"); const runtimeAppDir = join(runtimeDir, "app"); const rollupConfig = defu(nitro.options.rollupConfig, { input: nitro.options.entry, output: { dir: nitro.options.output.serverDir, entryFileNames: "index.mjs", chunkFileNames(chunkInfo) { let prefix = ""; const modules = Object.keys(chunkInfo.modules); const lastModule = modules[modules.length - 1]; if (lastModule.startsWith(buildServerDir)) { prefix = join("app", relative(buildServerDir, dirname(lastModule))); } else if (lastModule.startsWith(runtimeAppDir)) { prefix = "app"; } else if (lastModule.startsWith(nitro.options.buildDir)) { prefix = "build"; } else if (lastModule.startsWith(runtimeDir)) { prefix = "nitro"; } else if (nitro.options.handlers.find((m) => lastModule.startsWith(m.handler))) { prefix = "handlers"; } else if (lastModule.includes("assets") || lastModule.startsWith("\0raw:")) { prefix = "raw"; } else if (lastModule.startsWith("\0")) { prefix = "rollup"; } return join("chunks", prefix, "[name].mjs"); }, inlineDynamicImports: nitro.options.inlineDynamicImports, format: "esm", exports: "auto", intro: "", outro: "", preferConst: true, sanitizeFileName: sanitizeFilePath, sourcemap: nitro.options.sourceMap, sourcemapExcludeSources: true, sourcemapPathTransform(relativePath, sourcemapPath) { return resolve(dirname(sourcemapPath), relativePath); } }, external: env.external, makeAbsoluteExternalsRelative: false, 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(); return nitro.options.moduleSideEffects.some((m) => normalizedId.startsWith(m) || idWithoutNodeModules.startsWith(m)); } } }); if (nitro.options.timing) { rollupConfig.plugins.push(timing()); } if (nitro.options.imports) { rollupConfig.plugins.push(unimportPlugin.rollup(nitro.options.imports)); } rollupConfig.plugins.push(raw()); if (nitro.options.experimental.wasm) { const options = { ...nitro.options.experimental.wasm }; rollupConfig.plugins.push(wasmPlugin(options)); } const buildEnvVars = { NODE_ENV: nitro.options.dev ? "development" : nitro.options.preset === "nitro-prerender" ? "prerender" : "production", prerender: nitro.options.preset === "nitro-prerender", server: true, client: false, dev: String(nitro.options.dev), RUNTIME_CONFIG: nitro.options.runtimeConfig, DEBUG: nitro.options.dev }; rollupConfig.plugins.push(importMeta(nitro)); rollupConfig.plugins.push(replace({ preventAssignment: true, values: { "typeof window": '"undefined"', _import_meta_url_: "import.meta.url", ...Object.fromEntries([".", ";", ")", "[", "]", "}", " "].map((d) => [`import.meta${d}`, `globalThis._importMeta_${d}`])), ...Object.fromEntries([";", "(", "{", "}", " ", " ", "\n"].map((d) => [`${d}global.`, `${d}globalThis.`])), ...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)])), ...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(handlers(nitro)); rollupConfig.plugins.push(virtual({ "#internal/nitro/virtual/polyfill": env.polyfill.map((p) => `import '${p}';`).join("\n") }, nitro.vfs)); rollupConfig.plugins.push(virtual(nitro.options.virtual, nitro.vfs)); rollupConfig.plugins.push(virtual({ "#internal/nitro/virtual/plugins": ` ${nitro.options.plugins.map((plugin) => `import _${hash(plugin)} from '${plugin}';`).join("\n")} export const plugins = [ ${nitro.options.plugins.map((plugin) => `_${hash(plugin)}`).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/virtual/error-handler": nitro.options.errorHandler, "~": nitro.options.srcDir, "@/": nitro.options.srcDir, "~~": nitro.options.rootDir, "@@/": nitro.options.rootDir, ...env.alias }) })); if (!nitro.options.noExternals) { rollupConfig.plugins.push(externals(defu(nitro.options.externals, { outDir: nitro.options.output.serverDir, moduleDirectories: nitro.options.nodeModulesDirs, external: [ ...nitro.options.dev ? [nitro.options.buildDir] : [] ], inline: [ "#", "~", "@/", "~~", "@@/", "virtual:", runtimeDir, nitro.options.srcDir, ...nitro.options.handlers.map((m) => m.handler).filter((i) => typeof i === "string") ], traceOptions: { base: "/", processCwd: nitro.options.rootDir, exportsOnly: true }, exportConditions: [ "default", nitro.options.dev ? "development" : "production", "module", "node", "import" ] }))); } else { rollupConfig.plugins.push({ name: "no-externals", async resolveId(id, from, options) { const resolved = await this.resolve(id, from, { ...options, skipSelf: true }); if (!resolved || resolved.external) { throw new Error(`Cannot resolve ${JSON.stringify(id)} from ${JSON.stringify(from)} and externals are not allowed!`); } } }); } rollupConfig.plugins.push(nodeResolve({ extensions, preferBuiltins: !!nitro.options.node, rootDir: nitro.options.rootDir, modulePaths: nitro.options.nodeModulesDirs, mainFields: ["main"], exportConditions: [ "default", nitro.options.dev ? "development" : "production", "module", "node", "import" ] })); rollupConfig.plugins.push(commonjs({ esmExternals: (id) => !id.startsWith("unenv/"), requireReturnsDefault: "auto", ...nitro.options.commonJS })); rollupConfig.plugins.push(json()); rollupConfig.plugins.push(inject(env.inject)); if (nitro.options.minify) { rollupConfig.plugins.push(terser({ mangle: { keep_fnames: true, keep_classnames: true }, format: { comments: false } })); } if (nitro.options.analyze) { rollupConfig.plugins.push(visualizer({ ...nitro.options.analyze, filename: nitro.options.analyze.filename.replace("{name}", "nitro"), title: "Nitro Server bundle stats" })); } return rollupConfig; }; const GLOB_SCAN_PATTERN = "**/*.{ts,mjs,js,cjs}"; const httpMethodRegex = /\.(connect|delete|get|head|options|patch|post|put|trace)/; async function scanHandlers(nitro) { const handlers = await Promise.all([ scanMiddleware(nitro), scanRoutes(nitro, "api", "/api"), scanRoutes(nitro, "routes", "/") ]).then((r) => r.flat()); nitro.scannedHandlers = handlers.flatMap((h) => h.handlers); return handlers; } function scanMiddleware(nitro) { return scanServerDir(nitro, "middleware", (file) => ({ middleware: true, handler: file.fullPath })); } function scanRoutes(nitro, dir, prefix = "/") { return scanServerDir(nitro, dir, (file) => { let route = file.path.replace(/\.[a-zA-Z]+$/, "").replace(/\[\.\.\.\]/g, "**").replace(/\[\.\.\.(\w+)]/g, "**:$1").replace(/\[(\w+)\]/g, ":$1"); route = withLeadingSlash(withoutTrailingSlash(withBase(route, prefix))); let method; const methodMatch = route.match(httpMethodRegex); if (methodMatch) { route = route.substring(0, methodMatch.index); method = methodMatch[1]; } route = route.replace(/\/index$/, "") || "/"; return { handler: file.fullPath, lazy: true, route, method }; }); } async function scanServerDir(nitro, name, mapper) { const dirs = nitro.options.scanDirs.map((dir) => join(dir, name)); const files = await scanDirs(dirs); const handlers = files.map(mapper); return { dirs, files, handlers }; } async function scanPlugins(nitro) { const plugins = []; for (const dir of nitro.options.scanDirs) { const pluginDir = join(dir, "plugins"); const pluginFiles = await globby(GLOB_SCAN_PATTERN, { cwd: pluginDir, absolute: true }); plugins.push(...pluginFiles.sort()); } return plugins; } function scanDirs(dirs) { return Promise.all(dirs.map(async (dir) => { const fileNames = await globby(GLOB_SCAN_PATTERN, { cwd: dir, dot: true }); return fileNames.map((fileName) => { return { dir, path: fileName, fullPath: resolve(dir, fileName) }; }).sort((a, b) => b.path.localeCompare(a.path)); })).then((r) => r.flat()); } async function createStorage(nitro) { const storage = createStorage$1(); const mounts = { ...nitro.options.storage, ...nitro.options.devStorage }; for (const [path, opts] of Object.entries(mounts)) { const driver = await import(builtinDrivers[opts.driver] || opts.driver).then((r) => r.default || r); storage.mount(path, driver(opts)); } return storage; } async function snapshotStorage(nitro) { const data = {}; const allKeys = Array.from(new Set(await Promise.all( nitro.options.bundledStorage.map((base) => nitro.storage.getKeys(base)) ).then((r) => r.flat()))); await Promise.all(allKeys.map(async (key) => { data[key] = await nitro.storage.getItem(key); })); return data; } async function compressPublicAssets(nitro) { const publicFiles = await globby("**", { cwd: nitro.options.output.publicDir, absolute: false, dot: true, ignore: ["**/*.gz", "**/*.br"] }); for (const fileName of publicFiles) { const filePath = resolve(nitro.options.output.publicDir, fileName); const fileContents = await fsp.readFile(filePath); if (existsSync$1(filePath + ".gz") || existsSync$1(filePath + ".br")) { continue; } const mimeType = mime.getType(fileName) || "text/plain"; if (fileContents.length < 1024 || fileName.endsWith(".map") || !isCompressableMime(mimeType)) { continue; } const { gzip, brotli } = nitro.options.compressPublicAssets || {}; const encodings = [gzip !== false && "gzip", brotli !== false && "br"].filter(Boolean); for (const encoding of encodings) { const suffix = "." + (encoding === "gzip" ? "gz" : "br"); const compressedPath = filePath + suffix; if (existsSync$1(compressedPath)) { continue; } const gzipOptions = { level: zlib.constants.Z_BEST_COMPRESSION }; const brotliOptions = { [zlib.constants.BROTLI_PARAM_MODE]: isTextMime(mimeType) ? zlib.constants.BROTLI_MODE_TEXT : zlib.constants.BROTLI_MODE_GENERIC, [zlib.constants.BROTLI_PARAM_QUALITY]: zlib.constants.BROTLI_MAX_QUALITY, [zlib.constants.BROTLI_PARAM_SIZE_HINT]: fileContents.length }; const compressedBuff = await new Promise((resolve2, reject) => { const cb = (error, result) => error ? reject(error) : resolve2(result); if (encoding === "gzip") { zlib.gzip(fileContents, gzipOptions, cb); } else { zlib.brotliCompress(fileContents, brotliOptions, cb); } }); await fsp.writeFile(compressedPath, compressedBuff); } } } function isTextMime(mimeType) { return /text|javascript|json|xml/.test(mimeType); } function isCompressableMime(mimeType) { return /image|text|font|json|xml|javascript/.test(mimeType); } async function prepare(nitro) { await prepareDir(nitro.options.output.dir); if (!nitro.options.noPublicDir) { await prepareDir(nitro.options.output.publicDir); } await prepareDir(nitro.options.output.serverDir); } async function prepareDir(dir) { await promises.mkdir(dir, { recursive: true }); await fse.emptyDir(dir); } async function copyPublicAssets(nitro) { if (nitro.options.noPublicDir) { return; } for (const asset of nitro.options.publicAssets) { if (await isDirectory(asset.dir)) { await fse.copy(asset.dir, join(nitro.options.output.publicDir, asset.baseURL)); } } if (nitro.options.compressPublicAssets) { await compressPublicAssets(nitro); } nitro.logger.success("Generated public " + prettyPath(nitro.options.output.publicDir)); } async function build(nitro) { const rollupConfig = getRollupConfig(nitro); await nitro.hooks.callHook("rollup:before", nitro); return nitro.options.dev ? _watch(nitro, rollupConfig) : _build(nitro, rollupConfig); } async function writeTypes(nitro) { const routeTypes = {}; const middleware = [ ...nitro.scannedHandlers, ...nitro.options.handlers ]; for (const mw of middleware) { if (typeof mw.handler !== "string" || !mw.route) { continue; } const relativePath = relative(join(nitro.options.buildDir, "types"), mw.handler).replace(/\.[a-z]+$/, ""); routeTypes[mw.route] = routeTypes[mw.route] || []; routeTypes[mw.route].push(`Awaited>`); } let autoImportedTypes = []; if (nitro.unimport) { autoImportedTypes = [ (await nitro.unimport.generateTypeDeclarations({ exportHelper: false, resolvePath: (i) => { if (i.from.startsWith("#internal/nitro")) { return resolveAlias(i.from, nitro.options.alias); } return i.from; } })).trim() ]; } const lines = [ "// Generated by nitro", "declare module 'nitropack' {", " type Awaited = T extends PromiseLike ? Awaited : T", " interface InternalApi {", ...Object.entries(routeTypes).map(([path, types]) => ` '${path}': ${types.join(" | ")}`), " }", "}", ...autoImportedTypes, "export {}" ]; await writeFile$1(join(nitro.options.buildDir, "types/nitro.d.ts"), lines.join("\n")); if (nitro.options.typescript.generateTsConfig) { const tsConfig = { compilerOptions: { target: "ESNext", module: "ESNext", moduleResolution: "Node", allowJs: true, resolveJsonModule: true, paths: nitro.options.typescript.internalPaths ? { "#internal/nitro": [ join(runtimeDir, "index") ], "#internal/nitro/*": [ join(runtimeDir, "*") ] } : {} }, include: [ "./nitro.d.ts", join(relative(join(nitro.options.buildDir, "types"), nitro.options.rootDir), "**/*"), ...nitro.options.srcDir !== nitro.options.rootDir ? [join(relative(join(nitro.options.buildDir, "types"), nitro.options.srcDir), "**/*")] : [] ] }; await writeFile$1(join(nitro.options.buildDir, "types/tsconfig.json"), JSON.stringify(tsConfig, null, 2)); } } async function _snapshot(nitro) { if (!nitro.options.bundledStorage.length || nitro.options.preset === "nitro-prerender") { return; } const storageDir = resolve(nitro.options.buildDir, "snapshot"); nitro.options.serverAssets.push({ baseName: "nitro:bundled", dir: storageDir }); const data = await snapshotStorage(nitro); await Promise.all(Object.entries(data).map(async ([path, contents]) => { if (typeof contents !== "string") { contents = JSON.stringify(contents); } const fsPath = join(storageDir, path.replace(/:/g, "/")); await promises.mkdir(dirname(fsPath), { recursive: true }); await promises.writeFile(fsPath, contents, "utf8"); })); } async function _build(nitro, rollupConfig) { await scanHandlers(nitro); await writeTypes(nitro); await _snapshot(nitro); nitro.logger.info(`Building Nitro Server (preset: \`${nitro.options.preset}\`)`); const build2 = await rollup.rollup(rollupConfig).catch((error) => { nitro.logger.error(formatRollupError(error)); throw error; }); await build2.write(rollupConfig.output); const nitroConfigPath = resolve(nitro.options.output.dir, "nitro.json"); const buildInfo = { date: new Date(), preset: nitro.options.preset, commands: { preview: nitro.options.commands.preview, deploy: nitro.options.commands.deploy } }; await writeFile$1(nitroConfigPath, JSON.stringify(buildInfo, null, 2)); nitro.logger.success("Nitro server built"); if (nitro.options.logLevel > 1) { await printFSTree(nitro.options.output.serverDir); } await nitro.hooks.callHook("compiled", nitro); const rOutput = relative(process.cwd(), nitro.options.output.dir); const rewriteRelativePaths = (input) => { return input.replace(/\s\.\/([^\s]*)/g, ` ${rOutput}/$1`); }; if (buildInfo.commands.preview) { nitro.logger.success(`You can preview this build using \`${rewriteRelativePaths(buildInfo.commands.preview)}\``); } if (buildInfo.commands.deploy) { nitro.logger.success(`You can deploy this build using \`${rewriteRelativePaths(buildInfo.commands.deploy)}\``); } } function startRollupWatcher(nitro, rollupConfig) { const watcher = rollup.watch(defu(rollupConfig, { watch: { chokidar: nitro.options.watchOptions } })); let start; watcher.on("event", (event) => { switch (event.code) { case "START": return; case "BUNDLE_START": start = Date.now(); return; case "END": nitro.hooks.callHook("compiled", nitro); nitro.logger.success("Nitro built", start ? `in ${Date.now() - start} ms` : ""); nitro.hooks.callHook("dev:reload"); return; case "ERROR": nitro.logger.error(formatRollupError(event.error)); } }); return watcher; } async function _watch(nitro, rollupConfig) { let rollupWatcher; const reload = debounce(async () => { if (rollupWatcher) { await rollupWatcher.close(); } await scanHandlers(nitro); rollupWatcher = startRollupWatcher(nitro, rollupConfig); await writeTypes(nitro); }); const watchPatterns = nitro.options.scanDirs.flatMap((dir) => [ join(dir, "api"), join(dir, "routes"), join(dir, "middleware", GLOB_SCAN_PATTERN) ]); const watchReloadEvents = /* @__PURE__ */ new Set(["add", "addDir", "unlink", "unlinkDir"]); const reloadWacher = watch(watchPatterns, { ignoreInitial: true }).on("all", (event) => { if (watchReloadEvents.has(event)) { reload(); } }); nitro.hooks.hook("close", () => { rollupWatcher.close(); reloadWacher.close(); }); await reload(); } function formatRollupError(_error) { try { const logs = []; for (const error of "errors" in _error ? _error.errors : [_error]) { const id = error.path || error.id || _error.id; let path = isAbsolute(id) ? relative(process.cwd(), id) : id; const location = error.loc || error.location; if (location) { path += `:${location.line}:${location.column}`; } const text = error.text || error.frame; logs.push(`Rollup error while processing \`${path}\`` + text ? "\n\n" + text : ""); } return logs.join("\n\n"); } catch { return _error?.toString(); } } function defineNitroPreset(preset) { return preset; } const awsLambda = defineNitroPreset({ entry: "#internal/nitro/entries/aws-lambda" }); const azureFunctions = defineNitroPreset({ serveStatic: true, entry: "#internal/nitro/entries/azure-functions", commands: { deploy: "az functionapp deployment source config-zip -g -n --src {{ output.dir }}/deploy.zip" }, hooks: { async "compiled"(ctx) { await writeRoutes$2(ctx); } } }); function zipDirectory(dir, outfile) { const archive = archiver("zip", { zlib: { level: 9 } }); const stream = createWriteStream(outfile); return new Promise((resolve2, reject) => { archive.directory(dir, false).on("error", (err) => reject(err)).pipe(stream); stream.on("close", () => resolve2(void 0)); archive.finalize(); }); } async function writeRoutes$2(nitro) { const host = { version: "2.0", extensions: { http: { routePrefix: "" } } }; const functionDefinition = { entryPoint: "handle", bindings: [ { authLevel: "anonymous", type: "httpTrigger", direction: "in", name: "req", route: "{*url}", methods: [ "delete", "get", "head", "options", "patch", "post", "put" ] }, { type: "http", direction: "out", name: "res" } ] }; await writeFile$1(resolve(nitro.options.output.serverDir, "function.json"), JSON.stringify(functionDefinition)); await writeFile$1(resolve(nitro.options.output.dir, "host.json"), JSON.stringify(host)); await zipDirectory(nitro.options.output.dir, join(nitro.options.output.dir, "deploy.zip")); } const azure = defineNitroPreset({ entry: "#internal/nitro/entries/azure", output: { serverDir: "{{ output.dir }}/server/functions" }, commands: { preview: "npx @azure/static-web-apps-cli start ./public --api-location ./server" }, hooks: { async "compiled"(ctx) { await writeRoutes$1(ctx); } } }); async function writeRoutes$1(nitro) { const host = { version: "2.0" }; let nodeVersion = "16"; try { const currentNodeVersion = fse.readJSONSync(join(nitro.options.rootDir, "package.json")).engines.node; if (["16", "14"].includes(currentNodeVersion)) { nodeVersion = currentNodeVersion; } } catch { const currentNodeVersion = process.versions.node.slice(0, 2); if (["16", "14"].includes(currentNodeVersion)) { nodeVersion = currentNodeVersion; } } const config = { platform: { apiRuntime: `node:${nodeVersion}` }, routes: [], navigationFallback: { rewrite: "/api/server" } }; const routeFiles = nitro._prerenderedRoutes || []; const indexFileExists = routeFiles.some((route) => route.fileName === "/index.html"); if (!indexFileExists) { config.routes.unshift( { route: "/index.html", redirect: "/" }, { route: "/", rewrite: "/api/server" } ); } const suffix = "/index.html".length; for (const { fileName } of routeFiles) { if (!fileName.endsWith("/index.html")) { continue; } config.routes.unshift({ route: fileName.slice(0, -suffix) || "/", rewrite: fileName }); } for (const { fileName } of routeFiles) { if (!fileName.endsWith(".html") || fileName.endsWith("index.html")) { continue; } const route = fileName.slice(0, -".html".length); const existingRouteIndex = config.routes.findIndex((_route) => _route.route === route); if (existingRouteIndex > -1) { config.routes.splice(existingRouteIndex, 1); } config.routes.unshift({ route, rewrite: fileName }); } const functionDefinition = { entryPoint: "handle", bindings: [ { authLevel: "anonymous", type: "httpTrigger", direction: "in", name: "req", route: "{*url}", methods: ["delete", "get", "head", "options", "patch", "post", "put"] }, { type: "http", direction: "out", name: "res" } ] }; await writeFile$1(resolve(nitro.options.output.serverDir, "function.json"), JSON.stringify(functionDefinition, null, 2)); await writeFile$1(resolve(nitro.options.output.serverDir, "../host.json"), JSON.stringify(host, null, 2)); const stubPackageJson = resolve(nitro.options.output.serverDir, "../package.json"); await writeFile$1(stubPackageJson, JSON.stringify({ private: true })); await writeFile$1(resolve(nitro.options.rootDir, "staticwebapp.config.json"), JSON.stringify(config, null, 2)); if (!indexFileExists) { await writeFile$1(resolve(nitro.options.output.publicDir, "index.html"), ""); } } const baseWorker = defineNitroPreset({ entry: null, node: false, minify: true, noExternals: true, rollupConfig: { output: { format: "iife", generatedCode: { symbols: true } } }, inlineDynamicImports: true }); const cloudflare = defineNitroPreset({ extends: "base-worker", entry: "#internal/nitro/entries/cloudflare", commands: { preview: "npx wrangler dev ./server/index.mjs --site ./public --local", deploy: "npx wrangler publish" }, hooks: { async "compiled"(nitro) { await writeFile$1(resolve(nitro.options.output.dir, "package.json"), JSON.stringify({ private: true, main: "./server/index.mjs" }, null, 2)); await writeFile$1(resolve(nitro.options.output.dir, "package-lock.json"), JSON.stringify({ lockfileVersion: 1 }, null, 2)); } } }); const cloudflarePages = defineNitroPreset({ extends: "cloudflare", entry: "#internal/nitro/entries/cloudflare-pages", commands: { preview: "npx wrangler pages dev .output/public", deploy: "npx wrangler pages publish .output/public" }, output: { serverDir: "{{ rootDir }}/functions" }, rollupConfig: { output: { entryFileNames: "path.js", format: "esm" } }, hooks: { async "compiled"(nitro) { await move(resolve(nitro.options.output.serverDir, "path.js"), resolve(nitro.options.output.serverDir, "[[path]].js")); await move(resolve(nitro.options.output.serverDir, "path.js.map"), resolve(nitro.options.output.serverDir, "[[path]].js.map")); } } }); const deno = defineNitroPreset({ entry: "#internal/nitro/entries/deno", node: false, noExternals: true, serveStatic: "deno", commands: { preview: "", deploy: "cd ./ && deployctl deploy --project= server/index.ts" }, rollupConfig: { preserveEntrySignatures: false, external: [ "https://deno.land/std/http/server.ts" ], output: { entryFileNames: "index.ts", manualChunks: () => "index", format: "esm" } } }); const digitalOcean = defineNitroPreset({ extends: "node-server" }); const firebase = defineNitroPreset({ entry: "#internal/nitro/entries/firebase", commands: { deploy: "npx firebase deploy" }, hooks: { async "compiled"(ctx) { await writeRoutes(ctx); } } }); async function writeRoutes(nitro) { if (!fse.existsSync(join(nitro.options.rootDir, "firebase.json"))) { const firebase2 = { functions: { source: relative(nitro.options.rootDir, nitro.options.output.serverDir) }, hosting: [ { site: "", public: relative(nitro.options.rootDir, nitro.options.output.publicDir), cleanUrls: true, rewrites: [ { source: "**", function: "server" } ] } ] }; await writeFile$1(resolve(nitro.options.rootDir, "firebase.json"), JSON.stringify(firebase2)); } const _require = createRequire(import.meta.url); const jsons = await globby(join(nitro.options.output.serverDir, "node_modules/**/package.json")); const prefixLength = `${nitro.options.output.serverDir}/node_modules/`.length; const suffixLength = "/package.json".length; const dependencies = jsons.reduce((obj, packageJson) => { const dirname = packageJson.slice(prefixLength, -suffixLength); if (!dirname.includes("node_modules")) { obj[dirname] = _require(packageJson).version; } return obj; }, {}); let nodeVersion = "14"; try { const currentNodeVersion = fse.readJSONSync(join(nitro.options.rootDir, "package.json")).engines.node; if (["16", "14"].includes(currentNodeVersion)) { nodeVersion = currentNodeVersion; } } catch { const currentNodeVersion = process.versions.node.slice(0, 2); if (["16", "14"].includes(currentNodeVersion)) { nodeVersion = currentNodeVersion; } } const getPackageVersion = async (id) => { const pkg = await readPackageJSON(id, { url: nitro.options.nodeModulesDirs }); return pkg.version; }; await writeFile$1( resolve(nitro.options.output.serverDir, "package.json"), JSON.stringify( { private: true, type: "module", main: "./index.mjs", dependencies: { "firebase-functions-test": "latest", "firebase-admin": await getPackageVersion("firebase-admin"), "firebase-functions": await getPackageVersion("firebase-functions"), ...dependencies }, engines: { node: nodeVersion } }, null, 2 ) ); } const heroku = defineNitroPreset({ extends: "node-server" }); const layer0 = defineNitroPreset({ extends: "node", commands: { deploy: "cd ./ && npm run deploy", preview: "cd ./ && npm run preview" }, hooks: { async "compiled"(nitro) { const layer0Config = { connector: "./layer0", name: "nitro-app", routes: "routes.js", backends: {}, includeFiles: { "public/**/*": true, "server/**/*": true } }; const configPath = resolve(nitro.options.output.dir, "layer0.config.js"); await writeFile(configPath, `module.exports = ${JSON.stringify(layer0Config, null, 2)}`); const routerPath = resolve(nitro.options.output.dir, "routes.js"); await writeFile(routerPath, routesTemplate()); const connectorPath = resolve(nitro.options.output.dir, "layer0/prod.js"); await writeFile(connectorPath, entryTemplate()); const pkgJSON = { private: true, scripts: { deploy: "npm install && 0 deploy", preview: "npm install && 0 build && 0 run -p" }, devDependencies: { "@layer0/cli": "^4.13.2", "@layer0/core": "^4.13.2" } }; await writeFile(resolve(nitro.options.output.dir, "package.json"), JSON.stringify(pkgJSON, null, 2)); } } }); async function writeFile(path, contents) { await promises.mkdir(dirname(path), { recursive: true }); await promises.writeFile(path, contents, "utf-8"); } function entryTemplate() { return ` const http = require('http') module.exports = async function prod(port) { const { handler } = await import('../server/index.mjs') const server = http.createServer(handler) server.listen(port) } `.trim(); } function routesTemplate() { return ` import { Router } from '@layer0/core' const router = new Router() export default router router.fallback(({ renderWithApp }) => { renderWithApp() }) `.trim(); } const netlify = defineNitroPreset({ extends: "aws-lambda", entry: "#internal/nitro/entries/netlify", output: { dir: "{{ rootDir }}/.netlify/functions-internal", publicDir: "{{ rootDir }}/dist" }, rollupConfig: { output: { entryFileNames: "server.mjs" } }, hooks: { async "compiled"(nitro) { await writeHeaders(nitro); await writeRedirects(nitro); const serverCJSPath = join(nitro.options.output.serverDir, "server.js"); const serverJSCode = ` let _handler exports.handler = function handler (event, context) { if (_handler) { return _handler(event, context) } return import('./server.mjs').then(m => { _handler = m.handler return _handler(event, context) }) } `.trim(); await promises.writeFile(serverCJSPath, serverJSCode); } } }); const netlifyBuilder = defineNitroPreset({ extends: "netlify", entry: "#internal/nitro/entries/netlify-builder" }); const netlifyEdge = defineNitroPreset({ extends: "base-worker", entry: "#internal/nitro/entries/netlify-edge", output: { serverDir: "{{ rootDir }}/.netlify/edge-functions", publicDir: "{{ rootDir }}/dist" }, rollupConfig: { output: { entryFileNames: "server.js", format: "esm" } }, hooks: { async "compiled"(nitro) { const manifest = { version: 1, functions: [ { function: "server", pattern: "^.*$" } ] }; const manifestPath = join(nitro.options.rootDir, ".netlify/edge-functions/manifest.json"); await promises.mkdir(dirname(manifestPath), { recursive: true }); await promises.writeFile(manifestPath, JSON.stringify(manifest, null, 2)); } } }); async function writeRedirects(nitro) { const redirectsPath = join(nitro.options.output.publicDir, "_redirects"); let contents = "/* /.netlify/functions/server 200"; for (const [key] of Object.entries(nitro.options.routeRules).filter(([_, routeRules]) => routeRules.cache && (routeRules.cache?.static || routeRules.cache?.swr))) { contents = `${key.replace("/**", "/*")} /.netlify/builders/server 200 ` + contents; } for (const [key, routeRules] of Object.entries(nitro.options.routeRules).filter(([_, routeRules2]) => routeRules2.redirect)) { let code = routeRules.redirect.statusCode; code = { 307: 302, 308: 301 }[code] || code; contents = `${key.replace("/**", "/*")} ${routeRules.redirect.to} ${code} ` + contents; } if (existsSync(redirectsPath)) { const currentRedirects = await promises.readFile(redirectsPath, "utf-8"); if (currentRedirects.match(/^\/\* /m)) { nitro.logger.info("Not adding Nitro fallback to `_redirects` (as an existing fallback was found)."); return; } nitro.logger.info("Adding Nitro fallback to `_redirects` to handle all unmatched routes."); contents = currentRedirects + "\n" + contents; } await promises.writeFile(redirectsPath, contents); } async function writeHeaders(nitro) { const headersPath = join(nitro.options.output.publicDir, "_headers"); let contents = ""; for (const [path, routeRules] of Object.entries(nitro.options.routeRules).filter(([_, routeRules2]) => routeRules2.headers)) { const headers = [ path.replace("/**", "/*"), ...Object.entries({ ...routeRules.headers }).map(([header, value]) => ` ${header}: ${value}`) ].join("\n"); contents += headers + "\n"; } if (existsSync(headersPath)) { const currentHeaders = await promises.readFile(headersPath, "utf-8"); if (currentHeaders.match(/^\/\* /m)) { nitro.logger.info("Not adding Nitro fallback to `_headers` (as an existing fallback was found)."); return; } nitro.logger.info("Adding Nitro fallback to `_headers` to handle all unmatched routes."); contents = currentHeaders + "\n" + contents; } await promises.writeFile(headersPath, contents); } const nitroDev = defineNitroPreset({ extends: "node", entry: "#internal/nitro/entries/nitro-dev", output: { serverDir: "{{ buildDir }}/dev" }, externals: { trace: false }, inlineDynamicImports: true, sourceMap: true }); const nitroPrerender = defineNitroPreset({ extends: "node", entry: "#internal/nitro/entries/nitro-prerenderer", output: { serverDir: "{{ buildDir }}/prerender" }, commands: { preview: "npx serve -s ./public" }, externals: { trace: false } }); const cli = defineNitroPreset({ extends: "node", entry: "#internal/nitro/entries/cli", commands: { preview: "Run with node ./server/index.mjs [route]" } }); const nodeServer = defineNitroPreset({ extends: "node", entry: "#internal/nitro/entries/node-server", serveStatic: true, commands: { preview: "node ./server/index.mjs" } }); const nodeCluster = defineNitroPreset({ extends: "node-server", entry: "#internal/nitro/entries/node-cluster" }); const node = defineNitroPreset({ entry: "#internal/nitro/entries/node" }); const renderCom = defineNitroPreset({ extends: "node-server" }); const scriptTemplate = (baseURL = "/") => `