From ffdc358561d1f90c6a6ad97c74fe54e4b3c66bd9 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Thu, 13 Jun 2024 23:35:00 +0100 Subject: [PATCH] perf(vite): various performance improvements (#27601) --- packages/vite/package.json | 1 + packages/vite/src/client.ts | 13 +++++++++--- packages/vite/src/css.ts | 17 +++++++++------ packages/vite/src/dev-bundler.ts | 8 ++++++- packages/vite/src/manifest.ts | 13 +++++++----- packages/vite/src/plugins/analyze.ts | 19 ++++++++++------- packages/vite/src/plugins/composable-keys.ts | 7 +++++-- packages/vite/src/plugins/public-dirs.ts | 2 +- packages/vite/src/plugins/ssr-styles.ts | 6 +++--- packages/vite/src/server.ts | 2 +- packages/vite/src/utils/transpile.ts | 2 +- packages/vite/src/vite.ts | 22 +++++++++++++------- pnpm-lock.yaml | 3 +++ 13 files changed, 77 insertions(+), 38 deletions(-) diff --git a/packages/vite/package.json b/packages/vite/package.json index d2b64f1169..24c6384263 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -28,6 +28,7 @@ "@types/clear": "0.1.4", "@types/estree": "1.0.5", "@types/fs-extra": "11.0.4", + "rollup": "^4.18.0", "unbuild": "latest", "vue": "3.4.27" }, diff --git a/packages/vite/src/client.ts b/packages/vite/src/client.ts index 12d746c021..607666f933 100644 --- a/packages/vite/src/client.ts +++ b/packages/vite/src/client.ts @@ -163,10 +163,11 @@ export async function buildClient (ctx: ViteBuildContext) { } // We want to respect users' own rollup output options + const fileNames = withoutLeadingSlash(join(ctx.nuxt.options.app.buildAssetsDir, '[hash].js')) clientConfig.build!.rollupOptions = defu(clientConfig.build!.rollupOptions!, { output: { - chunkFileNames: ctx.nuxt.options.dev ? undefined : withoutLeadingSlash(join(ctx.nuxt.options.app.buildAssetsDir, '[hash].js')), - entryFileNames: ctx.nuxt.options.dev ? 'entry.js' : withoutLeadingSlash(join(ctx.nuxt.options.app.buildAssetsDir, '[hash].js')), + chunkFileNames: ctx.nuxt.options.dev ? undefined : fileNames, + entryFileNames: ctx.nuxt.options.dev ? 'entry.js' : fileNames, } satisfies NonNullable['output'], }) as any @@ -228,7 +229,13 @@ export async function buildClient (ctx: ViteBuildContext) { }) const viteMiddleware = defineEventHandler(async (event) => { - const viteRoutes = viteServer.middlewares.stack.map(m => m.route).filter(r => r.length > 1) + const viteRoutes: string[] = [] + for (const viteRoute of viteServer.middlewares.stack) { + const m = viteRoute.route + if (m.length > 1) { + viteRoutes.push(m) + } + } if (!event.path.startsWith(clientConfig.base!) && !viteRoutes.some(route => event.path.startsWith(route))) { // @ts-expect-error _skip_transform is a private property event.node.req._skip_transform = true diff --git a/packages/vite/src/css.ts b/packages/vite/src/css.ts index 96413a3ce4..e99b323fa4 100644 --- a/packages/vite/src/css.ts +++ b/packages/vite/src/css.ts @@ -3,6 +3,8 @@ import type { Nuxt } from '@nuxt/schema' import type { InlineConfig as ViteConfig } from 'vite' import { distDir } from './dirs' +const lastPlugins = ['autoprefixer', 'cssnano'] + export function resolveCSSOptions (nuxt: Nuxt): ViteConfig['css'] { const css: ViteConfig['css'] & { postcss: NonNullable['postcss'], string>> } = { postcss: { @@ -10,19 +12,22 @@ export function resolveCSSOptions (nuxt: Nuxt): ViteConfig['css'] { }, } - const lastPlugins = ['autoprefixer', 'cssnano'] - css.postcss.plugins = Object.entries(nuxt.options.postcss.plugins) + css.postcss.plugins = [] + + const plugins = Object.entries(nuxt.options.postcss.plugins) .sort((a, b) => lastPlugins.indexOf(a[0]) - lastPlugins.indexOf(b[0])) - .filter(([, opts]) => opts) - .map(([name, opts]) => { + + for (const [name, opts] of plugins) { + if (opts) { const plugin = requireModule(name, { paths: [ ...nuxt.options.modulesDir, distDir, ], }) - return plugin(opts) - }) + css.postcss.plugins.push(plugin(opts)) + } + } return css } diff --git a/packages/vite/src/dev-bundler.ts b/packages/vite/src/dev-bundler.ts index cf774c241d..e2d970b524 100644 --- a/packages/vite/src/dev-bundler.ts +++ b/packages/vite/src/dev-bundler.ts @@ -238,7 +238,13 @@ export async function initViteDevBundler (ctx: ViteBuildContext, onBuild: () => const { code, ids } = await bundleRequest(options, ctx.entry) await fse.writeFile(resolve(ctx.nuxt.options.buildDir, 'dist/server/server.mjs'), code, 'utf-8') // Have CSS in the manifest to prevent FOUC on dev SSR - await writeManifest(ctx, ids.filter(isCSS).map(i => i.slice(1))) + const manifestIds: string[] = [] + for (const i of ids) { + if (isCSS(i)) { + manifestIds.push(i.slice(1)) + } + } + await writeManifest(ctx, manifestIds) const time = (Date.now() - start) logger.success(`Vite server built in ${time}ms`) await onBuild() diff --git a/packages/vite/src/manifest.ts b/packages/vite/src/manifest.ts index f5d3e7aa6a..54471633ec 100644 --- a/packages/vite/src/manifest.ts +++ b/packages/vite/src/manifest.ts @@ -50,11 +50,14 @@ export async function writeManifest (ctx: ViteBuildContext, css: string[] = []) await fse.mkdirp(serverDist) if (ctx.config.build?.cssCodeSplit === false) { - const entryCSS = Object.values(clientManifest as Record).find(val => (val).file?.endsWith('.css'))?.file - if (entryCSS) { - const key = relative(ctx.config.root!, ctx.entry) - clientManifest[key].css ||= [] - clientManifest[key].css!.push(entryCSS) + for (const key in clientManifest as Record) { + const val = clientManifest[key] + if (val.file?.endsWith('.css')) { + const key = relative(ctx.config.root!, ctx.entry) + clientManifest[key].css ||= [] + clientManifest[key].css!.push(val.file) + break + } } } diff --git a/packages/vite/src/plugins/analyze.ts b/packages/vite/src/plugins/analyze.ts index 3fa23b94b8..ddb923f244 100644 --- a/packages/vite/src/plugins/analyze.ts +++ b/packages/vite/src/plugins/analyze.ts @@ -3,6 +3,7 @@ import { transform } from 'esbuild' import { visualizer } from 'rollup-plugin-visualizer' import defu from 'defu' import type { NuxtOptions } from 'nuxt/schema' +import type { RenderedModule } from 'rollup' import type { ViteBuildContext } from '../vite' export function analyzePlugin (ctx: ViteBuildContext): Plugin[] { @@ -13,14 +14,18 @@ export function analyzePlugin (ctx: ViteBuildContext): Plugin[] { { name: 'nuxt:analyze-minify', async generateBundle (_opts, outputBundle) { - for (const [_bundleId, bundle] of Object.entries(outputBundle)) { + for (const _bundleId in outputBundle) { + const bundle = outputBundle[_bundleId] if (bundle.type !== 'chunk') { continue } - const originalEntries = Object.entries(bundle.modules) - const minifiedEntries = await Promise.all(originalEntries.map(async ([moduleId, module]) => { - const { code } = await transform(module.code || '', { minify: true }) - return [moduleId, { ...module, code }] - })) - bundle.modules = Object.fromEntries(minifiedEntries) + const minifiedModuleEntryPromises: Array> = [] + for (const moduleId in bundle.modules) { + const module = bundle.modules[moduleId] + minifiedModuleEntryPromises.push( + transform(module.code || '', { minify: true }) + .then(result => [moduleId, { ...module, code: result.code }]), + ) + } + bundle.modules = Object.fromEntries(await Promise.all(minifiedModuleEntryPromises)) } }, }, diff --git a/packages/vite/src/plugins/composable-keys.ts b/packages/vite/src/plugins/composable-keys.ts index 4832d60e61..af967c9eb7 100644 --- a/packages/vite/src/plugins/composable-keys.ts +++ b/packages/vite/src/plugins/composable-keys.ts @@ -23,12 +23,15 @@ const SUPPORTED_EXT_RE = /\.(?:m?[jt]sx?|vue)/ export const composableKeysPlugin = createUnplugin((options: ComposableKeysOptions) => { const composableMeta: Record = {} + const composableLengths = new Set() + const keyedFunctions = new Set() for (const { name, ...meta } of options.composables) { composableMeta[name] = meta + keyedFunctions.add(name) + composableLengths.add(meta.argumentLength) } - const maxLength = Math.max(...options.composables.map(({ argumentLength }) => argumentLength)) - const keyedFunctions = new Set(options.composables.map(({ name }) => name)) + const maxLength = Math.max(...composableLengths) const KEYED_FUNCTIONS_RE = new RegExp(`\\b(${[...keyedFunctions].map(f => escapeRE(f)).join('|')})\\b`) return { diff --git a/packages/vite/src/plugins/public-dirs.ts b/packages/vite/src/plugins/public-dirs.ts index 442b87e6e5..379d451819 100644 --- a/packages/vite/src/plugins/public-dirs.ts +++ b/packages/vite/src/plugins/public-dirs.ts @@ -25,7 +25,7 @@ export const VitePublicDirsPlugin = createUnplugin((options: { sourcemap?: boole resolveId: { enforce: 'post', handler (id) { - if (id === '/__skip_vite' || !id.startsWith('/') || id.startsWith('/@fs')) { return } + if (id === '/__skip_vite' || id[0] !== '/' || id.startsWith('/@fs')) { return } if (resolveFromPublicAssets(id)) { return PREFIX + encodeURIComponent(id) diff --git a/packages/vite/src/plugins/ssr-styles.ts b/packages/vite/src/plugins/ssr-styles.ts index 37fc7da1f7..b1e785c6e9 100644 --- a/packages/vite/src/plugins/ssr-styles.ts +++ b/packages/vite/src/plugins/ssr-styles.ts @@ -66,12 +66,12 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin { const { files, inBundle } = cssMap[file] // File has been tree-shaken out of build (or there are no styles to inline) if (!files.length || !inBundle) { continue } - + const fileName = filename(file) const base = typeof outputOptions.assetFileNames === 'string' ? outputOptions.assetFileNames : outputOptions.assetFileNames({ type: 'asset', - name: `${filename(file)}-styles.mjs`, + name: `${fileName}-styles.mjs`, source: '', }) @@ -79,7 +79,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin { emitted[file] = this.emitFile({ type: 'asset', - name: `${filename(file)}-styles.mjs`, + name: `${fileName}-styles.mjs`, source: [ ...files.map((css, i) => `import style_${i} from './${relative(baseDir, this.getFileName(css))}';`), `export default [${files.map((_, i) => `style_${i}`).join(', ')}]`, diff --git a/packages/vite/src/server.ts b/packages/vite/src/server.ts index 33965931a8..67919c8b2f 100644 --- a/packages/vite/src/server.ts +++ b/packages/vite/src/server.ts @@ -105,7 +105,7 @@ export async function buildServer (ctx: ViteBuildContext) { if (!ctx.nuxt.options.dev) { const nitroDependencies = await tryResolveModule('nitropack/package.json', ctx.nuxt.options.modulesDir) - .then(r => import(r!)).then(r => Object.keys(r.dependencies || {})).catch(() => []) + .then(r => import(r!)).then(r => r.dependencies ? Object.keys(r.dependencies) : []).catch(() => []) if (Array.isArray(serverConfig.ssr!.external)) { serverConfig.ssr!.external.push( // explicit dependencies we use in our ssr renderer - these can be inlined (if necessary) in the nitro build diff --git a/packages/vite/src/utils/transpile.ts b/packages/vite/src/utils/transpile.ts index 2484cc7034..8c60c1776a 100644 --- a/packages/vite/src/utils/transpile.ts +++ b/packages/vite/src/utils/transpile.ts @@ -10,7 +10,7 @@ interface Envs { export function transpile (envs: Envs): Array { const nuxt = useNuxt() - const transpile = [] + const transpile: Array = [] for (let pattern of nuxt.options.build.transpile) { if (typeof pattern === 'function') { diff --git a/packages/vite/src/vite.ts b/packages/vite/src/vite.ts index 1c6a9c82f4..b19b29ba0f 100644 --- a/packages/vite/src/vite.ts +++ b/packages/vite/src/vite.ts @@ -4,6 +4,7 @@ import { dirname, join, normalize, resolve } from 'pathe' import type { Nuxt, NuxtBuilder, ViteConfig } from '@nuxt/schema' import { addVitePlugin, isIgnored, logger, resolvePath } from '@nuxt/kit' import replace from '@rollup/plugin-replace' +import type { RollupReplaceOptions } from '@rollup/plugin-replace' import { sanitizeFilePath } from 'mlly' import { withoutLeadingSlash } from 'ufo' import { filename } from 'pathe/utils' @@ -102,10 +103,7 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => { rootDir: nuxt.options.rootDir, composables: nuxt.options.optimization.keyedComposables, }), - replace({ - ...Object.fromEntries([';', '(', '{', '}', ' ', '\t', '\n'].map(d => [`${d}global.`, `${d}globalThis.`])), - preventAssignment: true, - }), + replace({ preventAssignment: true, ...globalThisReplacements }), virtual(nuxt.vfs), ], server: { @@ -164,10 +162,16 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => { await nuxt.callHook('vite:extend', ctx) nuxt.hook('vite:extendConfig', (config) => { - config.plugins!.push(replace({ - preventAssignment: true, - ...Object.fromEntries(Object.entries(config.define!).filter(([key]) => key.startsWith('import.meta.'))), - })) + const replaceOptions: RollupReplaceOptions = Object.create(null) + replaceOptions.preventAssignment = true + + for (const key in config.define!) { + if (key.startsWith('import.meta.')) { + replaceOptions[key] = config.define![key] + } + } + + config.plugins!.push(replace(replaceOptions)) }) if (!ctx.nuxt.options.dev) { @@ -224,3 +228,5 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => { await buildClient(ctx) await buildServer(ctx) } + +const globalThisReplacements = Object.fromEntries([';', '(', '{', '}', ' ', '\t', '\n'].map(d => [`${d}global.`, `${d}globalThis.`])) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f38e4b444e..73b476bb7e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -744,6 +744,9 @@ importers: '@types/fs-extra': specifier: 11.0.4 version: 11.0.4 + rollup: + specifier: ^4.18.0 + version: 4.18.0 unbuild: specifier: latest version: 2.0.0(sass@1.69.4)(typescript@5.4.5)