refactor(kit,nuxt,webpack): reduce reassignments (#30589)

This commit is contained in:
Anthony Fu 2025-01-15 01:36:18 +08:00 committed by GitHub
parent 4187f68139
commit 07146ddf48
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 62 additions and 42 deletions

View File

@ -114,7 +114,7 @@ export function addWebpackPlugin (pluginOrGetter: WebpackPluginInstance | Webpac
const method: 'push' | 'unshift' = options?.prepend ? 'unshift' : 'push' const method: 'push' | 'unshift' = options?.prepend ? 'unshift' : 'push'
const plugin = typeof pluginOrGetter === 'function' ? pluginOrGetter() : pluginOrGetter const plugin = typeof pluginOrGetter === 'function' ? pluginOrGetter() : pluginOrGetter
config.plugins = config.plugins || [] config.plugins ||= []
config.plugins[method](...toArray(plugin)) config.plugins[method](...toArray(plugin))
}, options) }, options)
} }
@ -126,7 +126,7 @@ export function addRspackPlugin (pluginOrGetter: RspackPluginInstance | RspackPl
const method: 'push' | 'unshift' = options?.prepend ? 'unshift' : 'push' const method: 'push' | 'unshift' = options?.prepend ? 'unshift' : 'push'
const plugin = typeof pluginOrGetter === 'function' ? pluginOrGetter() : pluginOrGetter const plugin = typeof pluginOrGetter === 'function' ? pluginOrGetter() : pluginOrGetter
config.plugins = config.plugins || [] config.plugins ||= []
config.plugins[method](...toArray(plugin)) config.plugins[method](...toArray(plugin))
}, options) }, options)
} }
@ -139,7 +139,7 @@ export function addVitePlugin (pluginOrGetter: VitePlugin | VitePlugin[] | (() =
const method: 'push' | 'unshift' = options?.prepend ? 'unshift' : 'push' const method: 'push' | 'unshift' = options?.prepend ? 'unshift' : 'push'
const plugin = typeof pluginOrGetter === 'function' ? pluginOrGetter() : pluginOrGetter const plugin = typeof pluginOrGetter === 'function' ? pluginOrGetter() : pluginOrGetter
config.plugins = config.plugins || [] config.plugins ||= []
config.plugins[method](...toArray(plugin)) config.plugins[method](...toArray(plugin))
}, options) }, options)
} }

View File

@ -11,7 +11,7 @@ import { MODE_RE } from './utils'
export async function addComponentsDir (dir: ComponentsDir, opts: { prepend?: boolean } = {}) { export async function addComponentsDir (dir: ComponentsDir, opts: { prepend?: boolean } = {}) {
const nuxt = useNuxt() const nuxt = useNuxt()
await assertNuxtCompatibility({ nuxt: '>=2.13' }, nuxt) await assertNuxtCompatibility({ nuxt: '>=2.13' }, nuxt)
nuxt.options.components = nuxt.options.components || [] nuxt.options.components ||= []
dir.priority ||= 0 dir.priority ||= 0
nuxt.hook('components:dirs', (dirs) => { dirs[opts.prepend ? 'unshift' : 'push'](dir) }) nuxt.hook('components:dirs', (dirs) => { dirs[opts.prepend ? 'unshift' : 'push'](dir) })
} }
@ -26,7 +26,7 @@ export type AddComponentOptions = { name: string, filePath: string } & Partial<E
export async function addComponent (opts: AddComponentOptions) { export async function addComponent (opts: AddComponentOptions) {
const nuxt = useNuxt() const nuxt = useNuxt()
await assertNuxtCompatibility({ nuxt: '>=2.13' }, nuxt) await assertNuxtCompatibility({ nuxt: '>=2.13' }, nuxt)
nuxt.options.components = nuxt.options.components || [] nuxt.options.components ||= []
if (!opts.mode) { if (!opts.mode) {
const [, mode = 'all'] = opts.filePath.match(MODE_RE) || [] const [, mode = 'all'] = opts.filePath.match(MODE_RE) || []

View File

@ -58,7 +58,7 @@ export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<Nuxt
const processedLayers = new Set<string>() const processedLayers = new Set<string>()
for (const layer of layers) { for (const layer of layers) {
// Resolve `rootDir` & `srcDir` of layers // Resolve `rootDir` & `srcDir` of layers
layer.config = layer.config || {} layer.config ||= {}
layer.config.rootDir = layer.config.rootDir ?? layer.cwd! layer.config.rootDir = layer.config.rootDir ?? layer.cwd!
// Only process/resolve layers once // Only process/resolve layers once

View File

@ -16,7 +16,7 @@ export interface LoadNuxtOptions extends LoadNuxtConfigOptions {
export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> { export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
// Backward compatibility // Backward compatibility
opts.cwd = resolve(opts.cwd || (opts as any).rootDir /* backwards compat */ || '.') opts.cwd = resolve(opts.cwd || (opts as any).rootDir /* backwards compat */ || '.')
opts.overrides = opts.overrides || (opts as any).config as NuxtConfig /* backwards compat */ || {} opts.overrides ||= (opts as any).config as NuxtConfig /* backwards compat */ || {}
// Apply dev as config override // Apply dev as config override
opts.overrides.dev = !!opts.dev opts.overrides.dev = !!opts.dev

View File

@ -87,7 +87,7 @@ function _defineNuxtModule<
// Avoid duplicate installs // Avoid duplicate installs
const uniqueKey = module.meta.name || module.meta.configKey const uniqueKey = module.meta.name || module.meta.configKey
if (uniqueKey) { if (uniqueKey) {
nuxt.options._requiredModules = nuxt.options._requiredModules || {} nuxt.options._requiredModules ||= {}
if (nuxt.options._requiredModules[uniqueKey]) { if (nuxt.options._requiredModules[uniqueKey]) {
return false return false
} }

View File

@ -44,7 +44,7 @@ export async function installModule<
} }
} }
nuxt.options._installedModules = nuxt.options._installedModules || [] nuxt.options._installedModules ||= []
const entryPath = typeof moduleToInstall === 'string' ? resolveAlias(moduleToInstall) : undefined const entryPath = typeof moduleToInstall === 'string' ? resolveAlias(moduleToInstall) : undefined
if (typeof moduleToInstall === 'string' && entryPath !== moduleToInstall) { if (typeof moduleToInstall === 'string' && entryPath !== moduleToInstall) {

View File

@ -40,7 +40,7 @@ export function addDevServerHandler (handler: NitroDevEventHandler) {
*/ */
export function addServerPlugin (plugin: string) { export function addServerPlugin (plugin: string) {
const nuxt = useNuxt() const nuxt = useNuxt()
nuxt.options.nitro.plugins = nuxt.options.nitro.plugins || [] nuxt.options.nitro.plugins ||= []
nuxt.options.nitro.plugins.push(normalize(plugin)) nuxt.options.nitro.plugins.push(normalize(plugin))
} }
@ -89,8 +89,8 @@ export function useNitro (): Nitro {
export function addServerImports (imports: Import[]) { export function addServerImports (imports: Import[]) {
const nuxt = useNuxt() const nuxt = useNuxt()
nuxt.hook('nitro:config', (config) => { nuxt.hook('nitro:config', (config) => {
config.imports = config.imports || {} config.imports ||= {}
config.imports.imports = config.imports.imports || [] config.imports.imports ||= []
config.imports.imports.push(...imports) config.imports.imports.push(...imports)
}) })
} }
@ -102,8 +102,8 @@ export function addServerImportsDir (dirs: string | string[], opts: { prepend?:
const nuxt = useNuxt() const nuxt = useNuxt()
const _dirs = toArray(dirs) const _dirs = toArray(dirs)
nuxt.hook('nitro:config', (config) => { nuxt.hook('nitro:config', (config) => {
config.imports = config.imports || {} config.imports ||= {}
config.imports.dirs = config.imports.dirs || [] config.imports.dirs ||= []
config.imports.dirs[opts.prepend ? 'unshift' : 'push'](..._dirs) config.imports.dirs[opts.prepend ? 'unshift' : 'push'](..._dirs)
}) })
} }
@ -115,7 +115,7 @@ export function addServerImportsDir (dirs: string | string[], opts: { prepend?:
export function addServerScanDir (dirs: string | string[], opts: { prepend?: boolean } = {}) { export function addServerScanDir (dirs: string | string[], opts: { prepend?: boolean } = {}) {
const nuxt = useNuxt() const nuxt = useNuxt()
nuxt.hook('nitro:config', (config) => { nuxt.hook('nitro:config', (config) => {
config.scanDirs = config.scanDirs || [] config.scanDirs ||= []
for (const dir of toArray(dirs)) { for (const dir of toArray(dirs)) {
config.scanDirs[opts.prepend ? 'unshift' : 'push'](dir) config.scanDirs[opts.prepend ? 'unshift' : 'push'](dir)

View File

@ -20,9 +20,7 @@ export interface ExtendRouteRulesOptions {
export function extendRouteRules (route: string, rule: NitroRouteConfig, options: ExtendRouteRulesOptions = {}) { export function extendRouteRules (route: string, rule: NitroRouteConfig, options: ExtendRouteRulesOptions = {}) {
const nuxt = useNuxt() const nuxt = useNuxt()
for (const opts of [nuxt.options, nuxt.options.nitro]) { for (const opts of [nuxt.options, nuxt.options.nitro]) {
if (!opts.routeRules) { opts.routeRules ||= {}
opts.routeRules = {}
}
opts.routeRules[route] = options.override opts.routeRules[route] = options.override
? defu(rule, opts.routeRules[route]) ? defu(rule, opts.routeRules[route])
: defu(opts.routeRules[route], rule) : defu(opts.routeRules[route], rule)

View File

@ -5,10 +5,10 @@ import { normalize } from 'pathe'
import type { NuxtPlugin, NuxtPluginTemplate } from '@nuxt/schema' import type { NuxtPlugin, NuxtPluginTemplate } from '@nuxt/schema'
import { resolvePathSync } from 'mlly' import { resolvePathSync } from 'mlly'
import { isWindows } from 'std-env' import { isWindows } from 'std-env'
import { MODE_RE, filterInPlace } from './utils'
import { tryUseNuxt, useNuxt } from './context' import { tryUseNuxt, useNuxt } from './context'
import { addTemplate } from './template' import { addTemplate } from './template'
import { resolveAlias } from './resolve' import { resolveAlias } from './resolve'
import { MODE_RE } from './utils'
/** /**
* Normalize a nuxt plugin object * Normalize a nuxt plugin object
@ -82,7 +82,7 @@ export function addPlugin (_plugin: NuxtPlugin | string, opts: AddPluginOptions
const plugin = normalizePlugin(_plugin) const plugin = normalizePlugin(_plugin)
// Remove any existing plugin with the same src // Remove any existing plugin with the same src
nuxt.options.plugins = nuxt.options.plugins.filter(p => normalizePlugin(p).src !== plugin.src) filterInPlace(nuxt.options.plugins, p => normalizePlugin(p).src !== plugin.src)
// Prepend to array by default to be before user provided plugins since is usually used by modules // 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) nuxt.options.plugins[opts.append ? 'push' : 'unshift'](plugin)

View File

@ -8,6 +8,7 @@ import type { TSConfig } from 'pkg-types'
import { gte } from 'semver' import { gte } from 'semver'
import { readPackageJSON } from 'pkg-types' import { readPackageJSON } from 'pkg-types'
import { filterInPlace } from './utils'
import { tryResolveModule } from './internal/esm' import { tryResolveModule } from './internal/esm'
import { getDirectory } from './module/install' import { getDirectory } from './module/install'
import { tryUseNuxt, useNuxt } from './context' import { tryUseNuxt, useNuxt } from './context'
@ -23,7 +24,7 @@ export function addTemplate<T> (_template: NuxtTemplate<T> | string) {
const template = normalizeTemplate(_template) const template = normalizeTemplate(_template)
// Remove any existing template with the same destination path // Remove any existing template with the same destination path
nuxt.options.build.templates = nuxt.options.build.templates.filter(p => normalizeTemplate(p).dst !== template.dst) filterInPlace(nuxt.options.build.templates, p => normalizeTemplate(p).dst !== template.dst)
// Add to templates array // Add to templates array
nuxt.options.build.templates.push(template) nuxt.options.build.templates.push(template)
@ -229,9 +230,9 @@ export async function _generateTypes (nuxt: Nuxt) {
? resolve(nuxt.options.buildDir, tsConfig.compilerOptions!.baseUrl) ? resolve(nuxt.options.buildDir, tsConfig.compilerOptions!.baseUrl)
: nuxt.options.buildDir : nuxt.options.buildDir
tsConfig.compilerOptions = tsConfig.compilerOptions || {} tsConfig.compilerOptions ||= {}
tsConfig.compilerOptions.paths = tsConfig.compilerOptions.paths || {} tsConfig.compilerOptions.paths ||= {}
tsConfig.include = tsConfig.include || [] tsConfig.include ||= []
for (const alias in aliases) { for (const alias in aliases) {
if (excludedAlias.some(re => re.test(alias))) { if (excludedAlias.some(re => re.test(alias))) {

View File

@ -3,4 +3,19 @@ export function toArray<T> (value: T | T[]): T[] {
return Array.isArray(value) ? value : [value] return Array.isArray(value) ? value : [value]
} }
/**
* Filter out items from an array in place. This function mutates the array.
* `predicate` get through the array from the end to the start for performance.
*
* This function should be faster than `Array.prototype.filter` on large arrays.
*/
export function filterInPlace<T> (array: T[], predicate: (item: T, index: number, arr: T[]) => unknown) {
for (let i = array.length; i--; i >= 0) {
if (!predicate(array[i]!, i, array)) {
array.splice(i, 1)
}
}
return array
}
export const MODE_RE = /\.(server|client)(\.\w+)*$/ export const MODE_RE = /\.(server|client)(\.\w+)*$/

View File

@ -95,7 +95,7 @@ export function createClientOnly<T extends ComponentOptions> (component: T) {
if (isPromise(setupState)) { if (isPromise(setupState)) {
return Promise.resolve(setupState).then((setupState) => { return Promise.resolve(setupState).then((setupState) => {
if (typeof setupState !== 'function') { if (typeof setupState !== 'function') {
setupState = setupState || {} setupState ||= {}
setupState.mounted$ = mounted$ setupState.mounted$ = mounted$
return setupState return setupState
} }

View File

@ -37,7 +37,7 @@ export async function callOnce (...args: any): Promise<void> {
return return
} }
nuxtApp._once = nuxtApp._once || {} nuxtApp._once ||= {}
nuxtApp._once[_key] = nuxtApp._once[_key] || fn() || true nuxtApp._once[_key] = nuxtApp._once[_key] || fn() || true
await nuxtApp._once[_key] await nuxtApp._once[_key]
nuxtApp.payload.once.add(_key) nuxtApp.payload.once.add(_key)

View File

@ -250,7 +250,7 @@ export default defineNuxtModule<ComponentsOptions>({
// TODO: refactor this // TODO: refactor this
nuxt.hook('vite:extendConfig', (config, { isClient }) => { nuxt.hook('vite:extendConfig', (config, { isClient }) => {
config.plugins = config.plugins || [] config.plugins ||= []
if (isClient && selectiveClient) { if (isClient && selectiveClient) {
writeFileSync(join(nuxt.options.buildDir, 'components-chunk.mjs'), 'export const paths = {}') writeFileSync(join(nuxt.options.buildDir, 'components-chunk.mjs'), 'export const paths = {}')
@ -275,7 +275,7 @@ export default defineNuxtModule<ComponentsOptions>({
nuxt.hook(key, (configs) => { nuxt.hook(key, (configs) => {
configs.forEach((config) => { configs.forEach((config) => {
const mode = config.name === 'client' ? 'client' : 'server' const mode = config.name === 'client' ? 'client' : 'server'
config.plugins = config.plugins || [] config.plugins ||= []
if (mode !== 'server') { if (mode !== 'server') {
writeFileSync(join(nuxt.options.buildDir, 'components-chunk.mjs'), 'export const paths = {}') writeFileSync(join(nuxt.options.buildDir, 'components-chunk.mjs'), 'export const paths = {}')

View File

@ -239,7 +239,11 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
// Resolve user-provided paths // Resolve user-provided paths
nitroConfig.srcDir = resolve(nuxt.options.rootDir, nuxt.options.srcDir, nitroConfig.srcDir!) nitroConfig.srcDir = resolve(nuxt.options.rootDir, nuxt.options.srcDir, nitroConfig.srcDir!)
nitroConfig.ignore = [...(nitroConfig.ignore || []), ...resolveIgnorePatterns(nitroConfig.srcDir), `!${join(nuxt.options.buildDir, 'dist/client', nuxt.options.app.buildAssetsDir, '**/*')}`] nitroConfig.ignore ||= []
nitroConfig.ignore.push(
...resolveIgnorePatterns(nitroConfig.srcDir),
`!${join(nuxt.options.buildDir, 'dist/client', nuxt.options.app.buildAssetsDir, '**/*')}`,
)
// Resolve aliases in user-provided input - so `~/server/test` will work // Resolve aliases in user-provided input - so `~/server/test` will work
nitroConfig.plugins = nitroConfig.plugins?.map(plugin => plugin ? resolveAlias(plugin, nuxt.options.alias) : plugin) nitroConfig.plugins = nitroConfig.plugins?.map(plugin => plugin ? resolveAlias(plugin, nuxt.options.alias) : plugin)
@ -411,14 +415,16 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
const basePath = nitroConfig.typescript!.tsConfig!.compilerOptions?.baseUrl ? resolve(nuxt.options.buildDir, nitroConfig.typescript!.tsConfig!.compilerOptions?.baseUrl) : nuxt.options.buildDir const basePath = nitroConfig.typescript!.tsConfig!.compilerOptions?.baseUrl ? resolve(nuxt.options.buildDir, nitroConfig.typescript!.tsConfig!.compilerOptions?.baseUrl) : nuxt.options.buildDir
const aliases = nitroConfig.alias! const aliases = nitroConfig.alias!
const tsConfig = nitroConfig.typescript!.tsConfig! const tsConfig = nitroConfig.typescript!.tsConfig!
tsConfig.compilerOptions = tsConfig.compilerOptions || {} tsConfig.compilerOptions ||= {}
tsConfig.compilerOptions.paths = tsConfig.compilerOptions.paths || {} tsConfig.compilerOptions.paths ||= {}
for (const _alias in aliases) { for (const _alias in aliases) {
const alias = _alias as keyof typeof aliases const alias = _alias as keyof typeof aliases
if (excludedAlias.some(pattern => typeof pattern === 'string' ? alias === pattern : pattern.test(alias))) { if (excludedAlias.some(pattern => typeof pattern === 'string' ? alias === pattern : pattern.test(alias))) {
continue continue
} }
if (alias in tsConfig.compilerOptions.paths) { continue } if (alias in tsConfig.compilerOptions.paths) {
continue
}
const absolutePath = resolve(basePath, aliases[alias]!) const absolutePath = resolve(basePath, aliases[alias]!)
const stats = await fsp.stat(absolutePath).catch(() => null /* file does not exist */) const stats = await fsp.stat(absolutePath).catch(() => null /* file does not exist */)
@ -532,7 +538,7 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
await writeTypes(nitro) await writeTypes(nitro)
} }
// Exclude nitro output dir from typescript // Exclude nitro output dir from typescript
opts.tsConfig.exclude = opts.tsConfig.exclude || [] opts.tsConfig.exclude ||= []
opts.tsConfig.exclude.push(relative(nuxt.options.buildDir, resolve(nuxt.options.rootDir, nitro.options.output.dir))) opts.tsConfig.exclude.push(relative(nuxt.options.buildDir, resolve(nuxt.options.rootDir, nitro.options.output.dir)))
opts.references.push({ path: resolve(nuxt.options.buildDir, 'types/nitro.d.ts') }) opts.references.push({ path: resolve(nuxt.options.buildDir, 'types/nitro.d.ts') })
}) })

View File

@ -2,7 +2,7 @@ import { defineEventHandler, getRequestHeader } from 'h3'
export default defineEventHandler((event) => { export default defineEventHandler((event) => {
if (getRequestHeader(event, 'x-nuxt-no-ssr')) { if (getRequestHeader(event, 'x-nuxt-no-ssr')) {
event.context.nuxt = event.context.nuxt || {} event.context.nuxt ||= {}
event.context.nuxt.noSSR = true event.context.nuxt.noSSR = true
} }
}) })

View File

@ -488,8 +488,8 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
} }
// TODO: remove for v4 // TODO: remove for v4
islandHead.link = islandHead.link || [] islandHead.link ||= []
islandHead.style = islandHead.style || [] islandHead.style ||= []
const islandResponse: NuxtIslandResponse = { const islandResponse: NuxtIslandResponse = {
id: islandContext.id, id: islandContext.id,

View File

@ -530,8 +530,8 @@ export default defineNuxtModule({
getContents: () => 'export { START_LOCATION, useRoute } from \'vue-router\'', getContents: () => 'export { START_LOCATION, useRoute } from \'vue-router\'',
}) })
nuxt.options.vite.resolve = nuxt.options.vite.resolve || {} nuxt.options.vite.resolve ||= {}
nuxt.options.vite.resolve.dedupe = nuxt.options.vite.resolve.dedupe || [] nuxt.options.vite.resolve.dedupe ||= []
nuxt.options.vite.resolve.dedupe.push('vue-router') nuxt.options.vite.resolve.dedupe.push('vue-router')
// Add router options template // Add router options template

View File

@ -56,7 +56,7 @@ function clientNodeCompat (ctx: WebpackConfigContext) {
} }
ctx.config.plugins!.push(new webpack.DefinePlugin({ global: 'globalThis' })) ctx.config.plugins!.push(new webpack.DefinePlugin({ global: 'globalThis' }))
ctx.config.resolve = ctx.config.resolve || {} ctx.config.resolve ||= {}
ctx.config.resolve.fallback = { ctx.config.resolve.fallback = {
...env(nodeless).alias, ...env(nodeless).alias,
...ctx.config.resolve.fallback, ...ctx.config.resolve.fallback,
@ -92,7 +92,7 @@ function clientHMR (ctx: WebpackConfigContext) {
`webpack-hot-middleware/client?${hotMiddlewareClientOptionsStr}`, `webpack-hot-middleware/client?${hotMiddlewareClientOptionsStr}`,
) )
ctx.config.plugins = ctx.config.plugins || [] ctx.config.plugins ||= []
ctx.config.plugins.push(new webpack.HotModuleReplacementPlugin()) ctx.config.plugins.push(new webpack.HotModuleReplacementPlugin())
} }

View File

@ -85,7 +85,7 @@ function serverStandalone (ctx: WebpackConfigContext) {
} }
function serverPlugins (ctx: WebpackConfigContext) { function serverPlugins (ctx: WebpackConfigContext) {
ctx.config.plugins = ctx.config.plugins || [] ctx.config.plugins ||= []
// Server polyfills // Server polyfills
if (ctx.userConfig.serverURLPolyfill) { if (ctx.userConfig.serverURLPolyfill) {

View File

@ -50,7 +50,7 @@ function baseConfig (ctx: WebpackConfigContext) {
} }
function basePlugins (ctx: WebpackConfigContext) { function basePlugins (ctx: WebpackConfigContext) {
ctx.config.plugins = ctx.config.plugins || [] ctx.config.plugins ||= []
// Add timefix-plugin before other plugins // Add timefix-plugin before other plugins
if (ctx.options.dev) { if (ctx.options.dev) {