import { templateUtils } from '@nuxt/kit' import type { Nuxt, NuxtApp, NuxtTemplate } from '@nuxt/schema' import { genArrayFromRaw, genDynamicImport, genExport, genImport, genObjectFromRawEntries, genString } from 'knitwork' import { isAbsolute, join, relative } from 'pathe' import { resolveSchema, generateTypes } from 'untyped' import escapeRE from 'escape-string-regexp' export interface TemplateContext { nuxt: Nuxt app: NuxtApp } export const vueShim = { filename: 'types/vue-shim.d.ts', getContents: () => [ 'declare module \'*.vue\' {', ' import { DefineComponent } from \'@vue/runtime-core\'', ' const component: DefineComponent<{}, {}, any>', ' export default component', '}' ].join('\n') } // TODO: Use an alias export const appComponentTemplate = { filename: 'app-component.mjs', getContents: (ctx: TemplateContext) => genExport(ctx.app.mainComponent, ['default']) } // TODO: Use an alias export const rootComponentTemplate = { filename: 'root-component.mjs', getContents: (ctx: TemplateContext) => genExport(ctx.app.rootComponent, ['default']) } // TODO: Use an alias export const errorComponentTemplate = { filename: 'error-component.mjs', getContents: (ctx: TemplateContext) => genExport(ctx.app.errorComponent, ['default']) } export const cssTemplate = { filename: 'css.mjs', getContents: (ctx: TemplateContext) => ctx.nuxt.options.css.map(i => genImport(i)).join('\n') } export const clientPluginTemplate = { filename: 'plugins/client.mjs', getContents (ctx: TemplateContext) { const clientPlugins = ctx.app.plugins.filter(p => !p.mode || p.mode !== 'server') return [ templateUtils.importSources(clientPlugins.map(p => p.src)), `export default ${genArrayFromRaw(clientPlugins.map(p => templateUtils.importName(p.src)))}` ].join('\n') } } export const serverPluginTemplate = { filename: 'plugins/server.mjs', getContents (ctx: TemplateContext) { const serverPlugins = ctx.app.plugins.filter(p => !p.mode || p.mode !== 'client') return [ "import preload from '#app/plugins/preload.server'", templateUtils.importSources(serverPlugins.map(p => p.src)), `export default ${genArrayFromRaw([ 'preload', ...serverPlugins.map(p => templateUtils.importName(p.src)) ])}` ].join('\n') } } export const appViewTemplate = { filename: 'views/app.template.html', getContents () { return ` {{ HEAD }} {{ APP }} ` } } export const pluginsDeclaration = { filename: 'types/plugins.d.ts', getContents: (ctx: TemplateContext) => { const EXTENSION_RE = new RegExp(`(?<=\\w)(${ctx.nuxt.options.extensions.map(e => escapeRE(e)).join('|')})$`, 'g') const tsImports = ctx.app.plugins.map(p => (isAbsolute(p.src) ? relative(join(ctx.nuxt.options.buildDir, 'types'), p.src) : p.src).replace(EXTENSION_RE, '')) return `// Generated by Nuxt3' import type { Plugin } from '#app' type Decorate> = { [K in keyof T as K extends string ? \`$\${K}\` : never]: T[K] } type InjectionType = A extends Plugin ? Decorate : unknown type NuxtAppInjections = \n ${tsImports.map(p => `InjectionType`).join(' &\n ')} declare module '#app' { interface NuxtApp extends NuxtAppInjections { } } declare module '@vue/runtime-core' { interface ComponentCustomProperties extends NuxtAppInjections { } } export { } ` } } const adHocModules = ['router', 'pages', 'auto-imports', 'meta', 'components'] export const schemaTemplate = { filename: 'types/schema.d.ts', getContents: ({ nuxt }: TemplateContext) => { const moduleInfo = nuxt.options._installedModules.map(m => ({ ...m.meta || {}, importName: m.entryPath || m.meta?.name })).filter(m => m.configKey && m.name && !adHocModules.includes(m.name)) return [ "import { NuxtModule } from '@nuxt/schema'", "declare module '@nuxt/schema' {", ' interface NuxtConfig {', ...moduleInfo.filter(Boolean).map(meta => ` [${genString(meta.configKey)}]?: typeof ${genDynamicImport(meta.importName, { wrapper: false })}.default extends NuxtModule ? Partial : Record` ), ' }', generateTypes(resolveSchema(nuxt.options.publicRuntimeConfig), { interfaceName: 'PublicRuntimeConfig', addExport: false, addDefaults: false, allowExtraKeys: false, indentation: 2 }), generateTypes(resolveSchema(nuxt.options.privateRuntimeConfig), { interfaceName: 'PrivateRuntimeConfig', addExport: false, addDefaults: false, indentation: 2, allowExtraKeys: false, defaultDescrption: 'This value is only accessible from server-side.' }), '}' ].join('\n') } } // Add layouts template export const layoutTemplate: NuxtTemplate = { filename: 'layouts.mjs', getContents ({ app }) { const layoutsObject = genObjectFromRawEntries(Object.values(app.layouts).map(({ name, file }) => { return [name, `defineAsyncComponent(${genDynamicImport(file)})`] })) return [ 'import { defineAsyncComponent } from \'vue\'', `export default ${layoutsObject}` ].join('\n') } }