mirror of
https://github.com/nuxt/nuxt.git
synced 2025-01-18 17:35:57 +00:00
feat(nuxi): bundle analyzer (#701)
Co-authored-by: Pooya Parsa <pyapar@gmail.com>
This commit is contained in:
parent
694e95b2b4
commit
f0b9474b40
@ -15,9 +15,7 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* Nuxt uses `webpack-bundle-analyzer` to visualize your bundles and how to optimize them.
|
* Nuxt uses `webpack-bundle-analyzer` to visualize your bundles and how to optimize them.
|
||||||
*
|
*
|
||||||
* This option is normally enabled by the CLI argument `--analyze`.
|
* Set to `true` to enable bundle analysis, or pass an object with options: [for webpack](https://github.com/webpack-contrib/webpack-bundle-analyzer#options-for-plugin) or [for vite](https://github.com/btd/rollup-plugin-visualizer#options).
|
||||||
*
|
|
||||||
* Set to `true` to enable bundle analysis, or pass [an object with options](https://github.com/webpack-contrib/webpack-bundle-analyzer#options-for-plugin).
|
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```js
|
* ```js
|
||||||
@ -25,7 +23,6 @@ export default {
|
|||||||
* analyzerMode: 'static'
|
* analyzerMode: 'static'
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
* @version 2
|
|
||||||
*/
|
*/
|
||||||
analyze: false,
|
analyze: false,
|
||||||
|
|
||||||
|
@ -58,8 +58,8 @@
|
|||||||
"pathe": "^0.2.0",
|
"pathe": "^0.2.0",
|
||||||
"pretty-bytes": "^5.6.0",
|
"pretty-bytes": "^5.6.0",
|
||||||
"rollup": "^2.58.0",
|
"rollup": "^2.58.0",
|
||||||
"rollup-plugin-analyzer": "^4.0.0",
|
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
|
"rollup-plugin-visualizer": "^5.5.2",
|
||||||
"serve-placeholder": "^1.2.4",
|
"serve-placeholder": "^1.2.4",
|
||||||
"serve-static": "^1.14.1",
|
"serve-static": "^1.14.1",
|
||||||
"std-env": "^2.3.1",
|
"std-env": "^2.3.1",
|
||||||
|
@ -4,6 +4,7 @@ import defu from 'defu'
|
|||||||
import { createHooks, Hookable, NestedHooks } from 'hookable'
|
import { createHooks, Hookable, NestedHooks } from 'hookable'
|
||||||
import type { Preset } from 'unenv'
|
import type { Preset } from 'unenv'
|
||||||
import type { NuxtHooks, NuxtOptions } from '@nuxt/kit'
|
import type { NuxtHooks, NuxtOptions } from '@nuxt/kit'
|
||||||
|
import type { PluginVisualizerOptions } from 'rollup-plugin-visualizer'
|
||||||
import { tryImport, resolvePath, detectTarget, extendPreset } from './utils'
|
import { tryImport, resolvePath, detectTarget, extendPreset } from './utils'
|
||||||
import * as PRESETS from './presets'
|
import * as PRESETS from './presets'
|
||||||
import type { NodeExternalsOptions } from './rollup/plugins/externals'
|
import type { NodeExternalsOptions } from './rollup/plugins/externals'
|
||||||
@ -28,7 +29,7 @@ export interface NitroContext {
|
|||||||
minify: boolean
|
minify: boolean
|
||||||
sourceMap: boolean
|
sourceMap: boolean
|
||||||
externals: boolean | NodeExternalsOptions
|
externals: boolean | NodeExternalsOptions
|
||||||
analyze: boolean
|
analyze: false | PluginVisualizerOptions
|
||||||
entry: string
|
entry: string
|
||||||
node: boolean
|
node: boolean
|
||||||
preset: string
|
preset: string
|
||||||
@ -94,7 +95,7 @@ export function getNitroContext (nuxtOptions: NuxtOptions, input: NitroInput): N
|
|||||||
minify: undefined,
|
minify: undefined,
|
||||||
sourceMap: undefined,
|
sourceMap: undefined,
|
||||||
externals: undefined,
|
externals: undefined,
|
||||||
analyze: undefined,
|
analyze: nuxtOptions.build.analyze as any,
|
||||||
entry: undefined,
|
entry: undefined,
|
||||||
node: undefined,
|
node: undefined,
|
||||||
preset: undefined,
|
preset: undefined,
|
||||||
|
@ -12,7 +12,7 @@ import replace from '@rollup/plugin-replace'
|
|||||||
import virtual from '@rollup/plugin-virtual'
|
import virtual from '@rollup/plugin-virtual'
|
||||||
import wasmPlugin from '@rollup/plugin-wasm'
|
import wasmPlugin from '@rollup/plugin-wasm'
|
||||||
import inject from '@rollup/plugin-inject'
|
import inject from '@rollup/plugin-inject'
|
||||||
import analyze from 'rollup-plugin-analyzer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
import * as unenv from 'unenv'
|
import * as unenv from 'unenv'
|
||||||
|
|
||||||
import type { Preset } from 'unenv'
|
import type { Preset } from 'unenv'
|
||||||
@ -309,11 +309,6 @@ export const getRollupConfig = (nitroContext: NitroContext) => {
|
|||||||
// https://github.com/rollup/plugins/tree/master/packages/inject
|
// https://github.com/rollup/plugins/tree/master/packages/inject
|
||||||
rollupConfig.plugins.push(inject(env.inject))
|
rollupConfig.plugins.push(inject(env.inject))
|
||||||
|
|
||||||
if (nitroContext.analyze) {
|
|
||||||
// https://github.com/doesdev/rollup-plugin-analyzer
|
|
||||||
rollupConfig.plugins.push(analyze())
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/TrySound/rollup-plugin-terser
|
// https://github.com/TrySound/rollup-plugin-terser
|
||||||
// https://github.com/terser/terser#minify-nitroContext
|
// https://github.com/terser/terser#minify-nitroContext
|
||||||
if (nitroContext.minify) {
|
if (nitroContext.minify) {
|
||||||
@ -328,5 +323,14 @@ export const getRollupConfig = (nitroContext: NitroContext) => {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nitroContext.analyze) {
|
||||||
|
// https://github.com/btd/rollup-plugin-visualizer
|
||||||
|
rollupConfig.plugins.push(visualizer({
|
||||||
|
...nitroContext.analyze,
|
||||||
|
filename: nitroContext.analyze.filename.replace('{name}', 'nitro'),
|
||||||
|
title: 'Nitro Server bundle stats'
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
return rollupConfig
|
return rollupConfig
|
||||||
}
|
}
|
||||||
|
77
packages/nuxi/src/commands/analyze.ts
Normal file
77
packages/nuxi/src/commands/analyze.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { promises as fsp } from 'fs'
|
||||||
|
import { join, resolve } from 'pathe'
|
||||||
|
import { createApp, lazyHandle } from 'h3'
|
||||||
|
import type { PluginVisualizerOptions } from 'rollup-plugin-visualizer'
|
||||||
|
import { createServer } from '../utils/server'
|
||||||
|
import { writeTypes } from '../utils/prepare'
|
||||||
|
import { loadKit } from '../utils/kit'
|
||||||
|
import { clearDir } from '../utils/fs'
|
||||||
|
import { defineNuxtCommand } from './index'
|
||||||
|
|
||||||
|
export default defineNuxtCommand({
|
||||||
|
meta: {
|
||||||
|
name: 'analyze',
|
||||||
|
usage: 'npx nuxi analyze [rootDir]',
|
||||||
|
description: 'Build nuxt and analyze production bundle (experimental)'
|
||||||
|
},
|
||||||
|
async invoke (args) {
|
||||||
|
process.env.NODE_ENV = process.env.NODE_ENV || 'production'
|
||||||
|
|
||||||
|
const rootDir = resolve(args._[0] || '.')
|
||||||
|
const statsDir = join(rootDir, '.nuxt/stats')
|
||||||
|
|
||||||
|
const { loadNuxt, buildNuxt } = await loadKit(rootDir)
|
||||||
|
|
||||||
|
const analyzeOptions: PluginVisualizerOptions = {
|
||||||
|
template: 'treemap',
|
||||||
|
projectRoot: rootDir,
|
||||||
|
filename: join(statsDir, '{name}.html')
|
||||||
|
}
|
||||||
|
|
||||||
|
const nuxt = await loadNuxt({
|
||||||
|
rootDir,
|
||||||
|
config: {
|
||||||
|
build: {
|
||||||
|
// @ts-ignore
|
||||||
|
analyze: analyzeOptions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
await clearDir(nuxt.options.buildDir)
|
||||||
|
await writeTypes(nuxt)
|
||||||
|
await buildNuxt(nuxt)
|
||||||
|
|
||||||
|
const app = createApp()
|
||||||
|
const server = createServer(app)
|
||||||
|
|
||||||
|
const serveFile = (filePath: string) => lazyHandle(async () => {
|
||||||
|
const contents = await fsp.readFile(filePath, 'utf-8')
|
||||||
|
return (_req, res) => { res.end(contents) }
|
||||||
|
})
|
||||||
|
|
||||||
|
console.warn('Do not deploy analyze results! Use `nuxi build` before deployng.')
|
||||||
|
|
||||||
|
console.info('Starting stats server...')
|
||||||
|
|
||||||
|
app.use('/client', serveFile(join(statsDir, 'client.html')))
|
||||||
|
app.use('/nitro', serveFile(join(statsDir, 'nitro.html')))
|
||||||
|
app.use(() => `<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title><meta charset="utf-8">Nuxt Bundle Stats (experimental)</title>
|
||||||
|
</head>
|
||||||
|
<h1>Nuxt Bundle Stats (experimental)</h1>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="/nitro">Nitro server bundle stats</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/client">Client bundle stats</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</html>`)
|
||||||
|
|
||||||
|
await server.listen()
|
||||||
|
}
|
||||||
|
})
|
@ -5,6 +5,7 @@ const _rDefault = r => r.default || r
|
|||||||
export const commands = {
|
export const commands = {
|
||||||
dev: () => import('./dev').then(_rDefault),
|
dev: () => import('./dev').then(_rDefault),
|
||||||
build: () => import('./build').then(_rDefault),
|
build: () => import('./build').then(_rDefault),
|
||||||
|
analyze: () => import('./analyze').then(_rDefault),
|
||||||
generate: () => import('./generate').then(_rDefault),
|
generate: () => import('./generate').then(_rDefault),
|
||||||
prepare: () => import('./prepare').then(_rDefault),
|
prepare: () => import('./prepare').then(_rDefault),
|
||||||
usage: () => import('./usage').then(_rDefault),
|
usage: () => import('./usage').then(_rDefault),
|
||||||
|
@ -2,10 +2,10 @@ import type { RequestListener } from 'http'
|
|||||||
import type { ListenOptions } from 'listhen'
|
import type { ListenOptions } from 'listhen'
|
||||||
import { loading } from '@nuxt/design'
|
import { loading } from '@nuxt/design'
|
||||||
|
|
||||||
export function createServer () {
|
export function createServer (defaultApp?) {
|
||||||
const listener = createDynamicFunction(createLoadingHandler('Loading...'))
|
const listener = createDynamicFunction(defaultApp || createLoadingHandler('Loading...'))
|
||||||
|
|
||||||
async function listen (opts: Partial<ListenOptions>) {
|
async function listen (opts?: Partial<ListenOptions>) {
|
||||||
const { listen } = await import('listhen')
|
const { listen } = await import('listhen')
|
||||||
return listen(listener.call, opts)
|
return listen(listener.call, opts)
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
"postcss-import": "^14.0.2",
|
"postcss-import": "^14.0.2",
|
||||||
"postcss-import-resolver": "^2.0.0",
|
"postcss-import-resolver": "^2.0.0",
|
||||||
"postcss-url": "^10.1.3",
|
"postcss-url": "^10.1.3",
|
||||||
|
"rollup-plugin-visualizer": "^5.5.2",
|
||||||
"ufo": "^0.7.9",
|
"ufo": "^0.7.9",
|
||||||
"vite": "^2.6.10"
|
"vite": "^2.6.10"
|
||||||
},
|
},
|
||||||
|
@ -5,6 +5,8 @@ import vitePlugin from '@vitejs/plugin-vue'
|
|||||||
import viteJsxPlugin from '@vitejs/plugin-vue-jsx'
|
import viteJsxPlugin from '@vitejs/plugin-vue-jsx'
|
||||||
import type { Connect } from 'vite'
|
import type { Connect } from 'vite'
|
||||||
|
|
||||||
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
|
import { transform } from 'esbuild'
|
||||||
import { cacheDirPlugin } from './plugins/cache-dir'
|
import { cacheDirPlugin } from './plugins/cache-dir'
|
||||||
import { replace } from './plugins/replace'
|
import { replace } from './plugins/replace'
|
||||||
import { wpfs } from './utils/wpfs'
|
import { wpfs } from './utils/wpfs'
|
||||||
@ -45,6 +47,33 @@ export async function buildClient (ctx: ViteBuildContext) {
|
|||||||
}
|
}
|
||||||
} as ViteOptions)
|
} as ViteOptions)
|
||||||
|
|
||||||
|
// Add analyze plugin if needed
|
||||||
|
if (ctx.nuxt.options.build.analyze) {
|
||||||
|
clientConfig.plugins.push({
|
||||||
|
name: 'nuxt-analyze-minify',
|
||||||
|
async generateBundle (_opts, outputBundle) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
for (const [_bundleId, bundle] of Object.entries(outputBundle)) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
clientConfig.plugins.push(visualizer({
|
||||||
|
...ctx.nuxt.options.build.analyze as any,
|
||||||
|
// @ts-ignore
|
||||||
|
filename: ctx.nuxt.options.build.analyze.filename.replace('{name}', 'client'),
|
||||||
|
title: 'Client bundle stats',
|
||||||
|
gzipSize: true
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
await ctx.nuxt.callHook('vite:extendConfig', clientConfig, { isClient: true, isServer: false })
|
await ctx.nuxt.callHook('vite:extendConfig', clientConfig, { isClient: true, isServer: false })
|
||||||
|
|
||||||
const viteServer = await vite.createServer(clientConfig)
|
const viteServer = await vite.createServer(clientConfig)
|
||||||
|
@ -80,7 +80,7 @@ function clientPlugins (ctx: WebpackConfigContext) {
|
|||||||
|
|
||||||
// Webpack Bundle Analyzer
|
// Webpack Bundle Analyzer
|
||||||
// https://github.com/webpack-contrib/webpack-bundle-analyzer
|
// https://github.com/webpack-contrib/webpack-bundle-analyzer
|
||||||
if (!ctx.isDev && options.build.analyze) {
|
if (!ctx.isDev && ctx.name === 'client' && options.build.analyze) {
|
||||||
const statsDir = resolve(options.buildDir, 'stats')
|
const statsDir = resolve(options.buildDir, 'stats')
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
38
yarn.lock
38
yarn.lock
@ -2673,8 +2673,8 @@ __metadata:
|
|||||||
pathe: ^0.2.0
|
pathe: ^0.2.0
|
||||||
pretty-bytes: ^5.6.0
|
pretty-bytes: ^5.6.0
|
||||||
rollup: ^2.58.0
|
rollup: ^2.58.0
|
||||||
rollup-plugin-analyzer: ^4.0.0
|
|
||||||
rollup-plugin-terser: ^7.0.2
|
rollup-plugin-terser: ^7.0.2
|
||||||
|
rollup-plugin-visualizer: ^5.5.2
|
||||||
serve-placeholder: ^1.2.4
|
serve-placeholder: ^1.2.4
|
||||||
serve-static: ^1.14.1
|
serve-static: ^1.14.1
|
||||||
std-env: ^2.3.1
|
std-env: ^2.3.1
|
||||||
@ -2815,6 +2815,7 @@ __metadata:
|
|||||||
postcss-import: ^14.0.2
|
postcss-import: ^14.0.2
|
||||||
postcss-import-resolver: ^2.0.0
|
postcss-import-resolver: ^2.0.0
|
||||||
postcss-url: ^10.1.3
|
postcss-url: ^10.1.3
|
||||||
|
rollup-plugin-visualizer: ^5.5.2
|
||||||
ufo: ^0.7.9
|
ufo: ^0.7.9
|
||||||
unbuild: latest
|
unbuild: latest
|
||||||
vite: ^2.6.10
|
vite: ^2.6.10
|
||||||
@ -13158,7 +13159,7 @@ fsevents@~2.3.2:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"nanoid@npm:^3.1.23, nanoid@npm:^3.1.30":
|
"nanoid@npm:^3.1.22, nanoid@npm:^3.1.23, nanoid@npm:^3.1.30":
|
||||||
version: 3.1.30
|
version: 3.1.30
|
||||||
resolution: "nanoid@npm:3.1.30"
|
resolution: "nanoid@npm:3.1.30"
|
||||||
bin:
|
bin:
|
||||||
@ -14027,6 +14028,16 @@ fsevents@~2.3.2:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"open@npm:^7.4.2":
|
||||||
|
version: 7.4.2
|
||||||
|
resolution: "open@npm:7.4.2"
|
||||||
|
dependencies:
|
||||||
|
is-docker: ^2.0.0
|
||||||
|
is-wsl: ^2.1.1
|
||||||
|
checksum: 3333900ec0e420d64c23b831bc3467e57031461d843c801f569b2204a1acc3cd7b3ec3c7897afc9dde86491dfa289708eb92bba164093d8bd88fb2c231843c91
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"open@npm:^8.0.5":
|
"open@npm:^8.0.5":
|
||||||
version: 8.3.0
|
version: 8.3.0
|
||||||
resolution: "open@npm:8.3.0"
|
resolution: "open@npm:8.3.0"
|
||||||
@ -16906,13 +16917,6 @@ fsevents@~2.3.2:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"rollup-plugin-analyzer@npm:^4.0.0":
|
|
||||||
version: 4.0.0
|
|
||||||
resolution: "rollup-plugin-analyzer@npm:4.0.0"
|
|
||||||
checksum: 72f794f79efe4f620674a48949be9f64dc3cbf601c52ff90ae7cbeaacb604b86bb34321695d0f8f320e5c0b47880022849d92c51d231e1b32c6757a2c627be5e
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"rollup-plugin-dts@npm:^4.0.0":
|
"rollup-plugin-dts@npm:^4.0.0":
|
||||||
version: 4.0.0
|
version: 4.0.0
|
||||||
resolution: "rollup-plugin-dts@npm:4.0.0"
|
resolution: "rollup-plugin-dts@npm:4.0.0"
|
||||||
@ -16957,6 +16961,22 @@ fsevents@~2.3.2:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"rollup-plugin-visualizer@npm:^5.5.2":
|
||||||
|
version: 5.5.2
|
||||||
|
resolution: "rollup-plugin-visualizer@npm:5.5.2"
|
||||||
|
dependencies:
|
||||||
|
nanoid: ^3.1.22
|
||||||
|
open: ^7.4.2
|
||||||
|
source-map: ^0.7.3
|
||||||
|
yargs: ^16.2.0
|
||||||
|
peerDependencies:
|
||||||
|
rollup: ^2.0.0
|
||||||
|
bin:
|
||||||
|
rollup-plugin-visualizer: dist/bin/cli.js
|
||||||
|
checksum: b8a252c25efcf3dbd17557517768acc43208005dc9e3b805c3411dc226dd6765fc9779bf5c91577e909801a83b5f0bc2f6338e5b715f8ca8b4ecc924b12e8f25
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"rollup-pluginutils@npm:^2.8.2":
|
"rollup-pluginutils@npm:^2.8.2":
|
||||||
version: 2.8.2
|
version: 2.8.2
|
||||||
resolution: "rollup-pluginutils@npm:2.8.2"
|
resolution: "rollup-pluginutils@npm:2.8.2"
|
||||||
|
Loading…
Reference in New Issue
Block a user