2021-09-29 10:10:46 +00:00
|
|
|
import { existsSync, promises as fsp } from 'fs'
|
|
|
|
import { basename, extname, parse, resolve } from 'pathe'
|
2021-10-02 16:01:17 +00:00
|
|
|
import lodashTemplate from 'lodash.template'
|
2021-04-02 11:47:01 +00:00
|
|
|
import hash from 'hash-sum'
|
2021-07-26 10:45:42 +00:00
|
|
|
import type { WebpackPluginInstance, Configuration as WebpackConfig } from 'webpack'
|
2021-07-26 19:06:27 +00:00
|
|
|
import type { Plugin as VitePlugin, UserConfig as ViteConfig } from 'vite'
|
2021-09-29 10:10:46 +00:00
|
|
|
import { camelCase } from 'scule'
|
2021-04-02 11:47:01 +00:00
|
|
|
import { useNuxt } from '../nuxt'
|
2021-07-28 11:35:24 +00:00
|
|
|
import type { NuxtTemplate, NuxtPlugin, NuxtPluginTemplate } from '../types/nuxt'
|
2021-04-02 11:47:01 +00:00
|
|
|
|
2021-04-15 18:49:29 +00:00
|
|
|
/**
|
2021-07-28 11:35:24 +00:00
|
|
|
* Renders given template using lodash template during build into the project buildDir
|
2021-04-15 18:49:29 +00:00
|
|
|
*/
|
2021-07-28 11:35:24 +00:00
|
|
|
export function addTemplate (_template: NuxtTemplate | string) {
|
2021-04-02 11:47:01 +00:00
|
|
|
const nuxt = useNuxt()
|
|
|
|
|
2021-07-28 11:35:24 +00:00
|
|
|
// Noprmalize template
|
|
|
|
const template = normalizeTemplate(_template)
|
|
|
|
|
|
|
|
// Remove any existing template with the same filename
|
|
|
|
nuxt.options.build.templates = nuxt.options.build.templates
|
|
|
|
.filter(p => normalizeTemplate(p).filename !== template.filename)
|
|
|
|
|
|
|
|
// Add to templates array
|
|
|
|
nuxt.options.build.templates.push(template)
|
|
|
|
|
|
|
|
return template
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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 }
|
2021-08-10 00:27:23 +00:00
|
|
|
} else {
|
|
|
|
template = { ...template }
|
2021-07-28 11:35:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Use src if provided
|
|
|
|
if (template.src) {
|
2021-09-29 10:10:46 +00:00
|
|
|
if (!existsSync(template.src)) {
|
2021-07-28 11:35:24 +00:00
|
|
|
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}`
|
|
|
|
}
|
2021-04-02 11:47:01 +00:00
|
|
|
}
|
|
|
|
|
2021-07-28 11:35:24 +00:00
|
|
|
if (!template.src && !template.getContents) {
|
|
|
|
throw new Error('Invalid template. Either getContents or src options should be provided: ' + JSON.stringify(template))
|
|
|
|
}
|
2021-04-02 11:47:01 +00:00
|
|
|
|
2021-07-28 11:35:24 +00:00
|
|
|
if (!template.filename) {
|
|
|
|
throw new Error('Invalid template. Either filename should be provided: ' + JSON.stringify(template))
|
2021-04-02 11:47:01 +00:00
|
|
|
}
|
|
|
|
|
2021-07-28 11:35:24 +00:00
|
|
|
// Resolve dst
|
|
|
|
if (!template.dst) {
|
|
|
|
const nuxt = useNuxt()
|
|
|
|
template.dst = resolve(nuxt.options.buildDir, template.filename)
|
2021-04-02 11:47:01 +00:00
|
|
|
}
|
|
|
|
|
2021-07-28 11:35:24 +00:00
|
|
|
return template
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Normalize a nuxt plugin object
|
|
|
|
*/
|
|
|
|
export function normalizePlugin (plugin: NuxtPlugin | string): NuxtPlugin {
|
|
|
|
// Normalize src
|
|
|
|
if (typeof plugin === 'string') {
|
|
|
|
plugin = { src: plugin }
|
2021-08-10 00:27:23 +00:00
|
|
|
} else {
|
|
|
|
plugin = { ...plugin }
|
2021-07-28 11:35:24 +00:00
|
|
|
}
|
2021-08-10 00:27:23 +00:00
|
|
|
|
2021-07-28 11:35:24 +00:00
|
|
|
if (!plugin.src) {
|
|
|
|
throw new Error('Invalid plugin. src option is required: ' + JSON.stringify(plugin))
|
|
|
|
}
|
2021-04-02 11:47:01 +00:00
|
|
|
|
2021-07-28 11:35:24 +00:00
|
|
|
// 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
|
2021-04-02 11:47:01 +00:00
|
|
|
}
|
|
|
|
|
2021-04-15 18:49:29 +00:00
|
|
|
/**
|
2021-07-28 11:35:24 +00:00
|
|
|
* Registers a nuxt plugin and to the plugins array.
|
2021-04-15 18:49:29 +00:00
|
|
|
*
|
|
|
|
* Note: You can use mode or .client and .server modifiers with fileName option
|
|
|
|
* to use plugin only in client or server side.
|
|
|
|
*
|
2021-07-28 11:35:24 +00:00
|
|
|
* Note: By default plugin is prepended to the plugins array. You can use second argument to append (push) instead.
|
2021-04-15 18:49:29 +00:00
|
|
|
*
|
|
|
|
* @example
|
|
|
|
* ```js
|
|
|
|
* addPlugin({
|
|
|
|
* src: path.resolve(__dirname, 'templates/foo.js'),
|
2021-07-28 11:35:24 +00:00
|
|
|
* filename: 'foo.server.js' // [optional] only include in server bundle
|
2021-04-15 18:49:29 +00:00
|
|
|
* })
|
|
|
|
* ```
|
|
|
|
*/
|
2021-07-28 11:35:24 +00:00
|
|
|
export interface AddPluginOptions { append?: Boolean }
|
|
|
|
export function addPlugin (_plugin: NuxtPlugin | string, opts: AddPluginOptions = {}) {
|
2021-04-02 11:47:01 +00:00
|
|
|
const nuxt = useNuxt()
|
|
|
|
|
2021-07-28 11:35:24 +00:00
|
|
|
// Normalize plugin
|
|
|
|
const plugin = normalizePlugin(_plugin)
|
2021-04-02 11:47:01 +00:00
|
|
|
|
2021-07-28 11:35:24 +00:00
|
|
|
// Remove any existing plugin with the same src
|
|
|
|
nuxt.options.plugins = nuxt.options.plugins.filter(p => normalizePlugin(p).src !== plugin.src)
|
2021-04-02 11:47:01 +00:00
|
|
|
|
2021-07-28 11:35:24 +00:00
|
|
|
// 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)
|
2021-04-02 11:47:01 +00:00
|
|
|
|
2021-07-28 11:35:24 +00:00
|
|
|
return plugin
|
2021-04-02 11:47:01 +00:00
|
|
|
}
|
|
|
|
|
2021-04-15 18:49:29 +00:00
|
|
|
/**
|
2021-07-28 11:35:24 +00:00
|
|
|
* Adds a template and registers as a nuxt plugin.
|
2021-04-15 18:49:29 +00:00
|
|
|
*/
|
2021-07-28 11:35:24 +00:00
|
|
|
export function addPluginTemplate (plugin: NuxtPluginTemplate | string, opts: AddPluginOptions = {}): NuxtPluginTemplate {
|
|
|
|
if (typeof plugin === 'string') {
|
|
|
|
plugin = { src: plugin }
|
|
|
|
}
|
2021-08-16 20:00:43 +00:00
|
|
|
|
|
|
|
// Update plugin src to template destination
|
|
|
|
plugin.src = addTemplate(plugin).dst
|
|
|
|
|
2021-07-28 11:35:24 +00:00
|
|
|
return addPlugin(plugin, opts)
|
2021-04-02 11:47:01 +00:00
|
|
|
}
|
|
|
|
|
2021-04-15 18:49:29 +00:00
|
|
|
/** Adds a new server middleware to the end of the server middleware array. */
|
2021-04-02 11:47:01 +00:00
|
|
|
export function addServerMiddleware (middleware) {
|
2021-07-28 11:35:24 +00:00
|
|
|
useNuxt().options.serverMiddleware.push(middleware)
|
2021-04-02 11:47:01 +00:00
|
|
|
}
|
2021-07-26 10:45:42 +00:00
|
|
|
|
2021-07-26 19:06:27 +00:00
|
|
|
export interface ExtendConfigOptions {
|
2021-07-26 10:45:42 +00:00
|
|
|
/**
|
|
|
|
* Install plugin on dev
|
|
|
|
*
|
|
|
|
* @default true
|
|
|
|
*/
|
|
|
|
dev?: boolean
|
|
|
|
/**
|
|
|
|
* Install plugin on build
|
|
|
|
*
|
|
|
|
* @default true
|
|
|
|
*/
|
|
|
|
build?: boolean
|
|
|
|
}
|
|
|
|
|
2021-07-26 19:06:27 +00:00
|
|
|
export interface ExtendWebpackConfigOptions extends ExtendConfigOptions {
|
2021-07-26 10:45:42 +00:00
|
|
|
/**
|
|
|
|
* Install plugin on server side
|
|
|
|
*
|
|
|
|
* @default true
|
|
|
|
*/
|
|
|
|
server?: boolean
|
|
|
|
/**
|
|
|
|
* Install plugin on client side
|
|
|
|
*
|
|
|
|
* @default true
|
|
|
|
*/
|
|
|
|
client?: boolean
|
|
|
|
}
|
|
|
|
|
2021-07-26 19:06:27 +00:00
|
|
|
export interface ExtendViteConfigOptions extends ExtendConfigOptions {}
|
2021-07-26 10:45:42 +00:00
|
|
|
|
|
|
|
/**
|
2021-07-26 19:06:27 +00:00
|
|
|
* Extend Webpack config
|
|
|
|
*
|
|
|
|
* The fallback function might be called multiple times
|
|
|
|
* when applying to both client and server builds.
|
2021-07-26 10:45:42 +00:00
|
|
|
*/
|
2021-07-26 19:06:27 +00:00
|
|
|
export function extendWebpackConfig (
|
|
|
|
fn: ((config: WebpackConfig)=> void),
|
|
|
|
options: ExtendWebpackConfigOptions = {}
|
|
|
|
) {
|
2021-07-26 10:45:42 +00:00
|
|
|
const nuxt = useNuxt()
|
|
|
|
|
|
|
|
if (options.dev === false && nuxt.options.dev) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if (options.build === false && nuxt.options.build) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
nuxt.hook('webpack:config', (configs: WebpackConfig[]) => {
|
|
|
|
if (options.server !== false) {
|
|
|
|
const config = configs.find(i => i.name === 'server')
|
|
|
|
if (config) {
|
2021-07-26 19:06:27 +00:00
|
|
|
fn(config)
|
2021-07-26 10:45:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (options.client !== false) {
|
|
|
|
const config = configs.find(i => i.name === 'client')
|
|
|
|
if (config) {
|
2021-07-26 19:06:27 +00:00
|
|
|
fn(config)
|
2021-07-26 10:45:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-07-26 19:06:27 +00:00
|
|
|
* Extend Vite config
|
2021-07-26 10:45:42 +00:00
|
|
|
*/
|
2021-07-26 19:06:27 +00:00
|
|
|
export function extendViteConfig (
|
|
|
|
fn: ((config: ViteConfig) => void),
|
|
|
|
options: ExtendViteConfigOptions = {}
|
|
|
|
) {
|
2021-07-26 10:45:42 +00:00
|
|
|
const nuxt = useNuxt()
|
|
|
|
|
|
|
|
if (options.dev === false && nuxt.options.dev) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if (options.build === false && nuxt.options.build) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-07-26 19:06:27 +00:00
|
|
|
nuxt.hook('vite:extend', ({ config }) => fn(config))
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Append Webpack plugin to the config.
|
|
|
|
*/
|
|
|
|
export function addWebpackPlugin (plugin: WebpackPluginInstance, options?: ExtendWebpackConfigOptions) {
|
|
|
|
extendWebpackConfig((config) => {
|
|
|
|
config.plugins = config.plugins || []
|
|
|
|
config.plugins.push(plugin)
|
|
|
|
}, options)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Append Vite plugin to the config.
|
|
|
|
*/
|
|
|
|
export function addVitePlugin (plugin: VitePlugin, options?: ExtendViteConfigOptions) {
|
|
|
|
extendViteConfig((config) => {
|
2021-07-26 10:45:42 +00:00
|
|
|
config.plugins = config.plugins || []
|
|
|
|
config.plugins.push(plugin)
|
2021-07-26 19:06:27 +00:00
|
|
|
}, options)
|
2021-07-26 10:45:42 +00:00
|
|
|
}
|
2021-09-05 21:21:33 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if current nuxt instance is version 2 legacy
|
|
|
|
*/
|
|
|
|
export function isNuxt2 (nuxt?: any) {
|
2021-10-02 16:19:36 +00:00
|
|
|
nuxt = nuxt || useNuxt()
|
|
|
|
const version = (nuxt?.version || nuxt?.constructor?.version || '').replace(/^v|-.*$/g, '')
|
|
|
|
return version.startsWith('2.')
|
2021-09-05 21:21:33 +00:00
|
|
|
}
|
2021-09-29 10:10:46 +00:00
|
|
|
|
|
|
|
export async function compileTemplate (template: NuxtTemplate, ctx: any) {
|
|
|
|
const data = { ...ctx, ...template.options }
|
|
|
|
if (template.src) {
|
|
|
|
try {
|
|
|
|
const srcContents = await fsp.readFile(template.src, 'utf-8')
|
|
|
|
return lodashTemplate(srcContents, {})(data)
|
|
|
|
} catch (err) {
|
|
|
|
console.error('Error compiling template: ', template)
|
|
|
|
throw err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (template.getContents) {
|
|
|
|
return template.getContents(data)
|
|
|
|
}
|
|
|
|
throw new Error('Invalid template: ' + JSON.stringify(template))
|
|
|
|
}
|
|
|
|
|
|
|
|
const serialize = data => JSON.stringify(data, null, 2).replace(/"{(.+)}"/g, '$1')
|
|
|
|
|
|
|
|
const importName = (src: string) => `${camelCase(basename(src, extname(src))).replace(/[^a-zA-Z?\d\s:]/g, '')}_${hash(src)}`
|
|
|
|
|
|
|
|
const importSources = (sources: string | string[], { lazy = false } = {}) => {
|
|
|
|
if (!Array.isArray(sources)) {
|
|
|
|
sources = [sources]
|
|
|
|
}
|
|
|
|
return sources.map((src) => {
|
|
|
|
if (lazy) {
|
|
|
|
return `const ${importName(src)} = () => import('${src}' /* webpackChunkName: '${src}' */)`
|
|
|
|
}
|
|
|
|
return `import ${importName(src)} from '${src}'`
|
|
|
|
}).join('\n')
|
|
|
|
}
|
|
|
|
|
|
|
|
export const templateUtils = {
|
|
|
|
serialize,
|
|
|
|
importName,
|
|
|
|
importSources
|
|
|
|
}
|