import { dirname, resolve, basename, extname, relative, normalize, isAbsolute, join } from 'pathe'; import { createHooks, createDebugger } from 'hookable'; import { useNuxt, resolveFiles, defineNuxtModule, addPlugin, addTemplate, addComponent, updateTemplates, addVitePlugin, addWebpackPlugin, findPath, isIgnored, resolveAlias, addPluginTemplate, logger, resolvePath, nuxtCtx, tryResolveModule, installModule, loadNuxtConfig, normalizeTemplate, compileTemplate, normalizePlugin, templateUtils, importModule } from '@nuxt/kit'; import escapeRE from 'escape-string-regexp'; import fse from 'fs-extra'; import { encodePath, parseURL, stringifyQuery, parseQuery, joinURL, withTrailingSlash, withoutLeadingSlash } from 'ufo'; import { existsSync, readdirSync, statSync, promises } from 'node:fs'; import { genArrayFromRaw, genSafeVariableName, genImport, genDynamicImport, genObjectFromRawEntries, genString, genExport } from 'knitwork'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { kebabCase, splitByCase, pascalCase, camelCase } from 'scule'; import { createUnplugin } from 'unplugin'; import { findStaticImports, findExports, parseStaticImport, resolvePath as resolvePath$1 } from 'mlly'; import { walk } from 'estree-walker'; import MagicString from 'magic-string'; import { globby } from 'globby'; import { hyphenate } from '@vue/shared'; import { parse, walk as walk$1, ELEMENT_NODE } from 'ultrahtml'; import { defineUnimportPreset, createUnimport, toImports, scanDirExports } from 'unimport'; import { createRequire } from 'node:module'; import { createTransformer } from 'unctx/transform'; import { stripLiteral } from 'strip-literal'; import { createNitro, scanHandlers, writeTypes, build as build$1, prepare, copyPublicAssets, prerender, createDevServer } from 'nitropack'; import defu from 'defu'; import { dynamicEventHandler } from 'h3'; import { createHeadCore } from 'unhead'; import { renderSSRHead } from '@unhead/ssr'; import chokidar from 'chokidar'; import { debounce } from 'perfect-debounce'; import { generateTypes, resolveSchema } from 'untyped'; import { hash } from 'ohash'; let _distDir = dirname(fileURLToPath(import.meta.url)); if (_distDir.match(/(chunks|shared)$/)) { _distDir = dirname(_distDir); } const distDir = _distDir; const pkgDir = resolve(distDir, ".."); resolve(distDir, "runtime"); function getNameFromPath(path) { return kebabCase(basename(path).replace(extname(path), "")).replace(/["']/g, ""); } function uniqueBy(arr, key) { const res = []; const seen = /* @__PURE__ */ new Set(); for (const item of arr) { if (seen.has(item[key])) { continue; } seen.add(item[key]); res.push(item); } return res; } function hasSuffix(path, suffix) { return basename(path).replace(extname(path), "").endsWith(suffix); } async function resolvePagesRoutes() { const nuxt = useNuxt(); const pagesDirs = nuxt.options._layers.map( (layer) => resolve(layer.config.srcDir, layer.config.dir?.pages || "pages") ); const allRoutes = (await Promise.all( pagesDirs.map(async (dir) => { const files = await resolveFiles(dir, `**/*{${nuxt.options.extensions.join(",")}}`); files.sort(); return generateRoutesFromFiles(files, dir); }) )).flat(); return uniqueBy(allRoutes, "path"); } function generateRoutesFromFiles(files, pagesDir) { const routes = []; for (const file of files) { const segments = relative(pagesDir, file).replace(new RegExp(`${escapeRE(extname(file))}$`), "").split("/"); const route = { name: "", path: "", file, children: [] }; let parent = routes; for (let i = 0; i < segments.length; i++) { const segment = segments[i]; const tokens = parseSegment(segment); const segmentName = tokens.map(({ value }) => value).join(""); route.name += (route.name && "-") + segmentName; const child = parent.find((parentRoute) => parentRoute.name === route.name && !parentRoute.path.endsWith("(.*)*")); if (child && child.children) { parent = child.children; route.path = ""; } else if (segmentName === "index" && !route.path) { route.path += "/"; } else if (segmentName !== "index") { route.path += getRoutePath(tokens); } } parent.push(route); } return prepareRoutes(routes); } function getRoutePath(tokens) { return tokens.reduce((path, token) => { return path + (token.type === 2 /* optional */ ? `:${token.value}?` : token.type === 1 /* dynamic */ ? `:${token.value}` : token.type === 3 /* catchall */ ? `:${token.value}(.*)*` : encodePath(token.value)); }, "/"); } const PARAM_CHAR_RE = /[\w\d_.]/; function parseSegment(segment) { let state = 0 /* initial */; let i = 0; let buffer = ""; const tokens = []; function consumeBuffer() { if (!buffer) { return; } if (state === 0 /* initial */) { throw new Error("wrong state"); } tokens.push({ type: state === 1 /* static */ ? 0 /* static */ : state === 2 /* dynamic */ ? 1 /* dynamic */ : state === 3 /* optional */ ? 2 /* optional */ : 3 /* catchall */, value: buffer }); buffer = ""; } while (i < segment.length) { const c = segment[i]; switch (state) { case 0 /* initial */: buffer = ""; if (c === "[") { state = 2 /* dynamic */; } else { i--; state = 1 /* static */; } break; case 1 /* static */: if (c === "[") { consumeBuffer(); state = 2 /* dynamic */; } else { buffer += c; } break; case 4 /* catchall */: case 2 /* dynamic */: case 3 /* optional */: if (buffer === "...") { buffer = ""; state = 4 /* catchall */; } if (c === "[" && state === 2 /* dynamic */) { state = 3 /* optional */; } if (c === "]" && (state !== 3 /* optional */ || buffer[buffer.length - 1] === "]")) { if (!buffer) { throw new Error("Empty param"); } else { consumeBuffer(); } state = 0 /* initial */; } else if (PARAM_CHAR_RE.test(c)) { buffer += c; } else ; break; } i++; } if (state === 2 /* dynamic */) { throw new Error(`Unfinished param "${buffer}"`); } consumeBuffer(); return tokens; } function prepareRoutes(routes, parent) { for (const route of routes) { if (route.name) { route.name = route.name.replace(/-index$/, ""); } if (parent && route.path.startsWith("/")) { route.path = route.path.slice(1); } if (route.children?.length) { route.children = prepareRoutes(route.children, route); } if (route.children?.find((childRoute) => childRoute.path === "")) { delete route.name; } } return routes; } function normalizeRoutes(routes, metaImports = /* @__PURE__ */ new Set()) { return { imports: metaImports, routes: genArrayFromRaw(routes.map((route) => { const file = normalize(route.file); const metaImportName = genSafeVariableName(file) + "Meta"; metaImports.add(genImport(`${file}?macro=true`, [{ name: "default", as: metaImportName }])); let aliasCode = `${metaImportName}?.alias || []`; if (Array.isArray(route.alias) && route.alias.length) { aliasCode = `${JSON.stringify(route.alias)}.concat(${aliasCode})`; } return { ...Object.fromEntries(Object.entries(route).map(([key, value]) => [key, JSON.stringify(value)])), name: `${metaImportName}?.name ?? ${route.name ? JSON.stringify(route.name) : "undefined"}`, path: `${metaImportName}?.path ?? ${JSON.stringify(route.path)}`, children: route.children ? normalizeRoutes(route.children, metaImports).routes : [], meta: route.meta ? `{...(${metaImportName} || {}), ...${JSON.stringify(route.meta)}}` : metaImportName, alias: aliasCode, redirect: route.redirect ? JSON.stringify(route.redirect) : `${metaImportName}?.redirect || undefined`, component: genDynamicImport(file, { interopDefault: true }) }; })) }; } const CODE_EMPTY = ` const __nuxt_page_meta = {} export default __nuxt_page_meta `; const CODE_HMR = ` // Vite if (import.meta.hot) { import.meta.hot.accept(mod => { Object.assign(__nuxt_page_meta, mod) }) } // Webpack if (import.meta.webpackHot) { import.meta.webpackHot.accept((err) => { if (err) { window.location = window.location.href } }) }`; const PageMetaPlugin = createUnplugin((options) => { return { name: "nuxt:pages-macros-transform", enforce: "post", transformInclude(id) { const query = parseMacroQuery(id); id = normalize(id); const isPagesDir = options.dirs.some((dir) => typeof dir === "string" ? id.startsWith(dir) : dir.test(id)); if (!isPagesDir && !query.macro) { return false; } const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href)); return /\.(m?[jt]sx?|vue)/.test(pathname); }, transform(code, id) { const query = parseMacroQuery(id); if (query.type && query.type !== "script") { return; } const s = new MagicString(code); function result() { if (s.hasChanged()) { return { code: s.toString(), map: options.sourcemap ? s.generateMap({ source: id, includeContent: true }) : void 0 }; } } const hasMacro = code.match(/\bdefinePageMeta\s*\(\s*/); if (!query.macro) { if (hasMacro) { walk(this.parse(code, { sourceType: "module", ecmaVersion: "latest" }), { enter(_node) { if (_node.type !== "CallExpression" || _node.callee.type !== "Identifier") { return; } const node = _node; const name = "name" in node.callee && node.callee.name; if (name === "definePageMeta") { s.overwrite(node.start, node.end, "false && {}"); } } }); } return result(); } const imports = findStaticImports(code); const scriptImport = imports.find((i) => parseMacroQuery(i.specifier).type === "script"); if (scriptImport) { const specifier = rewriteQuery(scriptImport.specifier); s.overwrite(0, code.length, `export { default } from ${JSON.stringify(specifier)}`); return result(); } const currentExports = findExports(code); for (const match of currentExports) { if (match.type !== "default" || !match.specifier) { continue; } const specifier = rewriteQuery(match.specifier); s.overwrite(0, code.length, `export { default } from ${JSON.stringify(specifier)}`); return result(); } if (!hasMacro && !code.includes("export { default }") && !code.includes("__nuxt_page_meta")) { s.overwrite(0, code.length, CODE_EMPTY + (options.dev ? CODE_HMR : "")); return result(); } const importMap = /* @__PURE__ */ new Map(); const addedImports = /* @__PURE__ */ new Set(); for (const i of imports) { const parsed = parseStaticImport(i); for (const name of [ parsed.defaultImport, ...Object.keys(parsed.namedImports || {}), parsed.namespacedImport ].filter(Boolean)) { importMap.set(name, i); } } walk(this.parse(code, { sourceType: "module", ecmaVersion: "latest" }), { enter(_node) { if (_node.type !== "CallExpression" || _node.callee.type !== "Identifier") { return; } const node = _node; const name = "name" in node.callee && node.callee.name; if (name !== "definePageMeta") { return; } const meta = node.arguments[0]; let contents = `const __nuxt_page_meta = ${code.slice(meta.start, meta.end) || "{}"} export default __nuxt_page_meta` + (options.dev ? CODE_HMR : ""); function addImport(name2) { if (name2 && importMap.has(name2)) { const importValue = importMap.get(name2).code; if (!addedImports.has(importValue)) { contents = importMap.get(name2).code + "\n" + contents; addedImports.add(importValue); } } } walk(meta, { enter(_node2) { if (_node2.type === "CallExpression") { const node2 = _node2; addImport("name" in node2.callee && node2.callee.name); } if (_node2.type === "Identifier") { const node2 = _node2; addImport(node2.name); } } }); s.overwrite(0, code.length, contents); } }); if (!s.hasChanged() && !code.includes("__nuxt_page_meta")) { s.overwrite(0, code.length, CODE_EMPTY + (options.dev ? CODE_HMR : "")); } return result(); }, vite: { handleHotUpdate: { order: "pre", handler: ({ modules }) => { const index = modules.findIndex((i) => i.id?.includes("?macro=true")); if (index !== -1) { modules.splice(index, 1); } } } } }; }); function rewriteQuery(id) { const query = stringifyQuery({ macro: "true", ...parseMacroQuery(id) }); return id.replace(/\?.+$/, "?" + query); } function parseMacroQuery(id) { const { search } = parseURL(decodeURIComponent(isAbsolute(id) ? pathToFileURL(id).href : id).replace(/\?macro=true$/, "")); const query = parseQuery(search); if (id.includes("?macro=true")) { return { macro: "true", ...query }; } return query; } const pagesModule = defineNuxtModule({ meta: { name: "pages" }, setup(_options, nuxt) { const pagesDirs = nuxt.options._layers.map( (layer) => resolve(layer.config.srcDir, layer.config.dir?.pages || "pages") ); const isNonEmptyDir = (dir) => existsSync(dir) && readdirSync(dir).length; const isPagesEnabled = () => { if (typeof nuxt.options.pages === "boolean") { return nuxt.options.pages; } if (nuxt.options._layers.some((layer) => existsSync(resolve(layer.config.srcDir, "app/router.options.ts")))) { return true; } if (pagesDirs.some((dir) => isNonEmptyDir(dir))) { return true; } return false; }; nuxt.options.pages = isPagesEnabled(); if (!nuxt.options.pages) { addPlugin(resolve(distDir, "app/plugins/router")); addTemplate({ filename: "pages.mjs", getContents: () => "export { useRoute } from '#app'" }); addComponent({ name: "NuxtPage", filePath: resolve(distDir, "pages/runtime/page-placeholder") }); return; } const runtimeDir = resolve(distDir, "pages/runtime"); nuxt.hook("prepare:types", ({ references }) => { references.push({ types: "vue-router" }); }); nuxt.hook("imports:sources", (sources) => { const routerImports = sources.find((s) => s.from === "#app" && s.imports.includes("onBeforeRouteLeave")); if (routerImports) { routerImports.from = "vue-router"; } }); nuxt.hook("builder:watch", async (event, path) => { const dirs = [ nuxt.options.dir.pages, nuxt.options.dir.layouts, nuxt.options.dir.middleware ].filter(Boolean); const pathPattern = new RegExp(`(^|\\/)(${dirs.map(escapeRE).join("|")})/`); if (event !== "change" && path.match(pathPattern)) { await updateTemplates({ filter: (template) => template.filename === "routes.mjs" }); } }); nuxt.hook("app:resolve", (app) => { if (app.mainComponent.includes("@nuxt/ui-templates")) { app.mainComponent = resolve(runtimeDir, "app.vue"); } app.middleware.unshift({ name: "validate", path: resolve(runtimeDir, "validate"), global: true }); }); if (!nuxt.options.dev && nuxt.options._generate) { const prerenderRoutes = /* @__PURE__ */ new Set(); nuxt.hook("modules:done", () => { nuxt.hook("pages:extend", (pages) => { prerenderRoutes.clear(); const processPages = (pages2, currentPath = "/") => { for (const page of pages2) { if (page.path.match(/^\/?:.*(\?|\(\.\*\)\*)$/) && !page.children?.length) { prerenderRoutes.add(currentPath); } if (page.path.includes(":")) { continue; } const route = joinURL(currentPath, page.path); prerenderRoutes.add(route); if (page.children) { processPages(page.children, route); } } }; processPages(pages); }); }); nuxt.hook("nitro:build:before", (nitro) => { for (const route of nitro.options.prerender.routes || []) { if (route === "/") { continue; } prerenderRoutes.add(route); } nitro.options.prerender.routes = Array.from(prerenderRoutes); }); } nuxt.hook("imports:extend", (imports) => { imports.push( { name: "definePageMeta", as: "definePageMeta", from: resolve(runtimeDir, "composables") }, { name: "useLink", as: "useLink", from: "vue-router" } ); }); const pageMetaOptions = { dev: nuxt.options.dev, sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client, dirs: nuxt.options._layers.map( (layer) => resolve(layer.config.srcDir, layer.config.dir?.pages || "pages") ) }; addVitePlugin(PageMetaPlugin.vite(pageMetaOptions)); addWebpackPlugin(PageMetaPlugin.webpack(pageMetaOptions)); addPlugin(resolve(runtimeDir, "router")); const getSources = (pages) => pages.flatMap( (p) => [relative(nuxt.options.srcDir, p.file), ...getSources(p.children || [])] ); nuxt.hook("build:manifest", async (manifest) => { const pages = await resolvePagesRoutes(); await nuxt.callHook("pages:extend", pages); const sourceFiles = getSources(pages); for (const key in manifest) { if (manifest[key].isEntry) { manifest[key].dynamicImports = manifest[key].dynamicImports?.filter((i) => !sourceFiles.includes(i)); } } }); addTemplate({ filename: "routes.mjs", async getContents() { const pages = await resolvePagesRoutes(); await nuxt.callHook("pages:extend", pages); const { routes, imports } = normalizeRoutes(pages); return [...imports, `export default ${routes}`].join("\n"); } }); addTemplate({ filename: "pages.mjs", getContents: () => "export { useRoute } from 'vue-router'" }); nuxt.options.vite.optimizeDeps = nuxt.options.vite.optimizeDeps || {}; nuxt.options.vite.optimizeDeps.include = nuxt.options.vite.optimizeDeps.include || []; nuxt.options.vite.optimizeDeps.include.push("vue-router"); addTemplate({ filename: "router.options.mjs", getContents: async () => { const routerOptionsFiles = (await Promise.all(nuxt.options._layers.map( async (layer) => await findPath(resolve(layer.config.srcDir, "app/router.options")) ))).filter(Boolean); routerOptionsFiles.push(resolve(runtimeDir, "router.options")); const configRouterOptions = genObjectFromRawEntries(Object.entries(nuxt.options.router.options).map(([key, value]) => [key, genString(value)])); return [ ...routerOptionsFiles.map((file, index) => genImport(file, `routerOptions${index}`)), `const configRouterOptions = ${configRouterOptions}`, "export default {", "...configRouterOptions,", ...routerOptionsFiles.map((_, index) => `...routerOptions${index},`).reverse(), "}" ].join("\n"); } }); addTemplate({ filename: "types/middleware.d.ts", getContents: ({ app }) => { const composablesFile = resolve(runtimeDir, "composables"); const namedMiddleware = app.middleware.filter((mw) => !mw.global); return [ "import type { NavigationGuard } from 'vue-router'", `export type MiddlewareKey = ${namedMiddleware.map((mw) => genString(mw.name)).join(" | ") || "string"}`, `declare module ${genString(composablesFile)} {`, " interface PageMeta {", " middleware?: MiddlewareKey | NavigationGuard | Array", " }", "}" ].join("\n"); } }); addTemplate({ filename: "types/layouts.d.ts", getContents: ({ app }) => { const composablesFile = resolve(runtimeDir, "composables"); return [ "import { ComputedRef, Ref } from 'vue'", `export type LayoutKey = ${Object.keys(app.layouts).map((name) => genString(name)).join(" | ") || "string"}`, `declare module ${genString(composablesFile)} {`, " interface PageMeta {", " layout?: false | LayoutKey | Ref | ComputedRef", " }", "}" ].join("\n"); } }); addComponent({ name: "NuxtPage", filePath: resolve(distDir, "pages/runtime/page") }); nuxt.hook("prepare:types", ({ references }) => { references.push({ path: resolve(nuxt.options.buildDir, "types/middleware.d.ts") }); references.push({ path: resolve(nuxt.options.buildDir, "types/layouts.d.ts") }); }); } }); const components = ["NoScript", "Link", "Base", "Title", "Meta", "Style", "Head", "Html", "Body"]; const metaModule = defineNuxtModule({ meta: { name: "meta" }, setup(options, nuxt) { const runtimeDir = nuxt.options.alias["#head"] || resolve(distDir, "head/runtime"); nuxt.options.build.transpile.push("@vueuse/head"); nuxt.options.alias["#head"] = runtimeDir; const componentsPath = resolve(runtimeDir, "components"); for (const componentName of components) { addComponent({ name: componentName, filePath: componentsPath, export: componentName, kebabName: componentName }); } addPlugin({ src: resolve(runtimeDir, "lib/vueuse-head.plugin") }); } }); const createImportMagicComments = (options) => { const { chunkName, prefetch, preload } = options; return [ `webpackChunkName: "${chunkName}"`, prefetch === true || typeof prefetch === "number" ? `webpackPrefetch: ${prefetch}` : false, preload === true || typeof preload === "number" ? `webpackPreload: ${preload}` : false ].filter(Boolean).join(", "); }; const componentsPluginTemplate = { filename: "components.plugin.mjs", getContents({ options }) { const globalComponents = options.getComponents().filter((c) => c.global === true); return `import { defineAsyncComponent } from 'vue' import { defineNuxtPlugin } from '#app' const components = ${genObjectFromRawEntries(globalComponents.map((c) => { const exp = c.export === "default" ? "c.default || c" : `c['${c.export}']`; const comment = createImportMagicComments(c); return [c.pascalName, `defineAsyncComponent(${genDynamicImport(c.filePath, { comment })}.then(c => ${exp}))`]; }))} export default defineNuxtPlugin(nuxtApp => { for (const name in components) { nuxtApp.vueApp.component(name, components[name]) nuxtApp.vueApp.component('Lazy' + name, components[name]) } }) `; } }; const componentsTemplate = { getContents({ options }) { const imports = /* @__PURE__ */ new Set(); imports.add("import { defineAsyncComponent } from 'vue'"); let num = 0; const components = options.getComponents(options.mode).flatMap((c) => { const exp = c.export === "default" ? "c.default || c" : `c['${c.export}']`; const comment = createImportMagicComments(c); const isClient = c.mode === "client"; const definitions = []; if (isClient) { num++; const identifier = `__nuxt_component_${num}`; imports.add(genImport("#app/components/client-only", [{ name: "createClientOnly" }])); imports.add(genImport(c.filePath, [{ name: c.export, as: identifier }])); definitions.push(`export const ${c.pascalName} = /*#__PURE__*/ createClientOnly(${identifier})`); } else { definitions.push(genExport(c.filePath, [{ name: c.export, as: c.pascalName }])); } definitions.push(`export const Lazy${c.pascalName} = defineAsyncComponent(${genDynamicImport(c.filePath, { comment })}.then(c => ${isClient ? `createClientOnly(${exp})` : exp}))`); return definitions; }); return [ ...imports, ...components, `export const componentNames = ${JSON.stringify(options.getComponents().map((c) => c.pascalName))}` ].join("\n"); } }; const componentsTypeTemplate = { filename: "components.d.ts", getContents: ({ options, nuxt }) => { const buildDir = nuxt.options.buildDir; const componentTypes = options.getComponents().map((c) => [ c.pascalName, `typeof ${genDynamicImport(isAbsolute(c.filePath) ? relative(buildDir, c.filePath).replace(/(?<=\w)\.(?!vue)\w+$/g, "") : c.filePath.replace(/(?<=\w)\.(?!vue)\w+$/g, ""), { wrapper: false })}['${c.export}']` ]); return `// Generated by components discovery declare module '@vue/runtime-core' { export interface GlobalComponents { ${componentTypes.map(([pascalName, type]) => ` '${pascalName}': ${type}`).join("\n")} ${componentTypes.map(([pascalName, type]) => ` 'Lazy${pascalName}': ${type}`).join("\n")} } } ${componentTypes.map(([pascalName, type]) => `export const ${pascalName}: ${type}`).join("\n")} ${componentTypes.map(([pascalName, type]) => `export const Lazy${pascalName}: ${type}`).join("\n")} export const componentNames: string[] `; } }; async function scanComponents(dirs, srcDir) { const components = []; const filePaths = /* @__PURE__ */ new Set(); const scannedPaths = []; for (const dir of dirs) { const resolvedNames = /* @__PURE__ */ new Map(); const files = (await globby(dir.pattern, { cwd: dir.path, ignore: dir.ignore })).sort(); for (const _file of files) { const filePath = join(dir.path, _file); if (scannedPaths.find((d) => filePath.startsWith(withTrailingSlash(d))) || isIgnored(filePath)) { continue; } if (filePaths.has(filePath)) { continue; } filePaths.add(filePath); const prefixParts = [].concat( dir.prefix ? splitByCase(dir.prefix) : [], dir.pathPrefix !== false ? splitByCase(relative(dir.path, dirname(filePath))) : [] ); let fileName = basename(filePath, extname(filePath)); const global = /\.(global)$/.test(fileName) || dir.global; const mode = fileName.match(/(?<=\.)(client|server)(\.global)?$/)?.[1] || "all"; fileName = fileName.replace(/(\.(client|server))?(\.global)?$/, ""); if (fileName.toLowerCase() === "index") { fileName = dir.pathPrefix === false ? basename(dirname(filePath)) : ""; } const fileNameParts = splitByCase(fileName); const componentNameParts = []; while (prefixParts.length && (prefixParts[0] || "").toLowerCase() !== (fileNameParts[0] || "").toLowerCase()) { componentNameParts.push(prefixParts.shift()); } const componentName = pascalCase(componentNameParts) + pascalCase(fileNameParts); const suffix = mode !== "all" ? `-${mode}` : ""; if (resolvedNames.has(componentName + suffix) || resolvedNames.has(componentName)) { console.warn( `Two component files resolving to the same name \`${componentName}\`: - ${filePath} - ${resolvedNames.get(componentName)}` ); continue; } resolvedNames.set(componentName + suffix, filePath); const pascalName = pascalCase(componentName).replace(/["']/g, ""); const kebabName = hyphenate(componentName); const shortPath = relative(srcDir, filePath); const chunkName = "components/" + kebabName + suffix; let component = { mode, global, prefetch: Boolean(dir.prefetch), preload: Boolean(dir.preload), filePath, pascalName, kebabName, chunkName, shortPath, export: "default" }; if (typeof dir.extendComponent === "function") { component = await dir.extendComponent(component) || component; } if (!components.some((c) => c.pascalName === component.pascalName && ["all", component.mode].includes(c.mode))) { components.push(component); } } scannedPaths.push(dir.path); } return components; } function isVueTemplate(id) { if (id.endsWith(".vue")) { return true; } const { search } = parseURL(decodeURIComponent(pathToFileURL(id).href)); if (!search) { return false; } const query = parseQuery(search); if (query.macro) { return true; } if (!("vue" in query) || query.type === "style") { return false; } return true; } const loaderPlugin = createUnplugin((options) => { const exclude = options.transform?.exclude || []; const include = options.transform?.include || []; return { name: "nuxt:components-loader", enforce: "post", transformInclude(id) { if (exclude.some((pattern) => id.match(pattern))) { return false; } if (include.some((pattern) => id.match(pattern))) { return true; } return isVueTemplate(id); }, transform(code, id) { const components = options.getComponents(); let num = 0; const imports = /* @__PURE__ */ new Set(); const map = /* @__PURE__ */ new Map(); const s = new MagicString(code); s.replace(/(?<=[ (])_?resolveComponent\(\s*["'](lazy-|Lazy)?([^'"]*?)["'][\s,]*[^)]*\)/g, (full, lazy, name) => { const component = findComponent(components, name, options.mode); if (component) { let identifier = map.get(component) || `__nuxt_component_${num++}`; map.set(component, identifier); const isClientOnly = component.mode === "client"; if (isClientOnly) { imports.add(genImport("#app/components/client-only", [{ name: "createClientOnly" }])); identifier += "_client"; } if (lazy) { imports.add(genImport("vue", [{ name: "defineAsyncComponent", as: "__defineAsyncComponent" }])); identifier += "_lazy"; imports.add(`const ${identifier} = /*#__PURE__*/ __defineAsyncComponent(${genDynamicImport(component.filePath, { interopDefault: true })}${isClientOnly ? ".then(c => createClientOnly(c))" : ""})`); } else { imports.add(genImport(component.filePath, [{ name: component.export, as: identifier }])); if (isClientOnly) { imports.add(`const ${identifier}_wrapped = /*#__PURE__*/ createClientOnly(${identifier})`); identifier += "_wrapped"; } } return identifier; } return full; }); if (imports.size) { s.prepend([...imports, ""].join("\n")); } if (s.hasChanged()) { return { code: s.toString(), map: options.sourcemap ? s.generateMap({ source: id, includeContent: true }) : void 0 }; } } }; }); function findComponent(components, name, mode) { const id = pascalCase(name).replace(/["']/g, ""); const component = components.find((component2) => id === component2.pascalName && ["all", mode, void 0].includes(component2.mode)); if (!component && components.some((component2) => id === component2.pascalName)) { return components.find((component2) => component2.pascalName === "ServerPlaceholder"); } return component; } const PLACEHOLDER_RE = /^(v-slot|#)(fallback|placeholder)/; const TreeShakeTemplatePlugin = createUnplugin((options) => { const regexpMap = /* @__PURE__ */ new WeakMap(); return { name: "nuxt:tree-shake-template", enforce: "pre", transformInclude(id) { const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href)); return pathname.endsWith(".vue"); }, async transform(code, id) { const template = code.match(/