From 2abcc16cfb77f925a8ed44a9744f16382881d309 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Tue, 20 Jun 2023 00:00:03 +0100 Subject: [PATCH] perf(nuxt): extract and apply plugin order at build time (#21611) --- packages/nuxt/package.json | 1 + packages/nuxt/src/app/entry.ts | 6 +- packages/nuxt/src/app/nuxt.ts | 120 ++--------- packages/nuxt/src/core/app.ts | 19 ++ packages/nuxt/src/core/nuxt.ts | 6 +- .../nuxt/src/core/plugins/plugin-metadata.ts | 189 ++++++++++++++++++ packages/nuxt/src/core/templates.ts | 10 +- packages/nuxt/test/plugin-metadata.test.ts | 64 ++++++ packages/schema/src/types/nuxt.ts | 7 + pnpm-lock.yaml | 24 +++ test/bundle.test.ts | 4 +- .../basic/plugins/invalid-plugin-1.ts | 1 + .../basic/plugins/invalid-plugin-2.ts | 3 + 13 files changed, 345 insertions(+), 109 deletions(-) create mode 100644 packages/nuxt/src/core/plugins/plugin-metadata.ts create mode 100644 packages/nuxt/test/plugin-metadata.test.ts create mode 100644 test/fixtures/basic/plugins/invalid-plugin-1.ts create mode 100644 test/fixtures/basic/plugins/invalid-plugin-2.ts diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index 3f19271566..190b1e044a 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -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", diff --git a/packages/nuxt/src/app/entry.ts b/packages/nuxt/src/app/entry.ts index 4aca681905..be990030b9 100644 --- a/packages/nuxt/src/app/entry.ts +++ b/packages/nuxt/src/app/entry.ts @@ -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) diff --git a/packages/nuxt/src/app/nuxt.ts b/packages/nuxt/src/app/nuxt.ts index 9a2cbf3587..bb7d2a25d9 100644 --- a/packages/nuxt/src/app/nuxt.ts +++ b/packages/nuxt/src/app/nuxt.ts @@ -160,7 +160,6 @@ export interface PluginMeta { export interface ResolvedPluginMeta { name?: string - order: number parallel?: boolean } @@ -170,7 +169,7 @@ export interface Plugin = Record = Record> extends PluginMeta { +export interface ObjectPlugin = Record> extends PluginMeta { hooks?: Partial setup?: Plugin /** @@ -181,6 +180,9 @@ export interface ObjectPluginInput = parallel?: boolean } +/** @deprecated Use `ObjectPlugin` */ +export type ObjectPluginInput = Record> = ObjectPlugin + export interface CreateOptions { vueApp: NuxtApp['vueApp'] ssrContext?: NuxtApp['ssrContext'] @@ -301,22 +303,26 @@ export function createNuxtApp (options: CreateOptions) { return nuxtApp } -export async function applyPlugin (nuxtApp: NuxtApp, plugin: Plugin) { - if (typeof plugin !== 'function') { return } - const { provide } = await nuxtApp.runWithContext(() => plugin(nuxtApp)) || {} - if (provide && typeof provide === 'object') { - for (const key in provide) { - nuxtApp.provide(key, provide[key]) +export async function applyPlugin (nuxtApp: NuxtApp, plugin: Plugin & ObjectPlugin) { + 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) { + nuxtApp.provide(key, provide[key]) + } } } } -export async function applyPlugins (nuxtApp: NuxtApp, plugins: Plugin[]) { +export async function applyPlugins (nuxtApp: NuxtApp, plugins: Array>) { const parallels: Promise[] = [] 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, number> = { - pre: -20, - default: 0, - post: 20 +/*! @__NO_SIDE_EFFECTS__ */ +export function defineNuxtPlugin> (plugin: Plugin | ObjectPlugin): Plugin & ObjectPlugin { + 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> (plugin: Plugin | ObjectPluginInput) { - return defineNuxtPlugin(plugin, { order: -40 }) -} - -/*! @__NO_SIDE_EFFECTS__ */ -export function defineNuxtPlugin> (plugin: Plugin | ObjectPluginInput, meta?: PluginMeta): Plugin { - if (typeof plugin === 'function') { return defineNuxtPlugin({ setup: plugin }, meta) } - - const wrapper: Plugin = (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 diff --git a/packages/nuxt/src/core/app.ts b/packages/nuxt/src/core/app.ts index 9300e2275d..1c65044ff3 100644 --- a/packages/nuxt/src/core/app.ts +++ b/packages/nuxt/src/core/app.ts @@ -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 { return defu(options, { @@ -149,3 +150,21 @@ function resolvePaths> (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)) +} diff --git a/packages/nuxt/src/core/nuxt.ts b/packages/nuxt/src/core/nuxt.ts index defc1037e3..12bc21c8a6 100644 --- a/packages/nuxt/src/core/nuxt.ts +++ b/packages/nuxt/src/core/nuxt.ts @@ -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() @@ -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, diff --git a/packages/nuxt/src/core/plugins/plugin-metadata.ts b/packages/nuxt/src/core/plugins/plugin-metadata.ts new file mode 100644 index 0000000000..56db40e07f --- /dev/null +++ b/packages/nuxt/src/core/plugins/plugin-metadata.ts @@ -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, number> = { + pre: internalOrderMap['user-pre'], + default: internalOrderMap['user-default'], + post: internalOrderMap['user-post'] +} + +const metaCache: Record> = {} +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 +} + +type PluginMetaKey = keyof PluginMeta +const keys: Record = { + name: 'name', + order: 'order', + enforce: 'enforce' +} +function isMetadataKey (key: string): key is PluginMetaKey { + return key in keys +} + +function extractMetaFromObject (properties: Array) { + 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 + } + } + } + } +}) diff --git a/packages/nuxt/src/core/templates.ts b/packages/nuxt/src/core/templates.ts index 66560a68b2..74da3eb6e5 100644 --- a/packages/nuxt/src/core/templates.ts +++ b/packages/nuxt/src/core/templates.ts @@ -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 = { export const clientPluginTemplate: NuxtTemplate = { 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 = { export const serverPluginTemplate: NuxtTemplate = { 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) { diff --git a/packages/nuxt/test/plugin-metadata.test.ts b/packages/nuxt/test/plugin-metadata.test.ts new file mode 100644 index 0000000000..271b86c871 --- /dev/null +++ b/packages/nuxt/test/plugin-metadata.test.ts @@ -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: () => {}, + }, { }) + " + `) + }) +}) diff --git a/packages/schema/src/types/nuxt.ts b/packages/schema/src/types/nuxt.ts index 49efdadd60..1deca39c6c 100644 --- a/packages/schema/src/types/nuxt.ts +++ b/packages/schema/src/types/nuxt.ts @@ -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 { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c609c40ccd..00e74ba4b2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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'} diff --git a/test/bundle.test.ts b/test/bundle.test.ts index d3dfa9c176..3c9e261b04 100644 --- a/test/bundle.test.ts +++ b/test/bundle.test.ts @@ -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"') diff --git a/test/fixtures/basic/plugins/invalid-plugin-1.ts b/test/fixtures/basic/plugins/invalid-plugin-1.ts new file mode 100644 index 0000000000..4a2f076d0d --- /dev/null +++ b/test/fixtures/basic/plugins/invalid-plugin-1.ts @@ -0,0 +1 @@ +import 'nonexistent-package' diff --git a/test/fixtures/basic/plugins/invalid-plugin-2.ts b/test/fixtures/basic/plugins/invalid-plugin-2.ts new file mode 100644 index 0000000000..b5e18bd7ea --- /dev/null +++ b/test/fixtures/basic/plugins/invalid-plugin-2.ts @@ -0,0 +1,3 @@ +export default function previousPlugin (one, inject) { + inject() +}