mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 05:35:13 +00:00
perf(nuxt): extract and apply plugin order at build time (#21611)
This commit is contained in:
parent
bb4ed5e406
commit
2abcc16cfb
@ -89,6 +89,7 @@
|
||||
"prompts": "^2.4.2",
|
||||
"scule": "^1.0.0",
|
||||
"strip-literal": "^1.0.1",
|
||||
"typescript-estree": "^18.1.0",
|
||||
"ufo": "^1.1.2",
|
||||
"ultrahtml": "^1.2.0",
|
||||
"uncrypto": "^0.1.3",
|
||||
|
@ -8,11 +8,11 @@ import type { $Fetch, NitroFetchRequest } from 'nitropack'
|
||||
import { baseURL } from '#build/paths.mjs'
|
||||
|
||||
import type { CreateOptions } from '#app'
|
||||
import { applyPlugins, createNuxtApp, normalizePlugins } from '#app/nuxt'
|
||||
import { applyPlugins, createNuxtApp } from '#app/nuxt'
|
||||
|
||||
import '#build/css'
|
||||
// @ts-expect-error virtual file
|
||||
import _plugins from '#build/plugins'
|
||||
import plugins from '#build/plugins'
|
||||
// @ts-expect-error virtual file
|
||||
import RootComponent from '#build/root-component.mjs'
|
||||
// @ts-expect-error virtual file
|
||||
@ -26,8 +26,6 @@ if (!globalThis.$fetch) {
|
||||
|
||||
let entry: Function
|
||||
|
||||
const plugins = normalizePlugins(_plugins)
|
||||
|
||||
if (process.server) {
|
||||
entry = async function createNuxtAppServer (ssrContext: CreateOptions['ssrContext']) {
|
||||
const vueApp = createApp(RootComponent)
|
||||
|
@ -160,7 +160,6 @@ export interface PluginMeta {
|
||||
|
||||
export interface ResolvedPluginMeta {
|
||||
name?: string
|
||||
order: number
|
||||
parallel?: boolean
|
||||
}
|
||||
|
||||
@ -170,7 +169,7 @@ export interface Plugin<Injections extends Record<string, unknown> = Record<stri
|
||||
meta?: ResolvedPluginMeta
|
||||
}
|
||||
|
||||
export interface ObjectPluginInput<Injections extends Record<string, unknown> = Record<string, unknown>> extends PluginMeta {
|
||||
export interface ObjectPlugin<Injections extends Record<string, unknown> = Record<string, unknown>> extends PluginMeta {
|
||||
hooks?: Partial<RuntimeNuxtHooks>
|
||||
setup?: Plugin<Injections>
|
||||
/**
|
||||
@ -181,6 +180,9 @@ export interface ObjectPluginInput<Injections extends Record<string, unknown> =
|
||||
parallel?: boolean
|
||||
}
|
||||
|
||||
/** @deprecated Use `ObjectPlugin` */
|
||||
export type ObjectPluginInput<Injections extends Record<string, unknown> = Record<string, unknown>> = ObjectPlugin<Injections>
|
||||
|
||||
export interface CreateOptions {
|
||||
vueApp: NuxtApp['vueApp']
|
||||
ssrContext?: NuxtApp['ssrContext']
|
||||
@ -301,8 +303,11 @@ export function createNuxtApp (options: CreateOptions) {
|
||||
return nuxtApp
|
||||
}
|
||||
|
||||
export async function applyPlugin (nuxtApp: NuxtApp, plugin: Plugin) {
|
||||
if (typeof plugin !== 'function') { return }
|
||||
export async function applyPlugin (nuxtApp: NuxtApp, plugin: Plugin & ObjectPlugin<any>) {
|
||||
if (plugin.hooks) {
|
||||
nuxtApp.hooks.addHooks(plugin.hooks)
|
||||
}
|
||||
if (typeof plugin === 'function') {
|
||||
const { provide } = await nuxtApp.runWithContext(() => plugin(nuxtApp)) || {}
|
||||
if (provide && typeof provide === 'object') {
|
||||
for (const key in provide) {
|
||||
@ -310,13 +315,14 @@ export async function applyPlugin (nuxtApp: NuxtApp, plugin: Plugin) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function applyPlugins (nuxtApp: NuxtApp, plugins: Plugin[]) {
|
||||
export async function applyPlugins (nuxtApp: NuxtApp, plugins: Array<Plugin & ObjectPlugin<any>>) {
|
||||
const parallels: Promise<any>[] = []
|
||||
const errors: Error[] = []
|
||||
for (const plugin of plugins) {
|
||||
const promise = applyPlugin(nuxtApp, plugin)
|
||||
if (plugin.meta?.parallel) {
|
||||
if (plugin.parallel) {
|
||||
parallels.push(promise.catch(e => errors.push(e)))
|
||||
} else {
|
||||
await promise
|
||||
@ -326,97 +332,15 @@ export async function applyPlugins (nuxtApp: NuxtApp, plugins: Plugin[]) {
|
||||
if (errors.length) { throw errors[0] }
|
||||
}
|
||||
|
||||
export function normalizePlugins (_plugins: Plugin[]) {
|
||||
const unwrappedPlugins: Plugin[] = []
|
||||
const legacyInjectPlugins: Plugin[] = []
|
||||
const invalidPlugins: Plugin[] = []
|
||||
|
||||
const plugins: Plugin[] = []
|
||||
|
||||
for (const plugin of _plugins) {
|
||||
if (typeof plugin !== 'function') {
|
||||
if (process.dev) { invalidPlugins.push(plugin) }
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO: Skip invalid plugins in next releases
|
||||
let _plugin = plugin
|
||||
if (plugin.length > 1) {
|
||||
// Allow usage without wrapper but warn
|
||||
if (process.dev) { legacyInjectPlugins.push(plugin) }
|
||||
// @ts-expect-error deliberate invalid second argument
|
||||
_plugin = (nuxtApp: NuxtApp) => plugin(nuxtApp, nuxtApp.provide)
|
||||
}
|
||||
|
||||
// Allow usage without wrapper but warn
|
||||
if (process.dev && !isNuxtPlugin(_plugin)) { unwrappedPlugins.push(_plugin) }
|
||||
|
||||
plugins.push(_plugin)
|
||||
}
|
||||
|
||||
plugins.sort((a, b) => (a.meta?.order || orderMap.default) - (b.meta?.order || orderMap.default))
|
||||
|
||||
if (process.dev && legacyInjectPlugins.length) {
|
||||
console.warn('[warn] [nuxt] You are using a plugin with legacy Nuxt 2 format (context, inject) which is likely to be broken. In the future they will be ignored:', legacyInjectPlugins.map(p => p.name || p).join(','))
|
||||
}
|
||||
if (process.dev && invalidPlugins.length) {
|
||||
console.warn('[warn] [nuxt] Some plugins are not exposing a function and skipped:', invalidPlugins)
|
||||
}
|
||||
if (process.dev && unwrappedPlugins.length) {
|
||||
console.warn('[warn] [nuxt] You are using a plugin that has not been wrapped in `defineNuxtPlugin`. It is advised to wrap your plugins as in the future this may enable enhancements:', unwrappedPlugins.map(p => p.name || p).join(','))
|
||||
}
|
||||
|
||||
return plugins
|
||||
}
|
||||
|
||||
// -50: pre-all (nuxt)
|
||||
// -40: custom payload revivers (user)
|
||||
// -30: payload reviving (nuxt)
|
||||
// -20: pre (user) <-- pre mapped to this
|
||||
// -10: default (nuxt)
|
||||
// 0: default (user) <-- default behavior
|
||||
// +10: post (nuxt)
|
||||
// +20: post (user) <-- post mapped to this
|
||||
// +30: post-all (nuxt)
|
||||
|
||||
const orderMap: Record<NonNullable<ObjectPluginInput['enforce']>, number> = {
|
||||
pre: -20,
|
||||
default: 0,
|
||||
post: 20
|
||||
/*! @__NO_SIDE_EFFECTS__ */
|
||||
export function defineNuxtPlugin<T extends Record<string, unknown>> (plugin: Plugin<T> | ObjectPlugin<T>): Plugin<T> & ObjectPlugin<T> {
|
||||
if (typeof plugin === 'function') { return plugin }
|
||||
delete plugin.name
|
||||
return Object.assign(plugin.setup || (() => {}), plugin, { [NuxtPluginIndicator]: true } as const)
|
||||
}
|
||||
|
||||
/*! @__NO_SIDE_EFFECTS__ */
|
||||
export function definePayloadPlugin<T extends Record<string, unknown>> (plugin: Plugin<T> | ObjectPluginInput<T>) {
|
||||
return defineNuxtPlugin(plugin, { order: -40 })
|
||||
}
|
||||
|
||||
/*! @__NO_SIDE_EFFECTS__ */
|
||||
export function defineNuxtPlugin<T extends Record<string, unknown>> (plugin: Plugin<T> | ObjectPluginInput<T>, meta?: PluginMeta): Plugin<T> {
|
||||
if (typeof plugin === 'function') { return defineNuxtPlugin({ setup: plugin }, meta) }
|
||||
|
||||
const wrapper: Plugin<T> = (nuxtApp) => {
|
||||
if (plugin.hooks) {
|
||||
nuxtApp.hooks.addHooks(plugin.hooks)
|
||||
}
|
||||
if (plugin.setup) {
|
||||
return plugin.setup(nuxtApp)
|
||||
}
|
||||
}
|
||||
|
||||
wrapper.meta = {
|
||||
name: meta?.name || plugin.name || plugin.setup?.name,
|
||||
parallel: plugin.parallel,
|
||||
order:
|
||||
meta?.order ||
|
||||
plugin.order ||
|
||||
orderMap[plugin.enforce || 'default'] ||
|
||||
orderMap.default
|
||||
}
|
||||
|
||||
wrapper[NuxtPluginIndicator] = true
|
||||
|
||||
return wrapper
|
||||
}
|
||||
export const definePayloadPlugin = defineNuxtPlugin
|
||||
|
||||
export function isNuxtPlugin (plugin: unknown) {
|
||||
return typeof plugin === 'function' && NuxtPluginIndicator in plugin
|
||||
|
@ -6,6 +6,7 @@ import type { Nuxt, NuxtApp, NuxtPlugin, NuxtTemplate, ResolvedNuxtTemplate } fr
|
||||
|
||||
import * as defaultTemplates from './templates'
|
||||
import { getNameFromPath, hasSuffix, uniqueBy } from './utils'
|
||||
import { extractMetadata, orderMap } from './plugins/plugin-metadata'
|
||||
|
||||
export function createApp (nuxt: Nuxt, options: Partial<NuxtApp> = {}): NuxtApp {
|
||||
return defu(options, {
|
||||
@ -149,3 +150,21 @@ function resolvePaths<Item extends Record<string, any>> (items: Item[], key: { [
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
export async function annotatePlugins (nuxt: Nuxt, plugins: NuxtPlugin[]) {
|
||||
const _plugins: NuxtPlugin[] = []
|
||||
for (const plugin of plugins) {
|
||||
try {
|
||||
const code = plugin.src in nuxt.vfs ? nuxt.vfs[plugin.src] : await fsp.readFile(plugin.src!, 'utf-8')
|
||||
_plugins.push({
|
||||
...extractMetadata(code),
|
||||
...plugin
|
||||
})
|
||||
} catch (e) {
|
||||
console.warn(`[nuxt] Could not resolve \`${plugin.src}\`.`)
|
||||
_plugins.push(plugin)
|
||||
}
|
||||
}
|
||||
|
||||
return _plugins.sort((a, b) => (a.order ?? orderMap.default) - (b.order ?? orderMap.default))
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { join, normalize, relative, resolve } from 'pathe'
|
||||
import { createDebugger, createHooks } from 'hookable'
|
||||
import type { LoadNuxtOptions } from '@nuxt/kit'
|
||||
import { addComponent, addPlugin, addVitePlugin, addWebpackPlugin, installModule, loadNuxtConfig, logger, nuxtCtx, resolveAlias, resolveFiles, resolvePath, tryResolveModule } from '@nuxt/kit'
|
||||
import { addBuildPlugin, addComponent, addPlugin, addVitePlugin, addWebpackPlugin, installModule, loadNuxtConfig, logger, nuxtCtx, resolveAlias, resolveFiles, resolvePath, tryResolveModule } from '@nuxt/kit'
|
||||
import type { Nuxt, NuxtHooks, NuxtOptions } from 'nuxt/schema'
|
||||
|
||||
import escapeRE from 'escape-string-regexp'
|
||||
@ -24,6 +24,7 @@ import { LayerAliasingPlugin } from './plugins/layer-aliasing'
|
||||
import { addModuleTranspiles } from './modules'
|
||||
import { initNitro } from './nitro'
|
||||
import schemaModule from './schema'
|
||||
import { RemovePluginMetadataPlugin } from './plugins/plugin-metadata'
|
||||
|
||||
export function createNuxt (options: NuxtOptions): Nuxt {
|
||||
const hooks = createHooks<NuxtHooks>()
|
||||
@ -72,6 +73,9 @@ async function initNuxt (nuxt: Nuxt) {
|
||||
}
|
||||
})
|
||||
|
||||
// Add plugin normalisation plugin
|
||||
addBuildPlugin(RemovePluginMetadataPlugin(nuxt))
|
||||
|
||||
// Add import protection
|
||||
const config = {
|
||||
rootDir: nuxt.options.rootDir,
|
||||
|
189
packages/nuxt/src/core/plugins/plugin-metadata.ts
Normal file
189
packages/nuxt/src/core/plugins/plugin-metadata.ts
Normal file
@ -0,0 +1,189 @@
|
||||
import type { CallExpression, Property, SpreadElement } from 'estree'
|
||||
import type { Node } from 'estree-walker'
|
||||
import { walk } from 'estree-walker'
|
||||
import { parse } from 'typescript-estree'
|
||||
import { defu } from 'defu'
|
||||
import { findExports } from 'mlly'
|
||||
import type { Nuxt } from '@nuxt/schema'
|
||||
import { createUnplugin } from 'unplugin'
|
||||
import MagicString from 'magic-string'
|
||||
import { normalize } from 'pathe'
|
||||
|
||||
// eslint-disable-next-line import/no-restricted-paths
|
||||
import type { ObjectPlugin, PluginMeta } from '#app'
|
||||
|
||||
const internalOrderMap = {
|
||||
// -50: pre-all (nuxt)
|
||||
'nuxt-pre-all': -50,
|
||||
// -40: custom payload revivers (user)
|
||||
'user-revivers': -40,
|
||||
// -30: payload reviving (nuxt)
|
||||
'nuxt-revivers': -30,
|
||||
// -20: pre (user) <-- pre mapped to this
|
||||
'user-pre': -20,
|
||||
// -10: default (nuxt)
|
||||
'nuxt-default': -10,
|
||||
// 0: default (user) <-- default behavior
|
||||
'user-default': 0,
|
||||
// +10: post (nuxt)
|
||||
'nuxt-post': 10,
|
||||
// +20: post (user) <-- post mapped to this
|
||||
'user-post': 20,
|
||||
// +30: post-all (nuxt)
|
||||
'nuxt-post-all': 30
|
||||
}
|
||||
|
||||
export const orderMap: Record<NonNullable<ObjectPlugin['enforce']>, number> = {
|
||||
pre: internalOrderMap['user-pre'],
|
||||
default: internalOrderMap['user-default'],
|
||||
post: internalOrderMap['user-post']
|
||||
}
|
||||
|
||||
const metaCache: Record<string, Omit<PluginMeta, 'enforce'>> = {}
|
||||
export function extractMetadata (code: string) {
|
||||
let meta: PluginMeta = {}
|
||||
if (metaCache[code]) {
|
||||
return metaCache[code]
|
||||
}
|
||||
walk(parse(code) as Node, {
|
||||
enter (_node) {
|
||||
if (_node.type !== 'CallExpression' || (_node as CallExpression).callee.type !== 'Identifier') { return }
|
||||
const node = _node as CallExpression & { start: number, end: number }
|
||||
const name = 'name' in node.callee && node.callee.name
|
||||
if (name !== 'defineNuxtPlugin' && name !== 'definePayloadPlugin') { return }
|
||||
|
||||
if (name === 'definePayloadPlugin') {
|
||||
meta.order = internalOrderMap['user-revivers']
|
||||
}
|
||||
|
||||
const metaArg = node.arguments[1]
|
||||
if (metaArg) {
|
||||
if (metaArg.type !== 'ObjectExpression') {
|
||||
throw new Error('Invalid plugin metadata')
|
||||
}
|
||||
meta = extractMetaFromObject(metaArg.properties)
|
||||
}
|
||||
|
||||
const plugin = node.arguments[0]
|
||||
if (plugin.type === 'ObjectExpression') {
|
||||
meta = defu(extractMetaFromObject(plugin.properties), meta)
|
||||
}
|
||||
|
||||
meta.order = meta.order || orderMap[meta.enforce || 'default'] || orderMap.default
|
||||
delete meta.enforce
|
||||
}
|
||||
})
|
||||
metaCache[code] = meta
|
||||
return meta as Omit<PluginMeta, 'enforce'>
|
||||
}
|
||||
|
||||
type PluginMetaKey = keyof PluginMeta
|
||||
const keys: Record<PluginMetaKey, string> = {
|
||||
name: 'name',
|
||||
order: 'order',
|
||||
enforce: 'enforce'
|
||||
}
|
||||
function isMetadataKey (key: string): key is PluginMetaKey {
|
||||
return key in keys
|
||||
}
|
||||
|
||||
function extractMetaFromObject (properties: Array<Property | SpreadElement>) {
|
||||
const meta: PluginMeta = {}
|
||||
for (const property of properties) {
|
||||
if (property.type === 'SpreadElement' || !('name' in property.key)) {
|
||||
throw new Error('Invalid plugin metadata')
|
||||
}
|
||||
const propertyKey = property.key.name
|
||||
if (!isMetadataKey(propertyKey)) { continue }
|
||||
if (property.value.type === 'Literal') {
|
||||
meta[propertyKey] = property.value.value as any
|
||||
}
|
||||
if (property.value.type === 'UnaryExpression' && property.value.argument.type === 'Literal') {
|
||||
meta[propertyKey] = JSON.parse(property.value.operator + property.value.argument.raw!)
|
||||
}
|
||||
}
|
||||
return meta
|
||||
}
|
||||
|
||||
export const RemovePluginMetadataPlugin = (nuxt: Nuxt) => createUnplugin(() => {
|
||||
return {
|
||||
name: 'nuxt:remove-plugin-metadata',
|
||||
enforce: 'pre',
|
||||
transform (code, id) {
|
||||
id = normalize(id)
|
||||
const plugin = nuxt.apps.default.plugins.find(p => p.src === id)
|
||||
if (!plugin) { return }
|
||||
|
||||
const s = new MagicString(code)
|
||||
const exports = findExports(code)
|
||||
const defaultExport = exports.find(e => e.type === 'default' || e.name === 'default')
|
||||
if (!defaultExport) {
|
||||
console.error(`[warn] [nuxt] Plugin \`${plugin.src}\` has no default export and will be ignored at build time. Add \`export default defineNuxtPlugin(() => {})\` to your plugin.`)
|
||||
s.overwrite(0, code.length, 'export default () => {}')
|
||||
return {
|
||||
code: s.toString(),
|
||||
map: nuxt.options.sourcemap.client || nuxt.options.sourcemap.server ? s.generateMap({ hires: true }) : null
|
||||
}
|
||||
}
|
||||
|
||||
let wrapped = false
|
||||
|
||||
try {
|
||||
walk(parse(code) as Node, {
|
||||
enter (_node) {
|
||||
if (_node.type === 'ExportDefaultDeclaration' && (_node.declaration.type === 'FunctionDeclaration' || _node.declaration.type === 'ArrowFunctionExpression')) {
|
||||
if ('params' in _node.declaration && _node.declaration.params.length > 1) {
|
||||
console.warn(`[warn] [nuxt] Plugin \`${plugin.src}\` is in legacy Nuxt 2 format (context, inject) which is likely to be broken and will be ignored.`)
|
||||
s.overwrite(0, code.length, 'export default () => {}')
|
||||
wrapped = true // silence a duplicate error
|
||||
return
|
||||
}
|
||||
}
|
||||
if (_node.type !== 'CallExpression' || (_node as CallExpression).callee.type !== 'Identifier') { return }
|
||||
const node = _node as CallExpression & { start: number, end: number }
|
||||
const name = 'name' in node.callee && node.callee.name
|
||||
if (name !== 'defineNuxtPlugin' && name !== 'definePayloadPlugin') { return }
|
||||
wrapped = true
|
||||
|
||||
if (node.arguments[0].type !== 'ObjectExpression') {
|
||||
// TODO: Warn if legacy plugin format is detected
|
||||
if ('params' in node.arguments[0] && node.arguments[0].params.length > 1) {
|
||||
console.warn(`[warn] [nuxt] Plugin \`${plugin.src}\` is in legacy Nuxt 2 format (context, inject) which is likely to be broken and will be ignored.`)
|
||||
s.overwrite(0, code.length, 'export default () => {}')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Remove metadata that already has been extracted
|
||||
if (!('order' in plugin) && !('name' in plugin)) { return }
|
||||
for (const [argIndex, arg] of node.arguments.entries()) {
|
||||
if (arg.type !== 'ObjectExpression') { continue }
|
||||
for (const [propertyIndex, property] of arg.properties.entries()) {
|
||||
if (property.type === 'SpreadElement' || !('name' in property.key)) { continue }
|
||||
const propertyKey = property.key.name
|
||||
if (propertyKey === 'order' || propertyKey === 'enforce' || propertyKey === 'name') {
|
||||
const nextIndex = arg.properties[propertyIndex + 1]?.range?.[0] || node.arguments[argIndex + 1]?.range?.[0] || (arg.range![1] - 1)
|
||||
s.remove(property.range![0], nextIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return
|
||||
}
|
||||
|
||||
if (!wrapped) {
|
||||
console.warn(`[warn] [nuxt] Plugin \`${plugin.src}\` is not wrapped in \`defineNuxtPlugin\`. It is advised to wrap your plugins as in the future this may enable enhancements.`)
|
||||
}
|
||||
|
||||
if (s.hasChanged()) {
|
||||
return {
|
||||
code: s.toString(),
|
||||
map: nuxt.options.sourcemap.client || nuxt.options.sourcemap.server ? s.generateMap({ hires: true }) : null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
@ -8,6 +8,7 @@ import { camelCase } from 'scule'
|
||||
import { resolvePath } from 'mlly'
|
||||
import { filename } from 'pathe/utils'
|
||||
import type { Nuxt, NuxtApp, NuxtTemplate } from 'nuxt/schema'
|
||||
import { annotatePlugins } from './app'
|
||||
|
||||
export interface TemplateContext {
|
||||
nuxt: Nuxt
|
||||
@ -54,8 +55,9 @@ export const cssTemplate: NuxtTemplate<TemplateContext> = {
|
||||
|
||||
export const clientPluginTemplate: NuxtTemplate<TemplateContext> = {
|
||||
filename: 'plugins/client.mjs',
|
||||
getContents (ctx) {
|
||||
const clientPlugins = ctx.app.plugins.filter(p => !p.mode || p.mode !== 'server')
|
||||
async getContents (ctx) {
|
||||
const clientPlugins = await annotatePlugins(ctx.nuxt, ctx.app.plugins.filter(p => !p.mode || p.mode !== 'server'))
|
||||
await annotatePlugins(ctx.nuxt, clientPlugins)
|
||||
const exports: string[] = []
|
||||
const imports: string[] = []
|
||||
for (const plugin of clientPlugins) {
|
||||
@ -73,8 +75,8 @@ export const clientPluginTemplate: NuxtTemplate<TemplateContext> = {
|
||||
|
||||
export const serverPluginTemplate: NuxtTemplate<TemplateContext> = {
|
||||
filename: 'plugins/server.mjs',
|
||||
getContents (ctx) {
|
||||
const serverPlugins = ctx.app.plugins.filter(p => !p.mode || p.mode !== 'client')
|
||||
async getContents (ctx) {
|
||||
const serverPlugins = await annotatePlugins(ctx.nuxt, ctx.app.plugins.filter(p => !p.mode || p.mode !== 'client'))
|
||||
const exports: string[] = []
|
||||
const imports: string[] = []
|
||||
for (const plugin of serverPlugins) {
|
||||
|
64
packages/nuxt/test/plugin-metadata.test.ts
Normal file
64
packages/nuxt/test/plugin-metadata.test.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { RemovePluginMetadataPlugin, extractMetadata } from '../src/core/plugins/plugin-metadata'
|
||||
|
||||
describe('plugin-metadata', () => {
|
||||
it('should extract metadata from object-syntax plugins', () => {
|
||||
const properties = Object.entries({
|
||||
name: 'test',
|
||||
enforce: 'post',
|
||||
hooks: { 'app:mounted': () => {} },
|
||||
setup: () => {},
|
||||
order: 1
|
||||
})
|
||||
|
||||
for (const item of properties) {
|
||||
const obj = [...properties.filter(([key]) => key !== item[0]), item]
|
||||
|
||||
const meta = extractMetadata([
|
||||
'export default defineNuxtPlugin({',
|
||||
...obj.map(([key, value]) => `${key}: ${typeof value === 'function' ? value.toString() : JSON.stringify(value)},`),
|
||||
'})'
|
||||
].join('\n'))
|
||||
|
||||
expect(meta).toMatchInlineSnapshot(`
|
||||
{
|
||||
"name": "test",
|
||||
"order": 1,
|
||||
}
|
||||
`)
|
||||
}
|
||||
})
|
||||
|
||||
const transformPlugin: any = RemovePluginMetadataPlugin({
|
||||
options: { sourcemap: { client: true } },
|
||||
apps: { default: { plugins: [{ src: 'my-plugin.mjs', order: 10 }] } }
|
||||
} as any).raw({}, {} as any)
|
||||
|
||||
it('should overwrite invalid plugins', () => {
|
||||
const invalidPlugins = [
|
||||
'export const plugin = {}',
|
||||
'export default function (ctx, inject) {}'
|
||||
]
|
||||
for (const plugin of invalidPlugins) {
|
||||
expect(transformPlugin.transform(plugin, 'my-plugin.mjs').code).toBe('export default () => {}')
|
||||
}
|
||||
})
|
||||
|
||||
it('should remove order/name properties from object-syntax plugins', () => {
|
||||
const plugin = `
|
||||
export default defineNuxtPlugin({
|
||||
name: 'test',
|
||||
enforce: 'post',
|
||||
setup: () => {},
|
||||
}, { order: 10, name: test })
|
||||
`
|
||||
expect(transformPlugin.transform(plugin, 'my-plugin.mjs').code).toMatchInlineSnapshot(`
|
||||
"
|
||||
export default defineNuxtPlugin({
|
||||
setup: () => {},
|
||||
}, { })
|
||||
"
|
||||
`)
|
||||
})
|
||||
})
|
@ -52,6 +52,13 @@ export interface NuxtPlugin {
|
||||
ssr?: boolean
|
||||
src: string
|
||||
mode?: 'all' | 'server' | 'client'
|
||||
/**
|
||||
* This allows more granular control over plugin order and should only be used by advanced users.
|
||||
* Lower numbers run first, and user plugins default to `0`.
|
||||
*
|
||||
* Default Nuxt priorities can be seen at [here](https://github.com/nuxt/nuxt/blob/9904849bc87c53dfbd3ea3528140a5684c63c8d8/packages/nuxt/src/core/plugins/plugin-metadata.ts#L15-L34).
|
||||
*/
|
||||
order?: number
|
||||
}
|
||||
|
||||
export interface NuxtApp {
|
||||
|
@ -433,6 +433,9 @@ importers:
|
||||
strip-literal:
|
||||
specifier: ^1.0.1
|
||||
version: 1.0.1
|
||||
typescript-estree:
|
||||
specifier: ^18.1.0
|
||||
version: 18.1.0(typescript@5.0.4)
|
||||
ufo:
|
||||
specifier: ^1.1.2
|
||||
version: 1.1.2
|
||||
@ -6156,6 +6159,10 @@ packages:
|
||||
resolution: {integrity: sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==}
|
||||
dev: false
|
||||
|
||||
/lodash.unescape@4.0.1:
|
||||
resolution: {integrity: sha512-DhhGRshNS1aX6s5YdBE3njCCouPgnG29ebyHvImlZzXZf2SHgt+J08DHgytTPnpywNbO1Y8mNUFyQuIDBq2JZg==}
|
||||
dev: false
|
||||
|
||||
/lodash.union@4.6.0:
|
||||
resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==}
|
||||
|
||||
@ -7808,6 +7815,11 @@ packages:
|
||||
/scule@1.0.0:
|
||||
resolution: {integrity: sha512-4AsO/FrViE/iDNEPaAQlb77tf0csuq27EsVpy6ett584EcRTp6pTDLoGWVxCD77y5iU5FauOvhsI4o1APwPoSQ==}
|
||||
|
||||
/semver@5.5.0:
|
||||
resolution: {integrity: sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/semver@5.7.1:
|
||||
resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==}
|
||||
hasBin: true
|
||||
@ -8389,6 +8401,18 @@ packages:
|
||||
is-typed-array: 1.1.10
|
||||
dev: true
|
||||
|
||||
/typescript-estree@18.1.0(typescript@5.0.4):
|
||||
resolution: {integrity: sha512-LvvwoTuPgMO5UyckFXZeNI+m1UH1bkgAXEaHDEtkmxugtlMKM3mzyvSQUdnQ/FhifwaLuoAsNPqTGLrJI/UUaQ==}
|
||||
engines: {node: '>=6.14.0'}
|
||||
deprecated: This package was moved to @typescript-eslint/typescript-estree, please install the latest version from there instead
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
dependencies:
|
||||
lodash.unescape: 4.0.1
|
||||
semver: 5.5.0
|
||||
typescript: 5.0.4
|
||||
dev: false
|
||||
|
||||
/typescript@5.0.4:
|
||||
resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==}
|
||||
engines: {node: '>=12.20'}
|
||||
|
@ -25,7 +25,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
|
||||
|
||||
it('default client bundle size', async () => {
|
||||
stats.client = await analyzeSizes('**/*.js', publicDir)
|
||||
expect(roundToKilobytes(stats.client.totalBytes)).toMatchInlineSnapshot('"97.3k"')
|
||||
expect(roundToKilobytes(stats.client.totalBytes)).toMatchInlineSnapshot('"96.7k"')
|
||||
expect(stats.client.files.map(f => f.replace(/\..*\.js/, '.js'))).toMatchInlineSnapshot(`
|
||||
[
|
||||
"_nuxt/entry.js",
|
||||
@ -35,7 +35,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
|
||||
|
||||
it('default server bundle size', async () => {
|
||||
stats.server = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir)
|
||||
expect(roundToKilobytes(stats.server.totalBytes)).toMatchInlineSnapshot('"62.0k"')
|
||||
expect(roundToKilobytes(stats.server.totalBytes)).toMatchInlineSnapshot('"61.0k"')
|
||||
|
||||
const modules = await analyzeSizes('node_modules/**/*', serverDir)
|
||||
expect(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot('"2295k"')
|
||||
|
1
test/fixtures/basic/plugins/invalid-plugin-1.ts
vendored
Normal file
1
test/fixtures/basic/plugins/invalid-plugin-1.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
import 'nonexistent-package'
|
3
test/fixtures/basic/plugins/invalid-plugin-2.ts
vendored
Normal file
3
test/fixtures/basic/plugins/invalid-plugin-2.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
export default function previousPlugin (one, inject) {
|
||||
inject()
|
||||
}
|
Loading…
Reference in New Issue
Block a user