mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 23:22:02 +00:00
refactor: improved template utils (#393)
This commit is contained in:
parent
c9ed8bc341
commit
cc212cedc8
@ -59,7 +59,7 @@ export default defineNuxtCommand({
|
||||
const dLoad = debounce(load, 250)
|
||||
const watcher = chokidar.watch([rootDir], { ignoreInitial: true, depth: 1 })
|
||||
watcher.on('all', (_event, file) => {
|
||||
if (file.includes('nuxt.config') || file.includes('modules')) {
|
||||
if (file.includes('nuxt.config') || file.includes('modules') || file.includes('pages')) {
|
||||
dLoad(true)
|
||||
}
|
||||
})
|
||||
|
@ -63,15 +63,17 @@ export default defineNuxtModule({
|
||||
}
|
||||
|
||||
app.templates.push({
|
||||
path: 'components.mjs',
|
||||
filename: 'components.mjs',
|
||||
src: resolve(__dirname, 'runtime/components.tmpl.mjs'),
|
||||
data: { components }
|
||||
options: { components }
|
||||
})
|
||||
|
||||
app.templates.push({
|
||||
path: 'components.d.ts',
|
||||
filename: 'components.d.ts',
|
||||
src: resolve(__dirname, 'runtime/components.tmpl.d.ts'),
|
||||
data: { components }
|
||||
options: { components }
|
||||
})
|
||||
|
||||
app.plugins.push({ src: '#build/components' })
|
||||
})
|
||||
|
||||
|
@ -1,13 +1,8 @@
|
||||
import type { Nuxt } from '../types/nuxt'
|
||||
import {
|
||||
addTemplate,
|
||||
addErrorLayout,
|
||||
addLayout,
|
||||
addPlugin,
|
||||
addServerMiddleware,
|
||||
extendBuild,
|
||||
extendRoutes
|
||||
} from './utils'
|
||||
import path from 'upath'
|
||||
import consola from 'consola'
|
||||
import type { Nuxt, NuxtPluginTemplate, NuxtTemplate } from '../types/nuxt'
|
||||
import { chainFn } from '../utils/task'
|
||||
import { addTemplate, addPluginTemplate, addServerMiddleware } from './utils'
|
||||
import { installModule } from './install'
|
||||
|
||||
/** Legacy ModuleContainer for backwards compatibility with existing Nuxt 2 modules. */
|
||||
@ -39,7 +34,7 @@ export function createModuleContainer (nuxt: Nuxt) {
|
||||
addTemplate,
|
||||
|
||||
/**
|
||||
* Registers a plugin using `addTemplate` and prepends it to the plugins[] array.
|
||||
* Registers a plugin template and prepends it to the plugins[] array.
|
||||
*
|
||||
* Note: You can use mode or .client and .server modifiers with fileName option
|
||||
* to use plugin only in client or server side.
|
||||
@ -56,26 +51,52 @@ export function createModuleContainer (nuxt: Nuxt) {
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
addPlugin,
|
||||
addPlugin (pluginTemplate: NuxtPluginTemplate): NuxtPluginTemplate {
|
||||
return addPluginTemplate(pluginTemplate)
|
||||
},
|
||||
|
||||
/** Register a custom layout. If its name is 'error' it will override the default error layout. */
|
||||
addLayout,
|
||||
addLayout (tmpl: NuxtTemplate, name: string) {
|
||||
const { filename, src } = addTemplate(tmpl)
|
||||
const layoutName = name || path.parse(src).name
|
||||
const layout = nuxt.options.layouts[layoutName]
|
||||
|
||||
if (layout) {
|
||||
consola.warn(`Duplicate layout registration, "${layoutName}" has been registered as "${layout}"`)
|
||||
}
|
||||
|
||||
// Add to nuxt layouts
|
||||
nuxt.options.layouts[layoutName] = `./${filename}`
|
||||
|
||||
// If error layout, set ErrorPage
|
||||
if (name === 'error') {
|
||||
this.addErrorLayout(filename)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the layout that will render Nuxt errors. It should already have been added via addLayout or addTemplate.
|
||||
*
|
||||
* @param dst - Path to layout file within the buildDir (`.nuxt/<dst>.vue`)
|
||||
*/
|
||||
addErrorLayout,
|
||||
addErrorLayout (dst: string) {
|
||||
const relativeBuildDir = path.relative(nuxt.options.rootDir, nuxt.options.buildDir)
|
||||
nuxt.options.ErrorPage = `~/${relativeBuildDir}/${dst}`
|
||||
},
|
||||
|
||||
/** Adds a new server middleware to the end of the server middleware array. */
|
||||
addServerMiddleware,
|
||||
|
||||
/** Allows extending webpack build config by chaining `options.build.extend` function. */
|
||||
extendBuild,
|
||||
extendBuild (fn) {
|
||||
// @ts-ignore
|
||||
nuxt.options.build.extend = chainFn(nuxt.options.build.extend, fn)
|
||||
},
|
||||
|
||||
/** Allows extending routes by chaining `options.build.extendRoutes` function. */
|
||||
extendRoutes,
|
||||
extendRoutes (fn) {
|
||||
nuxt.options.router.extendRoutes = chainFn(nuxt.options.router.extendRoutes, fn)
|
||||
},
|
||||
|
||||
/** `requireModule` is a shortcut for `addModule` */
|
||||
requireModule: installModule,
|
||||
|
@ -1,144 +1,144 @@
|
||||
import fs from 'fs'
|
||||
import path, { basename, parse } from 'upath'
|
||||
import { basename, parse, resolve } from 'upath'
|
||||
import hash from 'hash-sum'
|
||||
import consola from 'consola'
|
||||
import type { WebpackPluginInstance, Configuration as WebpackConfig } from 'webpack'
|
||||
import type { Plugin as VitePlugin, UserConfig as ViteConfig } from 'vite'
|
||||
import { useNuxt } from '../nuxt'
|
||||
import { chainFn } from '../utils/task'
|
||||
import type { TemplateOpts, PluginTemplateOpts } from '../types/module'
|
||||
import type { NuxtTemplate, NuxtPlugin, NuxtPluginTemplate } from '../types/nuxt'
|
||||
|
||||
/**
|
||||
* Renders given template using lodash template during build into the project buildDir (`.nuxt`).
|
||||
*
|
||||
* If a fileName is not provided or the template is string, target file name defaults to
|
||||
* [dirName].[fileName].[pathHash].[ext].
|
||||
*
|
||||
* Renders given template using lodash template during build into the project buildDir
|
||||
*/
|
||||
export function addTemplate (tmpl: TemplateOpts | string) {
|
||||
export function addTemplate (_template: NuxtTemplate | string) {
|
||||
const nuxt = useNuxt()
|
||||
|
||||
if (!tmpl) {
|
||||
throw new Error('Invalid tmpl: ' + JSON.stringify(tmpl))
|
||||
}
|
||||
// Noprmalize template
|
||||
const template = normalizeTemplate(_template)
|
||||
|
||||
// Validate & parse source
|
||||
const src = typeof tmpl === 'string' ? tmpl : tmpl.src
|
||||
const srcPath = parse(src)
|
||||
// Remove any existing template with the same filename
|
||||
nuxt.options.build.templates = nuxt.options.build.templates
|
||||
.filter(p => normalizeTemplate(p).filename !== template.filename)
|
||||
|
||||
if (typeof src !== 'string' || !fs.existsSync(src)) {
|
||||
throw new Error('tmpl src not found: ' + src)
|
||||
}
|
||||
// Add to templates array
|
||||
nuxt.options.build.templates.push(template)
|
||||
|
||||
// Mostly for DX, some people prefer `filename` vs `fileName`
|
||||
const fileName = typeof tmpl === 'string' ? '' : tmpl.fileName || tmpl.filename
|
||||
// Generate unique and human readable dst filename if not provided
|
||||
const dst = fileName || `${basename(srcPath.dir)}.${srcPath.name}.${hash(src)}${srcPath.ext}`
|
||||
// Add to tmpls list
|
||||
const tmplObj = {
|
||||
src,
|
||||
dst,
|
||||
options: typeof tmpl === 'string' ? undefined : tmpl.options
|
||||
}
|
||||
|
||||
nuxt.options.build.templates.push(tmplObj)
|
||||
|
||||
return tmplObj
|
||||
return template
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a plugin using `addTemplate` and prepends it to the plugins[] array.
|
||||
* Normalize a nuxt template object
|
||||
*/
|
||||
export function normalizeTemplate (template: NuxtTemplate | string): NuxtTemplate {
|
||||
if (!template) {
|
||||
throw new Error('Invalid template: ' + JSON.stringify(template))
|
||||
}
|
||||
|
||||
// Normalize
|
||||
if (typeof template === 'string') {
|
||||
template = { src: template }
|
||||
}
|
||||
|
||||
// Use src if provided
|
||||
if (template.src) {
|
||||
if (!fs.existsSync(template.src)) {
|
||||
throw new Error('Template not found: ' + template.src)
|
||||
}
|
||||
if (!template.filename) {
|
||||
const srcPath = parse(template.src)
|
||||
template.filename = template.fileName ||
|
||||
`${basename(srcPath.dir)}.${srcPath.name}.${hash(template.src)}${srcPath.ext}`
|
||||
}
|
||||
}
|
||||
|
||||
if (!template.src && !template.getContents) {
|
||||
throw new Error('Invalid template. Either getContents or src options should be provided: ' + JSON.stringify(template))
|
||||
}
|
||||
|
||||
if (!template.filename) {
|
||||
throw new Error('Invalid template. Either filename should be provided: ' + JSON.stringify(template))
|
||||
}
|
||||
|
||||
// Resolve dst
|
||||
if (!template.dst) {
|
||||
const nuxt = useNuxt()
|
||||
template.dst = resolve(nuxt.options.buildDir, template.filename)
|
||||
}
|
||||
|
||||
return template
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a nuxt plugin object
|
||||
*/
|
||||
export function normalizePlugin (plugin: NuxtPlugin | string): NuxtPlugin {
|
||||
// Normalize src
|
||||
if (typeof plugin === 'string') {
|
||||
plugin = { src: plugin }
|
||||
}
|
||||
if (!plugin.src) {
|
||||
throw new Error('Invalid plugin. src option is required: ' + JSON.stringify(plugin))
|
||||
}
|
||||
|
||||
// Normalize mode
|
||||
if (plugin.ssr) {
|
||||
plugin.mode = 'server'
|
||||
}
|
||||
if (!plugin.mode) {
|
||||
const [, mode = 'all'] = plugin.src.match(/\.(server|client)(\.\w+)*$/) || []
|
||||
plugin.mode = mode as 'all' | 'client' | 'server'
|
||||
}
|
||||
|
||||
return plugin
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a nuxt plugin and to the plugins array.
|
||||
*
|
||||
* Note: You can use mode or .client and .server modifiers with fileName option
|
||||
* to use plugin only in client or server side.
|
||||
*
|
||||
* If you choose to specify a fileName, you can configure a custom path for the
|
||||
* fileName too, so you can choose the folder structure inside .nuxt folder in
|
||||
* order to prevent name collisioning:
|
||||
* Note: By default plugin is prepended to the plugins array. You can use second argument to append (push) instead.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* addPlugin({
|
||||
* src: path.resolve(__dirname, 'templates/foo.js'),
|
||||
* fileName: 'foo.server.js' // [optional] only include in server bundle
|
||||
* filename: 'foo.server.js' // [optional] only include in server bundle
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
export function addPlugin (tmpl: PluginTemplateOpts) {
|
||||
export interface AddPluginOptions { append?: Boolean }
|
||||
export function addPlugin (_plugin: NuxtPlugin | string, opts: AddPluginOptions = {}) {
|
||||
const nuxt = useNuxt()
|
||||
|
||||
const { dst } = addTemplate(tmpl)
|
||||
// Normalize plugin
|
||||
const plugin = normalizePlugin(_plugin)
|
||||
|
||||
if (!tmpl.mode && typeof tmpl.ssr === 'boolean') {
|
||||
tmpl.mode = tmpl.ssr ? 'server' : 'client'
|
||||
}
|
||||
// Remove any existing plugin with the same src
|
||||
nuxt.options.plugins = nuxt.options.plugins.filter(p => normalizePlugin(p).src !== plugin.src)
|
||||
|
||||
// Add to nuxt plugins
|
||||
nuxt.options.plugins.unshift({
|
||||
src: path.join(nuxt.options.buildDir, dst),
|
||||
mode: tmpl.mode
|
||||
})
|
||||
}
|
||||
// Prepend to array by default to be before user provided plugins since is usually used by modules
|
||||
nuxt.options.plugins[opts.append ? 'push' : 'unshift'](plugin)
|
||||
|
||||
/** Register a custom layout. If its name is 'error' it will override the default error layout. */
|
||||
export function addLayout (tmpl: TemplateOpts, name: string) {
|
||||
const nuxt = useNuxt()
|
||||
|
||||
const { dst, src } = addTemplate(tmpl)
|
||||
const layoutName = name || path.parse(src).name
|
||||
const layout = nuxt.options.layouts[layoutName]
|
||||
|
||||
if (layout) {
|
||||
consola.warn(`Duplicate layout registration, "${layoutName}" has been registered as "${layout}"`)
|
||||
}
|
||||
|
||||
// Add to nuxt layouts
|
||||
nuxt.options.layouts[layoutName] = `./${dst}`
|
||||
|
||||
// If error layout, set ErrorPage
|
||||
if (name === 'error') {
|
||||
addErrorLayout(dst)
|
||||
}
|
||||
return plugin
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the layout that will render Nuxt errors. It should already have been added via addLayout or addTemplate.
|
||||
*
|
||||
* @param dst - Path to layout file within the buildDir (`.nuxt/<dst>.vue`)
|
||||
* Adds a template and registers as a nuxt plugin.
|
||||
*/
|
||||
export function addErrorLayout (dst: string) {
|
||||
const nuxt = useNuxt()
|
||||
|
||||
const relativeBuildDir = path.relative(nuxt.options.rootDir, nuxt.options.buildDir)
|
||||
nuxt.options.ErrorPage = `~/${relativeBuildDir}/${dst}`
|
||||
export function addPluginTemplate (plugin: NuxtPluginTemplate | string, opts: AddPluginOptions = {}): NuxtPluginTemplate {
|
||||
if (typeof plugin === 'string') {
|
||||
plugin = { src: plugin }
|
||||
}
|
||||
if (!plugin.src) {
|
||||
plugin.src = addTemplate(plugin).dst
|
||||
}
|
||||
return addPlugin(plugin, opts)
|
||||
}
|
||||
|
||||
/** Adds a new server middleware to the end of the server middleware array. */
|
||||
export function addServerMiddleware (middleware) {
|
||||
const nuxt = useNuxt()
|
||||
|
||||
nuxt.options.serverMiddleware.push(middleware)
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows extending webpack build config by chaining `options.build.extend` function.
|
||||
*
|
||||
* @deprecated use extendWebpackConfig() instead
|
||||
*/
|
||||
export function extendBuild (fn) {
|
||||
const nuxt = useNuxt()
|
||||
|
||||
// @ts-ignore TODO
|
||||
nuxt.options.build.extend = chainFn(nuxt.options.build.extend, fn)
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows extending routes by chaining `options.build.extendRoutes` function.
|
||||
*/
|
||||
export function extendRoutes (fn) {
|
||||
const nuxt = useNuxt()
|
||||
|
||||
nuxt.options.router.extendRoutes = chainFn(nuxt.options.router.extendRoutes, fn)
|
||||
useNuxt().options.serverMiddleware.push(middleware)
|
||||
}
|
||||
|
||||
export interface ExtendConfigOptions {
|
||||
|
@ -42,23 +42,3 @@ export type ModuleInstallOptions =
|
||||
ModuleSrc |
|
||||
[ModuleSrc, ModuleOptions?] |
|
||||
Partial<ModuleInstallOptionsObj>
|
||||
|
||||
// -- Templates --
|
||||
|
||||
export interface TemplateOpts {
|
||||
/** The target filename once the template is copied into the Nuxt buildDir */
|
||||
filename?: string
|
||||
/** The target filename once the template is copied into the Nuxt buildDir */
|
||||
fileName?: string
|
||||
/** An options object that will be accessible within the template via `<% options %>` */
|
||||
options?: Record<string, any>
|
||||
/** The resolved path to the source file to be templated */
|
||||
src: string
|
||||
}
|
||||
|
||||
export interface PluginTemplateOpts extends TemplateOpts {
|
||||
/** @deprecated use mode */
|
||||
ssr?: boolean
|
||||
/** Whether the plugin will be loaded on only server-side, only client-side or on both. */
|
||||
mode?: 'all' | 'server' | 'client'
|
||||
}
|
||||
|
@ -25,17 +25,26 @@ export interface Nuxt {
|
||||
vfs: Record<string, string>
|
||||
}
|
||||
|
||||
export interface NuxtPlugin {
|
||||
src: string
|
||||
mode?: 'server' | 'client' | 'all',
|
||||
export interface NuxtTemplate {
|
||||
/** @deprecated filename */
|
||||
fileName?: string
|
||||
/** resolved output file path (generated) */
|
||||
dst?: string
|
||||
/** The target filename once the template is copied into the Nuxt buildDir */
|
||||
filename?: string
|
||||
/** An options object that will be accessible within the template via `<% options %>` */
|
||||
options?: Record<string, any>
|
||||
/** The resolved path to the source file to be template */
|
||||
src?: string
|
||||
/** Provided compile option intead of src */
|
||||
getContents?: (data: Record<string, any>) => string | Promise<string>
|
||||
}
|
||||
|
||||
export interface NuxtTemplate {
|
||||
path: string // Relative path of destination
|
||||
src?: string // Absolute path to source file
|
||||
compile?: (data: Record<string, any>) => string
|
||||
data?: any
|
||||
export interface NuxtPlugin {
|
||||
/** @deprecated use mode */
|
||||
ssr?: boolean
|
||||
src: string
|
||||
mode?: 'all' | 'server' | 'client'
|
||||
}
|
||||
|
||||
export interface NuxtApp {
|
||||
@ -45,3 +54,6 @@ export interface NuxtApp {
|
||||
plugins: NuxtPlugin[]
|
||||
templates: NuxtTemplate[]
|
||||
}
|
||||
|
||||
type _TemplatePlugin = NuxtPlugin & NuxtTemplate
|
||||
export interface NuxtPluginTemplate extends _TemplatePlugin {}
|
||||
|
@ -2,7 +2,7 @@ import { resolve, join, relative } from 'upath'
|
||||
import globby from 'globby'
|
||||
import lodashTemplate from 'lodash/template'
|
||||
import defu from 'defu'
|
||||
import { tryResolvePath, resolveFiles, Nuxt, NuxtApp, NuxtTemplate, NuxtPlugin } from '@nuxt/kit'
|
||||
import { tryResolvePath, resolveFiles, Nuxt, NuxtApp, NuxtTemplate, normalizePlugin, normalizeTemplate } from '@nuxt/kit'
|
||||
import { readFile } from 'fs-extra'
|
||||
import * as templateUtils from './template.utils'
|
||||
|
||||
@ -11,7 +11,7 @@ export function createApp (nuxt: Nuxt, options: Partial<NuxtApp> = {}): NuxtApp
|
||||
dir: nuxt.options.srcDir,
|
||||
extensions: nuxt.options.extensions,
|
||||
plugins: [],
|
||||
templates: {}
|
||||
templates: []
|
||||
} as NuxtApp)
|
||||
}
|
||||
|
||||
@ -19,39 +19,31 @@ export async function generateApp (nuxt: Nuxt, app: NuxtApp) {
|
||||
// Resolve app
|
||||
await resolveApp(nuxt, app)
|
||||
|
||||
// Scan templates
|
||||
// Scan app templates
|
||||
const templatesDir = join(nuxt.options.appDir, '_templates')
|
||||
const templateFiles = await globby(join(templatesDir, '/**'))
|
||||
app.templates = templateFiles
|
||||
.filter(src => !src.endsWith('.d.ts'))
|
||||
.map(src => ({
|
||||
src,
|
||||
path: relative(templatesDir, src),
|
||||
data: {}
|
||||
} as NuxtTemplate))
|
||||
.map(src => ({ src, filename: relative(templatesDir, src) } as NuxtTemplate))
|
||||
|
||||
// Custom templates (created with addTemplate)
|
||||
const customTemplates = nuxt.options.build.templates.map(t => ({
|
||||
path: t.dst,
|
||||
src: t.src,
|
||||
data: {
|
||||
options: t.options
|
||||
}
|
||||
}))
|
||||
app.templates = app.templates.concat(customTemplates)
|
||||
// User templates from options.build.templates
|
||||
app.templates = app.templates.concat(nuxt.options.build.templates)
|
||||
|
||||
// Extend templates
|
||||
// Extend templates with hook
|
||||
await nuxt.callHook('app:templates', app)
|
||||
|
||||
// Normalize templates
|
||||
app.templates = app.templates.map(tmpl => normalizeTemplate(tmpl))
|
||||
|
||||
// Compile templates into vfs
|
||||
const templateContext = { utils: templateUtils, nuxt, app }
|
||||
await Promise.all(app.templates.map(async (template) => {
|
||||
const contents = await compileTemplate(template, templateContext)
|
||||
|
||||
const fullPath = resolve(nuxt.options.buildDir, template.path)
|
||||
const fullPath = template.dst || resolve(nuxt.options.buildDir, template.filename)
|
||||
nuxt.vfs[fullPath] = contents
|
||||
|
||||
const aliasPath = '#build/' + template.path.replace(/\.\w+$/, '')
|
||||
const aliasPath = '#build/' + template.filename.replace(/\.\w+$/, '')
|
||||
nuxt.vfs[aliasPath] = contents
|
||||
}))
|
||||
|
||||
@ -76,41 +68,26 @@ export async function resolveApp (nuxt: Nuxt, app: NuxtApp) {
|
||||
// Resolve plugins
|
||||
app.plugins = [
|
||||
...nuxt.options.plugins,
|
||||
...await resolvePlugins(nuxt)
|
||||
]
|
||||
...await resolveFiles(nuxt.options.srcDir, 'plugins/**/*.{js,ts,mjs,cjs}')
|
||||
].map(plugin => normalizePlugin(plugin))
|
||||
|
||||
// Extend app
|
||||
await nuxt.callHook('app:resolve', app)
|
||||
}
|
||||
|
||||
async function compileTemplate (tmpl: NuxtTemplate, ctx: any) {
|
||||
const data = { ...ctx, ...tmpl.data }
|
||||
if (tmpl.src) {
|
||||
async function compileTemplate (template: NuxtTemplate, ctx: any) {
|
||||
const data = { ...ctx, ...template.options }
|
||||
if (template.src) {
|
||||
try {
|
||||
const srcContents = await readFile(tmpl.src, 'utf-8')
|
||||
const srcContents = await readFile(template.src, 'utf-8')
|
||||
return lodashTemplate(srcContents, {})(data)
|
||||
} catch (err) {
|
||||
console.error('Error compiling template: ', tmpl)
|
||||
console.error('Error compiling template: ', template)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
if (tmpl.compile) {
|
||||
return tmpl.compile(data)
|
||||
if (template.getContents) {
|
||||
return template.getContents(data)
|
||||
}
|
||||
throw new Error('Invalid template:' + tmpl)
|
||||
}
|
||||
|
||||
async function resolvePlugins (nuxt: Nuxt) {
|
||||
const plugins = await resolveFiles(nuxt.options.srcDir, 'plugins/**/*.{js,ts}')
|
||||
|
||||
return plugins.map(src => ({
|
||||
src,
|
||||
mode: getPluginMode(src)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function getPluginMode (src: string) {
|
||||
const [, mode = 'all'] = src.match(/\.(server|client)(\.\w+)*$/) || []
|
||||
return mode as NuxtPlugin['mode']
|
||||
throw new Error('Invalid template: ' + JSON.stringify(template))
|
||||
}
|
||||
|
@ -1,65 +1,60 @@
|
||||
import { existsSync } from 'fs'
|
||||
import { defineNuxtModule } from '@nuxt/kit'
|
||||
import { defineNuxtModule, addTemplate, addPlugin } from '@nuxt/kit'
|
||||
import { resolve } from 'upath'
|
||||
import { resolveLayouts, resolvePagesRoutes } from './utils'
|
||||
|
||||
export default defineNuxtModule({
|
||||
name: 'router',
|
||||
setup (_options, nuxt) {
|
||||
const runtimeDir = resolve(__dirname, 'runtime')
|
||||
const pagesDir = resolve(nuxt.options.srcDir, nuxt.options.dir.pages)
|
||||
const routerPlugin = resolve(runtimeDir, 'router')
|
||||
const runtimeDir = resolve(__dirname, 'runtime')
|
||||
|
||||
// Disable module if pages dir do not exists
|
||||
if (!existsSync(pagesDir)) {
|
||||
return
|
||||
}
|
||||
|
||||
// Regenerate templates when adding or removing pages
|
||||
nuxt.hook('builder:watch', async (event, path) => {
|
||||
// Regenerate templates when adding or removing pages (plugin and routes)
|
||||
const pathPattern = new RegExp(`^(${nuxt.options.dir.pages}|${nuxt.options.dir.layouts})/`)
|
||||
if (event !== 'change' && path.match(pathPattern)) {
|
||||
await nuxt.callHook('builder:generateApp')
|
||||
}
|
||||
})
|
||||
|
||||
// Add default layout for pages
|
||||
nuxt.hook('app:resolve', (app) => {
|
||||
if (!existsSync(pagesDir)) {
|
||||
return
|
||||
}
|
||||
app.plugins.push({ src: routerPlugin })
|
||||
if (app.main.includes('app.tutorial')) {
|
||||
app.main = resolve(runtimeDir, 'app.vue')
|
||||
}
|
||||
})
|
||||
|
||||
nuxt.hook('app:templates', async (app) => {
|
||||
if (!existsSync(pagesDir)) {
|
||||
return
|
||||
// Add router plguin
|
||||
addPlugin(resolve(runtimeDir, 'router'))
|
||||
|
||||
// Add routes template
|
||||
addTemplate({
|
||||
filename: 'routes.mjs',
|
||||
async getContents () {
|
||||
const routes = await resolvePagesRoutes(nuxt)
|
||||
const serializedRoutes = routes.map(route => ({ ...route, component: `{() => import('${route.file}')}` }))
|
||||
return `export default ${JSON.stringify(serializedRoutes, null, 2).replace(/"{(.+)}"/g, '$1')}`
|
||||
}
|
||||
})
|
||||
|
||||
// Resolve routes
|
||||
const routes = await resolvePagesRoutes(nuxt)
|
||||
|
||||
// Add routes.js
|
||||
app.templates.push({
|
||||
path: 'routes.js',
|
||||
compile: () => {
|
||||
const serializedRoutes = routes.map(route => ({ ...route, component: `{() => import('${route.file}')}` }))
|
||||
return `export default ${JSON.stringify(serializedRoutes, null, 2).replace(/"{(.+)}"/g, '$1')}`
|
||||
}
|
||||
})
|
||||
|
||||
const layouts = await resolveLayouts(nuxt)
|
||||
|
||||
// Add routes.js
|
||||
app.templates.push({
|
||||
path: 'layouts.js',
|
||||
compile: () => {
|
||||
const layoutsObject = Object.fromEntries(layouts.map(({ name, file }) => {
|
||||
return [name, `{defineAsyncComponent({ suspensible: false, loader: () => import('${file}') })}`]
|
||||
}))
|
||||
return [
|
||||
'import { defineAsyncComponent } from \'vue\'',
|
||||
`export default ${JSON.stringify(layoutsObject, null, 2).replace(/"{(.+)}"/g, '$1')}
|
||||
`].join('\n')
|
||||
}
|
||||
})
|
||||
// Add layouts template
|
||||
addTemplate({
|
||||
filename: 'layouts.mjs',
|
||||
async getContents () {
|
||||
const layouts = await resolveLayouts(nuxt)
|
||||
const layoutsObject = Object.fromEntries(layouts.map(({ name, file }) => {
|
||||
return [name, `{defineAsyncComponent({ suspensible: false, loader: () => import('${file}') })}`]
|
||||
}))
|
||||
return [
|
||||
'import { defineAsyncComponent } from \'vue\'',
|
||||
`export default ${JSON.stringify(layoutsObject, null, 2).replace(/"{(.+)}"/g, '$1')}`
|
||||
].join('\n')
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user