diff --git a/packages/kit/src/config/schema/build.ts b/packages/kit/src/config/schema/build.ts
index 862d237add..ce9b4b71bb 100644
--- a/packages/kit/src/config/schema/build.ts
+++ b/packages/kit/src/config/schema/build.ts
@@ -15,9 +15,7 @@ export default {
/**
* 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](https://github.com/webpack-contrib/webpack-bundle-analyzer#options-for-plugin).
+ * 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).
*
* @example
* ```js
@@ -25,7 +23,6 @@ export default {
* analyzerMode: 'static'
* }
* ```
- * @version 2
*/
analyze: false,
diff --git a/packages/nitro/package.json b/packages/nitro/package.json
index af3997a67a..f1e895ca9e 100644
--- a/packages/nitro/package.json
+++ b/packages/nitro/package.json
@@ -58,8 +58,8 @@
"pathe": "^0.2.0",
"pretty-bytes": "^5.6.0",
"rollup": "^2.58.0",
- "rollup-plugin-analyzer": "^4.0.0",
"rollup-plugin-terser": "^7.0.2",
+ "rollup-plugin-visualizer": "^5.5.2",
"serve-placeholder": "^1.2.4",
"serve-static": "^1.14.1",
"std-env": "^2.3.1",
diff --git a/packages/nitro/src/context.ts b/packages/nitro/src/context.ts
index 64d3d4adc9..edf432670f 100644
--- a/packages/nitro/src/context.ts
+++ b/packages/nitro/src/context.ts
@@ -4,6 +4,7 @@ import defu from 'defu'
import { createHooks, Hookable, NestedHooks } from 'hookable'
import type { Preset } from 'unenv'
import type { NuxtHooks, NuxtOptions } from '@nuxt/kit'
+import type { PluginVisualizerOptions } from 'rollup-plugin-visualizer'
import { tryImport, resolvePath, detectTarget, extendPreset } from './utils'
import * as PRESETS from './presets'
import type { NodeExternalsOptions } from './rollup/plugins/externals'
@@ -28,7 +29,7 @@ export interface NitroContext {
minify: boolean
sourceMap: boolean
externals: boolean | NodeExternalsOptions
- analyze: boolean
+ analyze: false | PluginVisualizerOptions
entry: string
node: boolean
preset: string
@@ -94,7 +95,7 @@ export function getNitroContext (nuxtOptions: NuxtOptions, input: NitroInput): N
minify: undefined,
sourceMap: undefined,
externals: undefined,
- analyze: undefined,
+ analyze: nuxtOptions.build.analyze as any,
entry: undefined,
node: undefined,
preset: undefined,
diff --git a/packages/nitro/src/rollup/config.ts b/packages/nitro/src/rollup/config.ts
index 78a8c04cee..d01b83a5d8 100644
--- a/packages/nitro/src/rollup/config.ts
+++ b/packages/nitro/src/rollup/config.ts
@@ -12,7 +12,7 @@ import replace from '@rollup/plugin-replace'
import virtual from '@rollup/plugin-virtual'
import wasmPlugin from '@rollup/plugin-wasm'
import inject from '@rollup/plugin-inject'
-import analyze from 'rollup-plugin-analyzer'
+import { visualizer } from 'rollup-plugin-visualizer'
import * as unenv 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
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/terser/terser#minify-nitroContext
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
}
diff --git a/packages/nuxi/src/commands/analyze.ts b/packages/nuxi/src/commands/analyze.ts
new file mode 100644
index 0000000000..98aa4a23ea
--- /dev/null
+++ b/packages/nuxi/src/commands/analyze.ts
@@ -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(() => `
+
+
+Nuxt Bundle Stats (experimental)
+
+ Nuxt Bundle Stats (experimental)
+
+`)
+
+ await server.listen()
+ }
+})
diff --git a/packages/nuxi/src/commands/index.ts b/packages/nuxi/src/commands/index.ts
index 90c58785fc..1051d57dd1 100644
--- a/packages/nuxi/src/commands/index.ts
+++ b/packages/nuxi/src/commands/index.ts
@@ -5,6 +5,7 @@ const _rDefault = r => r.default || r
export const commands = {
dev: () => import('./dev').then(_rDefault),
build: () => import('./build').then(_rDefault),
+ analyze: () => import('./analyze').then(_rDefault),
generate: () => import('./generate').then(_rDefault),
prepare: () => import('./prepare').then(_rDefault),
usage: () => import('./usage').then(_rDefault),
diff --git a/packages/nuxi/src/utils/server.ts b/packages/nuxi/src/utils/server.ts
index 046afe8f2d..43aaf5223e 100644
--- a/packages/nuxi/src/utils/server.ts
+++ b/packages/nuxi/src/utils/server.ts
@@ -2,10 +2,10 @@ import type { RequestListener } from 'http'
import type { ListenOptions } from 'listhen'
import { loading } from '@nuxt/design'
-export function createServer () {
- const listener = createDynamicFunction(createLoadingHandler('Loading...'))
+export function createServer (defaultApp?) {
+ const listener = createDynamicFunction(defaultApp || createLoadingHandler('Loading...'))
- async function listen (opts: Partial) {
+ async function listen (opts?: Partial) {
const { listen } = await import('listhen')
return listen(listener.call, opts)
}
diff --git a/packages/vite/package.json b/packages/vite/package.json
index 786287280f..b99a6455e5 100644
--- a/packages/vite/package.json
+++ b/packages/vite/package.json
@@ -32,6 +32,7 @@
"postcss-import": "^14.0.2",
"postcss-import-resolver": "^2.0.0",
"postcss-url": "^10.1.3",
+ "rollup-plugin-visualizer": "^5.5.2",
"ufo": "^0.7.9",
"vite": "^2.6.10"
},
diff --git a/packages/vite/src/client.ts b/packages/vite/src/client.ts
index c21c47cb4d..0b920163db 100644
--- a/packages/vite/src/client.ts
+++ b/packages/vite/src/client.ts
@@ -5,6 +5,8 @@ import vitePlugin from '@vitejs/plugin-vue'
import viteJsxPlugin from '@vitejs/plugin-vue-jsx'
import type { Connect } from 'vite'
+import { visualizer } from 'rollup-plugin-visualizer'
+import { transform } from 'esbuild'
import { cacheDirPlugin } from './plugins/cache-dir'
import { replace } from './plugins/replace'
import { wpfs } from './utils/wpfs'
@@ -45,6 +47,33 @@ export async function buildClient (ctx: ViteBuildContext) {
}
} 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 })
const viteServer = await vite.createServer(clientConfig)
diff --git a/packages/webpack/src/configs/client.ts b/packages/webpack/src/configs/client.ts
index 834ac2365c..b1ee9cd113 100644
--- a/packages/webpack/src/configs/client.ts
+++ b/packages/webpack/src/configs/client.ts
@@ -80,7 +80,7 @@ function clientPlugins (ctx: WebpackConfigContext) {
// 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')
// @ts-ignore
diff --git a/yarn.lock b/yarn.lock
index 205ead75ea..c30845598c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2673,8 +2673,8 @@ __metadata:
pathe: ^0.2.0
pretty-bytes: ^5.6.0
rollup: ^2.58.0
- rollup-plugin-analyzer: ^4.0.0
rollup-plugin-terser: ^7.0.2
+ rollup-plugin-visualizer: ^5.5.2
serve-placeholder: ^1.2.4
serve-static: ^1.14.1
std-env: ^2.3.1
@@ -2815,6 +2815,7 @@ __metadata:
postcss-import: ^14.0.2
postcss-import-resolver: ^2.0.0
postcss-url: ^10.1.3
+ rollup-plugin-visualizer: ^5.5.2
ufo: ^0.7.9
unbuild: latest
vite: ^2.6.10
@@ -13158,7 +13159,7 @@ fsevents@~2.3.2:
languageName: node
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
resolution: "nanoid@npm:3.1.30"
bin:
@@ -14027,6 +14028,16 @@ fsevents@~2.3.2:
languageName: node
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":
version: 8.3.0
resolution: "open@npm:8.3.0"
@@ -16906,13 +16917,6 @@ fsevents@~2.3.2:
languageName: node
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":
version: 4.0.0
resolution: "rollup-plugin-dts@npm:4.0.0"
@@ -16957,6 +16961,22 @@ fsevents@~2.3.2:
languageName: node
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":
version: 2.8.2
resolution: "rollup-pluginutils@npm:2.8.2"