Skip to content

Commit 3d16e4c

Browse files
committed
feat(build): report total build time
1 parent 79a829d commit 3d16e4c

3 files changed

Lines changed: 49 additions & 2 deletions

File tree

packages/nuxt-cli/src/commands/build.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import colors from 'picocolors'
77

88
import { showBanner } from '../utils/banner'
99
import { overrideEnv } from '../utils/env'
10+
import { formatDuration } from '../utils/formatting'
1011
import { clearBuildDir } from '../utils/fs'
1112
import { loadKit } from '../utils/kit'
1213
import { acquireLock, formatLockError } from '../utils/lockfile'
@@ -39,6 +40,8 @@ export default defineCommand({
3940
async run(ctx) {
4041
overrideEnv('production')
4142

43+
const start = Date.now()
44+
4245
const cwd = resolve(ctx.args.cwd || ctx.args.rootDir)
4346

4447
const profileArg = ctx.args.profile
@@ -127,10 +130,10 @@ export default defineCommand({
127130
// TODO: revisit later if/when nuxt build --prerender will output hybrid
128131
const dir = nitro?.options.output.publicDir
129132
const publicDir = dir ? relative(process.cwd(), dir) : '.output/public'
130-
outro(`✨ You can now deploy ${colors.cyan(publicDir)} to any static hosting!`)
133+
outro(`✨ You can now deploy ${colors.cyan(publicDir)} to any static hosting! ${colors.gray(`(${formatDuration(Date.now() - start)})`)}`)
131134
}
132135
else {
133-
outro('✨ Build complete!')
136+
outro(`✨ Build complete in ${colors.cyan(formatDuration(Date.now() - start))}!`)
134137
}
135138
}
136139
finally {

packages/nuxt-cli/src/utils/formatting.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,22 @@ export function formatInfoBox(infoObj: Record<string, string | undefined>): stri
9797

9898
return boxStr
9999
}
100+
101+
/**
102+
* Format an elapsed duration in milliseconds for display, using `ms` below a
103+
* second and seconds (or minutes and seconds) above it.
104+
*/
105+
export function formatDuration(ms: number): string {
106+
if (ms < 1000) {
107+
return `${Math.round(ms)}ms`
108+
}
109+
110+
const totalSeconds = ms / 1000
111+
if (totalSeconds < 60) {
112+
return `${Number(totalSeconds.toFixed(totalSeconds < 10 ? 2 : 1))}s`
113+
}
114+
115+
const minutes = Math.floor(totalSeconds / 60)
116+
const seconds = Math.round(totalSeconds - minutes * 60)
117+
return seconds ? `${minutes}m ${seconds}s` : `${minutes}m`
118+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { describe, expect, it } from 'vitest'
2+
3+
import { formatDuration } from '../../../src/utils/formatting'
4+
5+
describe('formatDuration', () => {
6+
it('should render sub-second durations in milliseconds', () => {
7+
expect(formatDuration(0)).toBe('0ms')
8+
expect(formatDuration(12.4)).toBe('12ms')
9+
expect(formatDuration(999)).toBe('999ms')
10+
})
11+
12+
it('should render seconds above a second', () => {
13+
expect(formatDuration(1000)).toBe('1s')
14+
expect(formatDuration(1234)).toBe('1.23s')
15+
expect(formatDuration(9999)).toBe('10s')
16+
expect(formatDuration(12_300)).toBe('12.3s')
17+
expect(formatDuration(59_000)).toBe('59s')
18+
})
19+
20+
it('should render minutes and seconds above a minute', () => {
21+
expect(formatDuration(60_000)).toBe('1m')
22+
expect(formatDuration(90_000)).toBe('1m 30s')
23+
expect(formatDuration(3_600_000)).toBe('60m')
24+
})
25+
})

0 commit comments

Comments
 (0)