From d0b34f635d40b395764ba2d50fc50c99ef4d7ef6 Mon Sep 17 00:00:00 2001 From: Yuxuan Zhang Date: Wed, 27 Dec 2023 22:04:29 -0500 Subject: [PATCH 1/9] feat(build): log duration for each task, use humanized format --- package.json | 2 ++ pnpm-lock.yaml | 14 ++++++++++++++ src/node/build/build.ts | 11 +++++++---- src/node/utils/task.ts | 15 ++++++++++++--- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index bfc8ec25..22a3b6e2 100644 --- a/package.json +++ b/package.json @@ -137,6 +137,7 @@ "@types/debug": "^4.1.12", "@types/escape-html": "^1.0.4", "@types/fs-extra": "^11.0.4", + "@types/humanize-duration": "^3.27.3", "@types/lodash.template": "^4.5.3", "@types/mark.js": "^8.11.12", "@types/markdown-it-attrs": "^4.1.3", @@ -160,6 +161,7 @@ "fs-extra": "^11.2.0", "get-port": "^7.0.0", "gray-matter": "^4.0.3", + "humanize-duration": "^3.31.0", "lint-staged": "^15.2.0", "lodash.template": "^4.5.0", "lru-cache": "^10.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ba4c0aaf..24948b52 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -111,6 +111,9 @@ importers: '@types/fs-extra': specifier: ^11.0.4 version: 11.0.4 + '@types/humanize-duration': + specifier: ^3.27.3 + version: 3.27.3 '@types/lodash.template': specifier: ^4.5.3 version: 4.5.3 @@ -180,6 +183,9 @@ importers: gray-matter: specifier: ^4.0.3 version: 4.0.3 + humanize-duration: + specifier: ^3.31.0 + version: 3.31.0 lint-staged: specifier: ^15.2.0 version: 15.2.0(supports-color@9.4.0) @@ -1146,6 +1152,10 @@ packages: resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} dev: true + /@types/humanize-duration@3.27.3: + resolution: {integrity: sha512-wiiiFYjnrYDJE/ujU7wS/NShqp12IKrejozjDtcejP0zYi+cjyjVcfZHwcFUDKVJ7tHGsmgeW2ED92ABIIjfpg==} + dev: true + /@types/jquery@3.5.29: resolution: {integrity: sha512-oXQQC9X9MOPRrMhPHHOsXqeQDnWeCDT3PelUIg/Oy8FAbzSZtFHRjc7IpbfFVmpLtJ+UOoywpRsuO5Jxjybyeg==} dependencies: @@ -2742,6 +2752,10 @@ packages: engines: {node: '>=16.17.0'} dev: true + /humanize-duration@3.31.0: + resolution: {integrity: sha512-fRrehgBG26NNZysRlTq1S+HPtDpp3u+Jzdc/d5A4cEzOD86YLAkDaJyJg8krSdCi7CJ+s7ht3fwRj8Dl+Btd0w==} + dev: true + /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} dependencies: diff --git a/src/node/build/build.ts b/src/node/build/build.ts index 7dca478a..46e4d5c0 100644 --- a/src/node/build/build.ts +++ b/src/node/build/build.ts @@ -15,12 +15,13 @@ import { task } from '../utils/task' import { bundle } from './bundle' import { generateSitemap } from './generateSitemap' import { renderPage } from './render' +import humanizeDuration from 'humanize-duration' export async function build( root?: string, buildOptions: BuildOptions & { base?: string; mpa?: string } = {} ) { - const start = Date.now() + const timeStart = performance.now() process.env.NODE_ENV = 'production' const siteConfig = await resolveConfig(root, 'build', 'production') @@ -143,9 +144,11 @@ export async function build( await siteConfig.buildEnd?.(siteConfig) clearCache() - siteConfig.logger.info( - `build complete in ${((Date.now() - start) / 1000).toFixed(2)}s.` - ) + const timeEnd = performance.now() + const duration = humanizeDuration(timeEnd - timeStart, { + maxDecimalPoints: 2 + }) + siteConfig.logger.info(`build complete in ${duration}.`) } function linkVue() { diff --git a/src/node/utils/task.ts b/src/node/utils/task.ts index 1d8a3322..667caf67 100644 --- a/src/node/utils/task.ts +++ b/src/node/utils/task.ts @@ -1,4 +1,5 @@ import ora from 'ora' +import humanizeDuration from 'humanize-duration' export const okMark = '\x1b[32m✓\x1b[0m' export const failMark = '\x1b[31m✖\x1b[0m' @@ -7,12 +8,20 @@ export async function task(taskName: string, task: () => Promise) { const spinner = ora({ discardStdin: false }) spinner.start(taskName + '...') + let symbol = okMark + const timeStart = performance.now() + try { await task() } catch (e) { - spinner.stopAndPersist({ symbol: failMark }) + symbol = failMark throw e + } finally { + const timeEnd = performance.now() + const duration = humanizeDuration(timeEnd - timeStart, { + maxDecimalPoints: 2 + }) + const text = `${taskName} (${duration})` + spinner.stopAndPersist({ symbol, text }) } - - spinner.stopAndPersist({ symbol: okMark }) } From 0b37eff97e06bd630ea14203a520d5767319803c Mon Sep 17 00:00:00 2001 From: Yuxuan Zhang Date: Wed, 27 Dec 2023 22:20:44 -0500 Subject: [PATCH 2/9] feat(build): log duration for each task, use humanized format modify log format, use dash instead of parenthesis --- src/node/utils/task.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/utils/task.ts b/src/node/utils/task.ts index 667caf67..4a65e6e6 100644 --- a/src/node/utils/task.ts +++ b/src/node/utils/task.ts @@ -21,7 +21,7 @@ export async function task(taskName: string, task: () => Promise) { const duration = humanizeDuration(timeEnd - timeStart, { maxDecimalPoints: 2 }) - const text = `${taskName} (${duration})` + const text = `${taskName} - ${duration}` spinner.stopAndPersist({ symbol, text }) } } From e568de361f3f9de24ac7e4becb64925b88721492 Mon Sep 17 00:00:00 2001 From: Yuxuan Zhang Date: Wed, 27 Dec 2023 23:23:23 -0500 Subject: [PATCH 3/9] feat(build): add progress handler for task callback, show progress for render closes #3384 --- src/node/build/build.ts | 9 ++++++--- src/node/utils/task.ts | 19 +++++++++++++++++-- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/node/build/build.ts b/src/node/build/build.ts index 46e4d5c0..7686365b 100644 --- a/src/node/build/build.ts +++ b/src/node/build/build.ts @@ -55,7 +55,7 @@ export async function build( const entryPath = path.join(siteConfig.tempDir, 'app.js') const { render } = await import(pathToFileURL(entryPath).toString()) - await task('rendering pages', async () => { + await task('rendering pages', async (updateProgress) => { const appChunk = clientResult && (clientResult.output.find( @@ -108,9 +108,11 @@ export async function build( ]) } } - + debugger + const pages = ['404.md', ...siteConfig.pages] + let count_done = 0 await pMap( - ['404.md', ...siteConfig.pages], + pages, async (page) => { await renderPage( render, @@ -124,6 +126,7 @@ export async function build( metadataScript, additionalHeadTags ) + updateProgress(++count_done, pages.length) }, { concurrency: siteConfig.buildConcurrency } ) diff --git a/src/node/utils/task.ts b/src/node/utils/task.ts index 4a65e6e6..691624a9 100644 --- a/src/node/utils/task.ts +++ b/src/node/utils/task.ts @@ -4,7 +4,12 @@ import humanizeDuration from 'humanize-duration' export const okMark = '\x1b[32m✓\x1b[0m' export const failMark = '\x1b[31m✖\x1b[0m' -export async function task(taskName: string, task: () => Promise) { +type UpdateHandle = (done: number, total?: number) => any + +export async function task( + taskName: string, + task: (update: UpdateHandle) => Promise +): Promise { const spinner = ora({ discardStdin: false }) spinner.start(taskName + '...') @@ -12,7 +17,17 @@ export async function task(taskName: string, task: () => Promise) { const timeStart = performance.now() try { - await task() + const updateHandle: UpdateHandle = (done, total) => { + if (total === undefined) { + spinner.text = `${taskName} [ ${done} ]` + } else { + // match length to display them in same width + const _total = `${total}` + const _done = `${done}`.padStart(_total.length, ' ') + spinner.text = `${taskName} [ ${_done} / ${_total} ]` + } + } + return await task(updateHandle) } catch (e) { symbol = failMark throw e From 8849d8552317ee60761634e080381838d462d848 Mon Sep 17 00:00:00 2001 From: Yuxuan Zhang Date: Wed, 27 Dec 2023 23:45:41 -0500 Subject: [PATCH 4/9] fix: remove debugger statement for local test --- src/node/build/build.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/build/build.ts b/src/node/build/build.ts index 7686365b..8d5cf937 100644 --- a/src/node/build/build.ts +++ b/src/node/build/build.ts @@ -108,7 +108,7 @@ export async function build( ]) } } - debugger + const pages = ['404.md', ...siteConfig.pages] let count_done = 0 await pMap( From 9281648b3e7422363cefab57cdc33b67960d9d64 Mon Sep 17 00:00:00 2001 From: Yuxuan Zhang Date: Fri, 29 Dec 2023 00:19:26 -0500 Subject: [PATCH 5/9] move callback function outside try catch block, refactor logic --- src/node/utils/task.ts | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/node/utils/task.ts b/src/node/utils/task.ts index 691624a9..cd77d4fd 100644 --- a/src/node/utils/task.ts +++ b/src/node/utils/task.ts @@ -4,7 +4,7 @@ import humanizeDuration from 'humanize-duration' export const okMark = '\x1b[32m✓\x1b[0m' export const failMark = '\x1b[31m✖\x1b[0m' -type UpdateHandle = (done: number, total?: number) => any +export type UpdateHandle = (done: number, total?: number) => any export async function task( taskName: string, @@ -13,23 +13,24 @@ export async function task( const spinner = ora({ discardStdin: false }) spinner.start(taskName + '...') - let symbol = okMark + const updateHandle: UpdateHandle = (done, total) => { + if (total === undefined) { + spinner.text = `${taskName} [ ${done} ]` + } else { + // match length to display them in same width + const _total = `${total}` + const _done = `${done}`.padStart(_total.length, ' ') + spinner.text = `${taskName} [ ${_done} / ${_total} ]` + } + } + const timeStart = performance.now() + let success = true try { - const updateHandle: UpdateHandle = (done, total) => { - if (total === undefined) { - spinner.text = `${taskName} [ ${done} ]` - } else { - // match length to display them in same width - const _total = `${total}` - const _done = `${done}`.padStart(_total.length, ' ') - spinner.text = `${taskName} [ ${_done} / ${_total} ]` - } - } return await task(updateHandle) } catch (e) { - symbol = failMark + success = false throw e } finally { const timeEnd = performance.now() @@ -37,6 +38,7 @@ export async function task( maxDecimalPoints: 2 }) const text = `${taskName} - ${duration}` + const symbol = success ? okMark : failMark spinner.stopAndPersist({ symbol, text }) } } From ace515b3f9e4ea956271cad281855f3d9a168da7 Mon Sep 17 00:00:00 2001 From: Yuxuan Zhang Date: Sat, 30 Dec 2023 19:10:58 -0500 Subject: [PATCH 6/9] use picocolors instead of terminal control sequence for color marks --- src/node/utils/task.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/node/utils/task.ts b/src/node/utils/task.ts index cd77d4fd..9ceb247a 100644 --- a/src/node/utils/task.ts +++ b/src/node/utils/task.ts @@ -1,8 +1,9 @@ import ora from 'ora' import humanizeDuration from 'humanize-duration' +import c from 'picocolors' -export const okMark = '\x1b[32m✓\x1b[0m' -export const failMark = '\x1b[31m✖\x1b[0m' +export const okMark = c.green('✓') +export const failMark = c.red('✖') export type UpdateHandle = (done: number, total?: number) => any From 9195369746adc479d354e5d0362c265cc0d9894e Mon Sep 17 00:00:00 2001 From: Yuxuan Zhang Date: Sat, 30 Dec 2023 19:32:02 -0500 Subject: [PATCH 7/9] expose updateHandle for subtasks --- src/node/utils/task.ts | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/node/utils/task.ts b/src/node/utils/task.ts index 9ceb247a..1466454b 100644 --- a/src/node/utils/task.ts +++ b/src/node/utils/task.ts @@ -5,7 +5,17 @@ import c from 'picocolors' export const okMark = c.green('✓') export const failMark = c.red('✖') -export type UpdateHandle = (done: number, total?: number) => any +export type UpdateHandle = ( + done?: number, + total?: number, + subtask?: string +) => any + +let updateHandle: UpdateHandle | null = null + +export const updateCurrentTask: UpdateHandle = (...args) => { + updateHandle?.(...args) +} export async function task( taskName: string, @@ -14,14 +24,17 @@ export async function task( const spinner = ora({ discardStdin: false }) spinner.start(taskName + '...') - const updateHandle: UpdateHandle = (done, total) => { - if (total === undefined) { - spinner.text = `${taskName} [ ${done} ]` + updateHandle = (done, total, subtask) => { + const taskFullName = subtask ? `${taskName} - ${subtask}` : taskName + if (done === undefined) { + spinner.text = taskFullName + '...' + } else if (total === undefined) { + spinner.text = `${taskFullName} [ ${done} ]` } else { // match length to display them in same width const _total = `${total}` const _done = `${done}`.padStart(_total.length, ' ') - spinner.text = `${taskName} [ ${_done} / ${_total} ]` + spinner.text = `${taskFullName} [ ${_done} / ${_total} ]` } } @@ -34,6 +47,7 @@ export async function task( success = false throw e } finally { + updateHandle = null const timeEnd = performance.now() const duration = humanizeDuration(timeEnd - timeStart, { maxDecimalPoints: 2 From 007fa592dafacaf8e16a3a550c85a1c4ef3eb3de Mon Sep 17 00:00:00 2001 From: Yuxuan Zhang Date: Sat, 30 Dec 2023 20:24:54 -0500 Subject: [PATCH 8/9] build/bundle: use task return value passthrough --- src/node/build/bundle.ts | 63 ++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/src/node/build/bundle.ts b/src/node/build/bundle.ts index 159bb43c..8d19cee3 100644 --- a/src/node/build/bundle.ts +++ b/src/node/build/bundle.ts @@ -143,40 +143,41 @@ export async function bundle( configFile: config.vite?.configFile }) - let clientResult!: Rollup.RollupOutput | null - let serverResult!: Rollup.RollupOutput - - await task('building client + server bundles', async () => { - clientResult = config.mpa - ? null - : ((await build(await resolveViteConfig(false))) as Rollup.RollupOutput) - serverResult = (await build( - await resolveViteConfig(true) - )) as Rollup.RollupOutput - }) + const serverResult = await task( + 'building server bundle', + () => resolveViteConfig(true).then(build) as Promise + ) - if (config.mpa) { - // in MPA mode, we need to copy over the non-js asset files from the - // server build since there is no client-side build. - await Promise.all( - serverResult.output.map(async (chunk) => { - if (!chunk.fileName.endsWith('.js')) { - const tempPath = path.resolve(config.tempDir, chunk.fileName) - const outPath = path.resolve(config.outDir, chunk.fileName) - await fs.copy(tempPath, outPath) + const clientResult = !config.mpa + ? await task( + 'building client bundle', + () => + resolveViteConfig(false).then(build) as Promise + ) + : await task('building client bundle (MPA)', async () => { + // in MPA mode, we need to copy over the non-js asset files from the + // server build since there is no client-side build. + await Promise.all( + serverResult.output.map(async (chunk) => { + if (!chunk.fileName.endsWith('.js')) { + const tempPath = path.resolve(config.tempDir, chunk.fileName) + const outPath = path.resolve(config.outDir, chunk.fileName) + await fs.copy(tempPath, outPath) + } + }) + ) + // also copy over public dir + const publicDir = path.resolve(config.srcDir, 'public') + if (fs.existsSync(publicDir)) { + await fs.copy(publicDir, config.outDir) + } + // build