@@ -7,13 +7,13 @@ import { styleText } from 'node:util'
77import { intro , note , outro , taskLog } from '@clack/prompts'
88import { defineCommand } from 'citty'
99import { defu } from 'defu'
10- import { H3 , lazyEventHandler } from 'h3-next'
11- import { join } from 'pathe'
10+ import { join , relative , resolve } from 'pathe'
1211import { serve } from 'srvx'
1312
1413import { overrideEnv } from '../utils/env'
1514import { clearDir } from '../utils/fs'
1615import { loadKit } from '../utils/kit'
16+ import { acquireLock , acquireOutputLock , formatLockError } from '../utils/lockfile'
1717import { logger } from '../utils/logger'
1818import { relativeToProcess , resolveRootDir } from '../utils/paths'
1919import { dotEnvArgs , extendsArgs , logLevelArgs , rootDirArgs } from './_shared'
@@ -72,7 +72,7 @@ export default defineCommand({
7272
7373 const cwd = resolveRootDir ( ctx . args )
7474 const name = ctx . args . name || 'default'
75- const slug = name . trim ( ) . replace ( NON_WORD_RE , '_' )
75+ const slug = name . trim ( ) . replace ( NON_WORD_RE , '_' ) || 'default'
7676
7777 intro ( styleText ( 'cyan' , 'Analyzing bundle size...' ) )
7878
@@ -131,62 +131,86 @@ export default defineCommand({
131131
132132 const analyzeDir = nuxt . options . analyzeDir
133133 const buildDir = nuxt . options . buildDir
134- const outDir
135- = nuxt . options . nitro . output ?. dir || join ( nuxt . options . rootDir , '.output' )
134+ const outDir = resolve ( nuxt . options . rootDir , nuxt . options . nitro . output ?. dir || '.output' )
136135
137136 nuxt . options . build . analyze = defu ( nuxt . options . build . analyze , {
138137 filename : join ( analyzeDir , 'client.html' ) ,
139138 } )
140139
141- const tasklog = taskLog ( {
142- title : 'Building Nuxt with analysis enabled' ,
143- retainLog : false ,
144- limit : 1 ,
145- } )
146-
147- tasklog . message ( 'Clearing analyze directory...' )
148- await clearDir ( analyzeDir )
149- tasklog . message ( 'Building Nuxt...' )
150- await buildNuxt ( nuxt )
151- tasklog . success ( 'Build complete' )
140+ const lockInfo = { command : 'analyze' as const , cwd }
141+ const lock = acquireLock ( buildDir , lockInfo )
142+ if ( lock . existing ) {
143+ logger . error ( formatLockError ( lock . existing ) )
144+ throw new Error ( `Another Nuxt ${ lock . existing . command } is already running (PID ${ lock . existing . pid } ).` )
145+ }
152146
153- if ( skippedPrerenderRoutes > 0 ) {
154- logger . info ( `Skipped prerendering ${ skippedPrerenderRoutes } route${ skippedPrerenderRoutes === 1 ? '' : 's' } . Pass ${ styleText ( 'cyan' , '--prerender' ) } to include assets emitted while prerendering.` )
147+ const outputLock = acquireOutputLock ( nuxt . options . rootDir , outDir , lockInfo )
148+ if ( outputLock . existing ) {
149+ lock . release ( )
150+ logger . error ( formatLockError ( outputLock . existing ) )
151+ throw new Error ( `Another Nuxt build is already writing to ${ relative ( process . cwd ( ) , outDir ) } (PID ${ outputLock . existing . pid } ).` )
155152 }
156153
157- const endTime = Date . now ( )
154+ try {
155+ const tasklog = taskLog ( {
156+ title : 'Building Nuxt with analysis enabled' ,
157+ retainLog : false ,
158+ limit : 1 ,
159+ } )
158160
159- const meta : NuxtAnalyzeMeta = {
160- name,
161- slug,
162- startTime,
163- endTime,
164- analyzeDir,
165- buildDir,
166- outDir,
161+ tasklog . message ( 'Clearing analyze directory...' )
162+ await clearDir ( analyzeDir )
163+ tasklog . message ( 'Building Nuxt...' )
164+ await buildNuxt ( nuxt )
165+ tasklog . success ( 'Build complete' )
166+
167+ if ( skippedPrerenderRoutes > 0 ) {
168+ logger . info ( `Skipped prerendering ${ skippedPrerenderRoutes } route${ skippedPrerenderRoutes === 1 ? '' : 's' } . Pass ${ styleText ( 'cyan' , '--prerender' ) } to include assets emitted while prerendering.` )
169+ }
170+
171+ const meta : NuxtAnalyzeMeta = {
172+ name,
173+ slug,
174+ startTime,
175+ endTime : Date . now ( ) ,
176+ analyzeDir,
177+ buildDir,
178+ outDir,
179+ }
180+
181+ await nuxt . callHook ( 'build:analyze:done' , meta )
182+ await fsp . writeFile ( join ( analyzeDir , 'meta.json' ) , JSON . stringify ( meta , null , 2 ) , 'utf-8' )
183+ }
184+ finally {
185+ outputLock . release ( )
186+ lock . release ( )
167187 }
168-
169- await nuxt . callHook ( 'build:analyze:done' , meta )
170- await fsp . writeFile ( join ( analyzeDir , 'meta.json' ) , JSON . stringify ( meta , null , 2 ) , 'utf-8' )
171188
172189 note ( `${ relativeToProcess ( analyzeDir ) } \n\nDo not deploy analyze results! Use ${ styleText ( 'cyan' , 'nuxt build' ) } before deploying.` , 'Build location' )
173190
174191 if ( ctx . args . serve !== false && ! process . env . CI ) {
175- const app = new H3 ( )
176-
177- const opts = { headers : { 'content-type' : 'text/html' } }
178- const serveFile = ( filePath : string ) => lazyEventHandler ( async ( ) => {
179- const contents = await fsp . readFile ( filePath , 'utf-8' )
180- return ( ) => new Response ( contents , opts )
181- } )
192+ const headers = { 'content-type' : 'text/html' }
193+ const readReport = ( name : string ) => fsp . readFile ( join ( analyzeDir , name ) , 'utf8' ) . catch ( ( ) => undefined )
194+ const reports = new Map ( [
195+ [ '/client' , await readReport ( 'client.html' ) ] ,
196+ [ '/nitro' , await readReport ( 'nitro.html' ) ] ,
197+ ] )
182198
183199 logger . step ( 'Starting stats server...' )
184200
185- app . use ( '/client' , serveFile ( join ( analyzeDir , 'client.html' ) ) )
186- app . use ( '/nitro' , serveFile ( join ( analyzeDir , 'nitro.html' ) ) )
187- app . use ( ( ) => new Response ( indexHtml , opts ) )
188-
189- await serve ( app ) . serve ( )
201+ await serve ( {
202+ hostname : process . env . HOST || 'localhost' ,
203+ fetch ( request ) {
204+ const pathname = new URL ( request . url ) . pathname . replace ( / \/ $ / , '' )
205+ if ( reports . has ( pathname ) ) {
206+ const report = reports . get ( pathname )
207+ return report === undefined
208+ ? new Response ( 'This report was not generated by the analyze build.' , { status : 404 , headers } )
209+ : new Response ( report , { headers } )
210+ }
211+ return new Response ( indexHtml , { headers } )
212+ } ,
213+ } ) . ready ( )
190214 }
191215 else {
192216 outro ( '✨ Analysis build complete!' )
0 commit comments