diff --git a/.changeset/stale-ways-drum.md b/.changeset/stale-ways-drum.md new file mode 100644 index 000000000000..e84375a69f3f --- /dev/null +++ b/.changeset/stale-ways-drum.md @@ -0,0 +1,6 @@ +--- +"@rspack/core": patch +"@rspack/cli": patch +--- + +feat: stats for timings and builtAt diff --git a/packages/rspack-cli/src/commands/build.ts b/packages/rspack-cli/src/commands/build.ts index 02f640ccdb8e..5464c8b0b24b 100644 --- a/packages/rspack-cli/src/commands/build.ts +++ b/packages/rspack-cli/src/commands/build.ts @@ -83,12 +83,11 @@ export class BuildCommand implements RspackCommand { } } }; - console.time("build"); + let rspackOptions = { ...options, argv: { ...options } }; const errorHandler = (err, Stats) => { callback(err, Stats); - console.timeEnd("build"); }; const compiler = await cli.createCompiler( diff --git a/packages/rspack-cli/src/rspack-cli.ts b/packages/rspack-cli/src/rspack-cli.ts index 46633bd7e73f..73f8ad6d40b4 100644 --- a/packages/rspack-cli/src/rspack-cli.ts +++ b/packages/rspack-cli/src/rspack-cli.ts @@ -155,7 +155,7 @@ export class RspackCLI { } if (typeof item.stats === "undefined") { - item.stats = { preset: "errors-warnings" }; + item.stats = { preset: "errors-warnings", timings: true }; } else if (typeof item.stats === "boolean") { item.stats = item.stats ? { preset: "normal" } : { preset: "none" }; } else if (typeof item.stats === "string") { diff --git a/packages/rspack/scripts/precompile-schema.js b/packages/rspack/scripts/precompile-schema.js index d562d350ea11..2e4417b3406c 100644 --- a/packages/rspack/scripts/precompile-schema.js +++ b/packages/rspack/scripts/precompile-schema.js @@ -77,18 +77,23 @@ ajv.addKeyword({ const validate = ajv.compile(require(configSchema)); const code = standaloneCode(ajv, validate); -terser - .minify(code, { - compress: { - passes: 3 - }, - mangle: true, - ecma: 2015, - toplevel: true - }) - .then(minified => { - const code = - "/** This file was automatically generated, Run `pnpm precompile-schema` to update */\n" + - minified.code; - fs.promises.writeFile(configCheck, code); - }); +const generated = + "/** This file was automatically generated, Run `pnpm precompile-schema` to update */\n" + + code; +fs.writeFileSync(configCheck, generated); + +// terser +// .minify(code, { +// compress: { +// passes: 3 +// }, +// mangle: true, +// ecma: 2015, +// toplevel: true +// }) +// .then(minified => { +// const code = +// "/** This file was automatically generated, Run `pnpm precompile-schema` to update */\n" + +// minified.code; +// fs.promises.writeFile(configCheck, code); +// }); diff --git a/packages/rspack/src/compilation.ts b/packages/rspack/src/compilation.ts index f4f6b4533871..1574bc944c96 100644 --- a/packages/rspack/src/compilation.ts +++ b/packages/rspack/src/compilation.ts @@ -85,10 +85,14 @@ export class Compilation { inputFileSystem: any; logging: Map; name?: string; + startTime?: number; + endTime?: number; normalModuleFactory?: NormalModuleFactory; constructor(compiler: Compiler, inner: JsCompilation) { this.name = undefined; + this.startTime = undefined; + this.endTime = undefined; let processAssetsHooks = createFakeProcessAssetsHook(this); this.hooks = { processAssets: processAssetsHooks, @@ -227,6 +231,11 @@ export class Compilation { options.outputPath, !context.forToString ); + options.timings = optionOrLocalFallback(options.timings, true); + options.builtAt = optionOrLocalFallback( + options.builtAt, + !context.forToString + ); return options; } diff --git a/packages/rspack/src/compiler.ts b/packages/rspack/src/compiler.ts index 48a105ead369..39ef23701068 100644 --- a/packages/rspack/src/compiler.ts +++ b/packages/rspack/src/compiler.ts @@ -381,6 +381,7 @@ class Compiler { if (this.running) { return callback(new ConcurrentCompilationError()); } + const startTime = Date.now(); this.running = true; const doRun = () => { // @ts-expect-error @@ -407,6 +408,8 @@ class Compiler { if (err) { return finalCallback(err); } + this.compilation.startTime = startTime; + this.compilation.endTime = Date.now(); const stats = new Stats(this.compilation); this.hooks.done.callAsync(stats, err => { if (err) { diff --git a/packages/rspack/src/config/adapter-rule-use.ts b/packages/rspack/src/config/adapter-rule-use.ts index 836ca6ff068b..63c5ae6c892d 100644 --- a/packages/rspack/src/config/adapter-rule-use.ts +++ b/packages/rspack/src/config/adapter-rule-use.ts @@ -591,7 +591,7 @@ function composeJsUse( typeof sourceMap === "string" ? sourceMap : JSON.stringify(sourceMap) - ) + ) : undefined, additionalData: additionalData ? toBuffer(JSON.stringify(additionalData)) diff --git a/packages/rspack/src/config/schema.js b/packages/rspack/src/config/schema.js index d4182fe0baee..a5d6862cf5bb 100644 --- a/packages/rspack/src/config/schema.js +++ b/packages/rspack/src/config/schema.js @@ -1584,6 +1584,27 @@ module.exports = { warningsCount: { description: "Add warnings count.", type: "boolean" + }, + outputPath: { + description: "Add output path information.", + type: "boolean" + }, + chunkModules: { + description: "Add built modules information to chunk information.", + type: "boolean" + }, + chunkRelations: { + description: + "Add information about parent, children and sibling chunks to chunk information.", + type: "boolean" + }, + timings: { + description: "Add timing information.", + type: "boolean" + }, + builtAt: { + description: "Add built at time information.", + type: "boolean" } } }, diff --git a/packages/rspack/src/config/types.ts b/packages/rspack/src/config/types.ts index cc304074255c..100167d509dd 100644 --- a/packages/rspack/src/config/types.ts +++ b/packages/rspack/src/config/types.ts @@ -426,6 +426,8 @@ export interface StatsOptions { outputPath?: boolean; chunkModules?: boolean; chunkRelations?: boolean; + timings?: boolean; + builtAt?: boolean; } ///// Optimization ///// diff --git a/packages/rspack/src/stats.ts b/packages/rspack/src/stats.ts index 24101d74a259..d87a1e395112 100644 --- a/packages/rspack/src/stats.ts +++ b/packages/rspack/src/stats.ts @@ -15,6 +15,8 @@ import { LogType } from "./logging/Logger"; export type StatsCompilation = { name?: string; hash?: string; + time?: number; + builtAt?: number; publicPath?: string; outputPath?: string; assets?: binding.JsStatsAsset[]; @@ -62,6 +64,12 @@ export class Stats { if (options.hash) { obj.hash = this.#inner.getHash(); } + if (options.timings) { + obj.time = this.compilation.endTime! - this.compilation.startTime!; + } + if (options.builtAt) { + obj.builtAt = this.compilation.endTime; + } if (options.publicPath) { obj.publicPath = this.compilation.outputOptions.publicPath; } diff --git a/packages/rspack/src/watching.ts b/packages/rspack/src/watching.ts index 5ab3b3707cfd..0fe6e26b6938 100644 --- a/packages/rspack/src/watching.ts +++ b/packages/rspack/src/watching.ts @@ -28,6 +28,7 @@ class Watching { onChange?: () => void; onInvalid?: () => void; invalid: boolean; + startTime?: number; #invalidReported: boolean; #closeCallbacks?: ((err?: Error) => void)[]; #initial: boolean; @@ -206,8 +207,8 @@ class Watching { } #go(changedFiles?: ReadonlySet, removedFiles?: ReadonlySet) { + if (this.startTime === undefined) this.startTime = Date.now(); this.running = true; - const logger = this.compiler.getInfrastructureLogger("watcher"); if (this.watcher) { this.pausedWatcher = this.watcher; this.lastWatcherStartTime = Date.now(); @@ -223,27 +224,18 @@ class Watching { this.#collectedRemovedFiles); this.#collectedChangedFiles = undefined; this.#collectedRemovedFiles = undefined; - const begin = Date.now(); this.invalid = false; this.#invalidReported = false; this.compiler.hooks.watchRun.callAsync(this.compiler, err => { if (err) return this._done(err); const isRebuild = this.compiler.options.devServer && !this.#initial; - const print = isRebuild - ? () => - console.log("rebuild success, time cost", Date.now() - begin, "ms") - : () => - console.log("build success, time cost", Date.now() - begin, "ms"); const onBuild = (err?: Error) => { if (err) return this._done(err); // if (this.invalid) return this._done(null); // @ts-expect-error this._done(null); - if (!err && !this.#closed && !this.invalid) { - print(); - } }; if (isRebuild) { @@ -260,13 +252,11 @@ class Watching { */ private _done(error?: Error) { this.running = false; - let stats: Stats | null = null; const handleError = (err?: Error, cbs?: Callback[]) => { // @ts-expect-error this.compiler.hooks.failed.call(err); // this.compiler.cache.beginIdle(); // this.compiler.idle = true; - // @ts-expect-error this.handler(err, stats); if (!cbs) { cbs = this.callbacks; @@ -282,7 +272,10 @@ class Watching { const cbs = this.callbacks; this.callbacks = []; - stats = new Stats(this.compiler.compilation); + this.compiler.compilation.startTime = this.startTime; + this.compiler.compilation.endTime = Date.now(); + const stats = new Stats(this.compiler.compilation); + this.startTime = undefined; this.compiler.hooks.done.callAsync(stats, err => { if (err) return handleError(err, cbs); // @ts-expect-error @@ -298,7 +291,6 @@ class Watching { } }); for (const cb of cbs) cb(null); - // @ts-expect-error this.compiler.hooks.afterDone.call(stats); }); } diff --git a/packages/rspack/tests/Stats.test.ts b/packages/rspack/tests/Stats.test.ts index 3b68f438e661..7365f233e351 100644 --- a/packages/rspack/tests/Stats.test.ts +++ b/packages/rspack/tests/Stats.test.ts @@ -16,7 +16,7 @@ describe("Stats", () => { main: "./fixtures/a" } }); - const statsOptions = { all: true }; + const statsOptions = { all: true, timings: false, builtAt: false }; expect(typeof stats?.hash).toBe("string"); expect(stats?.toJson(statsOptions)).toMatchInlineSnapshot(` { @@ -157,7 +157,7 @@ describe("Stats", () => { context: __dirname, entry: "./fixtures/abc" }); - expect(stats?.toString()).toMatchInlineSnapshot(` + expect(stats?.toString({ timings: false })).toMatchInlineSnapshot(` "Hash: 2168fece27972fed PublicPath: auto Asset Size Chunks Chunk Names diff --git a/packages/rspack/tests/StatsTestCases.test.ts b/packages/rspack/tests/StatsTestCases.test.ts index 1845ff0e0c80..189ea484ef7e 100644 --- a/packages/rspack/tests/StatsTestCases.test.ts +++ b/packages/rspack/tests/StatsTestCases.test.ts @@ -40,7 +40,11 @@ describe("StatsTestCases", () => { }; const stats = await util.promisify(rspack)(options); if (!stats) return expect(false); - const statsOptions = options.stats ?? { all: true }; + const statsOptions = options.stats ?? { + all: true, + timings: false, + builtAt: false + }; const statsJson = stats.toJson(statsOptions); // case ends with error should generate errors if (/error$/.test(testName)) {