diff --git a/packages/webpack/src/plugins/vue/client.ts b/packages/webpack/src/plugins/vue/client.ts index 761ef1c40e..14c0c184e3 100644 --- a/packages/webpack/src/plugins/vue/client.ts +++ b/packages/webpack/src/plugins/vue/client.ts @@ -17,10 +17,6 @@ interface PluginOptions { nuxt: Nuxt } -function uniq (items: T[]) { - return [...new Set(items)] -} - export default class VueSSRClientPlugin { options: PluginOptions @@ -34,38 +30,45 @@ export default class VueSSRClientPlugin { compiler.hooks.afterEmit.tap('VueSSRClientPlugin', async (compilation: Compilation) => { const stats = compilation.getStats().toJson() - const allFiles = uniq(stats.assets! - .map(a => a.name)) - .filter(file => !isHotUpdate(file)) + const initialFiles = new Set() + for (const name in stats.entrypoints!) { + const entryAssets = stats.entrypoints![name]!.assets! + for (const asset of entryAssets) { + const file = asset.name + if ((isJS(file) || isCSS(file)) && !isHotUpdate(file)) { + initialFiles.add(file) + } + } + } - const initialFiles = uniq(Object.keys(stats.entrypoints!) - .map(name => stats.entrypoints![name].assets!) - .reduce((files, entryAssets) => files.concat(entryAssets.map(entryAsset => entryAsset.name)), [] as string[]) - .filter(file => isJS(file) || isCSS(file))) - .filter(file => !isHotUpdate(file)) + const allFiles = new Set() + const asyncFiles = new Set() - const asyncFiles = allFiles - .filter(file => isJS(file) || isCSS(file)) - .filter(file => !initialFiles.includes(file)) - .filter(file => !isHotUpdate(file)) + for (const asset of stats.assets!) { + const file = asset.name + if (!isHotUpdate(file)) { + allFiles.add(file) + if (initialFiles.has(file)) { continue } + if (isJS(file) || isCSS(file)) { + asyncFiles.add(file) + } + } + } const assetsMapping: Record = {} - stats.assets! - .filter(({ name }) => isJS(name)) - .filter(({ name }) => !isHotUpdate(name)) - .forEach(({ name, chunkNames = [] }) => { + for (const { name, chunkNames = [] } of stats.assets!) { + if (isJS(name) && !isHotUpdate(name)) { const componentHash = hash(chunkNames.join('|')) - if (!assetsMapping[componentHash]) { - assetsMapping[componentHash] = [] - } - assetsMapping[componentHash].push(name) - }) + const map = assetsMapping[componentHash] ||= [] + map.push(name) + } + } const webpackManifest = { publicPath: stats.publicPath, - all: allFiles, - initial: initialFiles, - async: asyncFiles, + all: [...allFiles], + initial: [...initialFiles], + async: [...asyncFiles], modules: { /* [identifier: string]: Array */ } as Record, assetsMapping, } @@ -78,7 +81,7 @@ export default class VueSSRClientPlugin { if (m.chunks!.length === 1) { const [cid] = m.chunks! const chunk = stats.chunks!.find(c => c.id === cid) - if (!chunk || !chunk.files) { + if (!chunk || !chunk.files || !cid) { return } const id = m.identifier!.replace(/\s\w+$/, '') // remove appended hash diff --git a/packages/webpack/src/plugins/vue/server.ts b/packages/webpack/src/plugins/vue/server.ts index 1729908751..646589c601 100644 --- a/packages/webpack/src/plugins/vue/server.ts +++ b/packages/webpack/src/plugins/vue/server.ts @@ -26,7 +26,7 @@ export default class VueSSRServerPlugin { }, (assets: any, cb: any) => { const stats = compilation.getStats().toJson() const [entryName] = Object.keys(stats.entrypoints!) - const entryInfo = stats.entrypoints![entryName] + const entryInfo = stats.entrypoints![entryName!] if (!entryInfo) { // #5553 diff --git a/packages/webpack/src/plugins/vue/util.ts b/packages/webpack/src/plugins/vue/util.ts index 7d2788dac9..58ad48d38c 100644 --- a/packages/webpack/src/plugins/vue/util.ts +++ b/packages/webpack/src/plugins/vue/util.ts @@ -13,8 +13,7 @@ export const validate = (compiler: Compiler) => { if (!compiler.options.externals) { logger.info( - 'It is recommended to externalize dependencies in the server build for ' + - 'better build performance.', + 'It is recommended to externalize dependencies in the server build for better build performance.', ) } } diff --git a/packages/webpack/src/presets/esbuild.ts b/packages/webpack/src/presets/esbuild.ts index e85926462b..6a778cd023 100644 --- a/packages/webpack/src/presets/esbuild.ts +++ b/packages/webpack/src/presets/esbuild.ts @@ -16,10 +16,10 @@ export function esbuild (ctx: WebpackConfigContext) { loader: 'esbuild-loader', exclude: (file) => { // Not exclude files outside node_modules - file = file.split('node_modules', 2)[1] - if (!file) { return false } + const lastSegment = file.split('node_modules', 2)[1] + if (!lastSegment) { return false } - return !ctx.transpile.some(module => module.test(file)) + return !ctx.transpile.some(module => module.test(lastSegment)) }, resolve: { fullySpecified: false, diff --git a/packages/webpack/src/presets/style.ts b/packages/webpack/src/presets/style.ts index 5c0a14ab26..c9fa4be788 100644 --- a/packages/webpack/src/presets/style.ts +++ b/packages/webpack/src/presets/style.ts @@ -21,14 +21,15 @@ function minimizer (ctx: WebpackConfigContext) { } function extractCSS (ctx: WebpackConfigContext) { + const config = ctx.userConfig.extractCSS + if (!config) { return } // CSS extraction - if (ctx.userConfig.extractCSS) { - ctx.config.plugins!.push(new MiniCssExtractPlugin({ - filename: fileName(ctx, 'css'), - chunkFilename: fileName(ctx, 'css'), - ...ctx.userConfig.extractCSS === true ? {} : ctx.userConfig.extractCSS, - })) - } + const filename = fileName(ctx, 'css') + ctx.config.plugins!.push(new MiniCssExtractPlugin({ + filename, + chunkFilename: filename, + ...config === true ? {} : config, + })) } function loaders (ctx: WebpackConfigContext) { diff --git a/packages/webpack/src/utils/postcss.ts b/packages/webpack/src/utils/postcss.ts index da73650726..89d089f071 100644 --- a/packages/webpack/src/utils/postcss.ts +++ b/packages/webpack/src/utils/postcss.ts @@ -6,21 +6,18 @@ import { defu } from 'defu' const isPureObject = (obj: unknown): obj is Object => obj !== null && !Array.isArray(obj) && typeof obj === 'object' +const ensureItemIsLast = (item: string) => (arr: string[]) => { + const index = arr.indexOf(item) + if (index !== -1) { + arr.splice(index, 1) + arr.push(item) + } + return arr +} + const orderPresets = { - cssnanoLast (names: string[]) { - const nanoIndex = names.indexOf('cssnano') - if (nanoIndex !== names.length - 1) { - names.push(names.splice(nanoIndex, 1)[0]) - } - return names - }, - autoprefixerLast (names: string[]) { - const nanoIndex = names.indexOf('autoprefixer') - if (nanoIndex !== names.length - 1) { - names.push(names.splice(nanoIndex, 1)[0]) - } - return names - }, + cssnanoLast: ensureItemIsLast('cssnano'), + autoprefixerLast: ensureItemIsLast('autoprefixer'), autoprefixerAndCssnanoLast (names: string[]) { return orderPresets.cssnanoLast(orderPresets.autoprefixerLast(names)) }, diff --git a/packages/webpack/src/virtual-modules.ts b/packages/webpack/src/virtual-modules.ts index c9a05db442..8c117756db 100644 --- a/packages/webpack/src/virtual-modules.ts +++ b/packages/webpack/src/virtual-modules.ts @@ -8,7 +8,7 @@ export function registerVirtualModules () { const virtualModules = new VirtualModulesPlugin(nuxt.vfs) const writeFiles = () => { for (const filePath in nuxt.vfs) { - virtualModules.writeModule(filePath, nuxt.vfs[filePath]) + virtualModules.writeModule(filePath, nuxt.vfs[filePath] || '') } }