From cb15ca7b89b3c12e061678fdf7debafb9039ee0e Mon Sep 17 00:00:00 2001 From: Jakub Romanczyk Date: Tue, 13 May 2025 14:50:47 +0200 Subject: [PATCH 1/4] feat: add support for profiling rspack --- packages/repack/package.json | 1 + .../repack/src/commands/rspack/profile.ts | 75 +++++++++++++++++++ packages/repack/src/commands/rspack/start.ts | 9 +++ pnpm-lock.yaml | 9 +++ 4 files changed, 94 insertions(+) create mode 100644 packages/repack/src/commands/rspack/profile.ts diff --git a/packages/repack/package.json b/packages/repack/package.json index 1652abfbb..d32ebf524 100644 --- a/packages/repack/package.json +++ b/packages/repack/package.json @@ -84,6 +84,7 @@ "estree-util-is-identifier-name": "^1.1.0", "events": "^3.3.0", "execa": "^5.0.0", + "exit-hook": "^4.0.0", "flow-remove-types": "^2.268.0", "gradient-string": "^2.0.2", "image-size": "^1.1.1", diff --git a/packages/repack/src/commands/rspack/profile.ts b/packages/repack/src/commands/rspack/profile.ts new file mode 100644 index 000000000..472e2df8c --- /dev/null +++ b/packages/repack/src/commands/rspack/profile.ts @@ -0,0 +1,75 @@ +/** + * Reference: https://github.com/web-infra-dev/rspack/blob/bad70431988d77d8a95320066f72c77b6cc4c120/packages/rspack-cli/src/utils/profile.ts + */ + +/** + * `RSPACK_PROFILE=ALL` // all trace events + * `RSPACK_PROFILE=OVERVIEW` // overview trace events + * `RSPACK_PROFILE=warn,tokio::net=info` // trace filter from https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#example-syntax + */ +import fs from 'node:fs'; +import path from 'node:path'; +import { rspack } from '@rspack/core'; + +const overviewTraceFilter = 'info'; +const allTraceFilter = 'trace'; +const defaultRustTraceLayer = 'chrome'; + +function resolveLayer(value: string) { + if (value === 'OVERVIEW') { + return overviewTraceFilter; + } + if (value === 'ALL') { + return allTraceFilter; + } + return value; +} + +export async function applyProfile( + filterValue: string, + traceLayer: string = defaultRustTraceLayer, + traceOutput?: string +) { + const { asyncExitHook } = await import('exit-hook'); + + if (traceLayer !== 'chrome' && traceLayer !== 'logger') { + throw new Error(`unsupported trace layer: ${traceLayer}`); + } + + if (!traceOutput) { + const timestamp = Date.now(); + const defaultOutputDir = path.resolve( + `.rspack-profile-${timestamp}-${process.pid}` + ); + const defaultRustTraceChromeOutput = path.join( + defaultOutputDir, + 'trace.json' + ); + const defaultRustTraceLoggerOutput = 'stdout'; + + const defaultTraceOutput = + traceLayer === 'chrome' + ? defaultRustTraceChromeOutput + : defaultRustTraceLoggerOutput; + + // biome-ignore lint/style/noParameterAssign: setting default value makes sense + traceOutput = defaultTraceOutput; + } + + const filter = resolveLayer(filterValue); + + await ensureFileDir(traceOutput); + await rspack.experiments.globalTrace.register( + filter, + traceLayer, + traceOutput + ); + asyncExitHook(rspack.experiments.globalTrace.cleanup, { + wait: 500, + }); +} + +async function ensureFileDir(outputFilePath: string) { + const dir = path.dirname(outputFilePath); + await fs.promises.mkdir(dir, { recursive: true }); +} diff --git a/packages/repack/src/commands/rspack/start.ts b/packages/repack/src/commands/rspack/start.ts index f6ed0c782..2541a0cb3 100644 --- a/packages/repack/src/commands/rspack/start.ts +++ b/packages/repack/src/commands/rspack/start.ts @@ -73,6 +73,15 @@ export async function start( }); } + if (process.env.RSPACK_PROFILE) { + const { applyProfile } = await import('./profile.js'); + await applyProfile( + process.env.RSPACK_PROFILE, + process.env.RSPACK_TRACE_LAYER, + process.env.RSPACK_TRACE_OUTPUT + ); + } + const compiler = new Compiler(configs, reporter, cliConfig.root); const { createServer } = await import('@callstack/repack-dev-server'); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6bfcfd4bc..52362736f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -557,6 +557,9 @@ importers: execa: specifier: ^5.0.0 version: 5.1.1 + exit-hook: + specifier: ^4.0.0 + version: 4.0.0 flow-remove-types: specifier: ^2.268.0 version: 2.268.0 @@ -4606,6 +4609,10 @@ packages: resolution: {integrity: sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==} engines: {node: ^18.19.0 || >=20.5.0} + exit-hook@4.0.0: + resolution: {integrity: sha512-Fqs7ChZm72y40wKjOFXBKg7nJZvQJmewP5/7LtePDdnah/+FH9Hp5sgMujSCMPXlxOAW2//1jrW9pnsY7o20vQ==} + engines: {node: '>=18'} + exit@0.1.2: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} @@ -12824,6 +12831,8 @@ snapshots: strip-final-newline: 4.0.0 yoctocolors: 2.1.1 + exit-hook@4.0.0: {} + exit@0.1.2: {} expand-tilde@2.0.2: From 0fe8333692c4ff9198e3b39af9af0f0eff25c251 Mon Sep 17 00:00:00 2001 From: Jakub Romanczyk Date: Tue, 13 May 2025 15:02:26 +0200 Subject: [PATCH 2/4] fix: shutdown gracefully --- packages/repack/src/commands/common/setupInteractions.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/repack/src/commands/common/setupInteractions.ts b/packages/repack/src/commands/common/setupInteractions.ts index d3511cd4b..3d5f1136f 100644 --- a/packages/repack/src/commands/common/setupInteractions.ts +++ b/packages/repack/src/commands/common/setupInteractions.ts @@ -41,12 +41,17 @@ export function setupInteractions( readline.emitKeypressEvents(process.stdin); process.stdin.setRawMode(true); + // graceful shutdown + process.on('SIGINT', () => { + process.exit(); + }); + process.stdin.on('keypress', (_key, data) => { const { ctrl, name } = data; if (ctrl === true) { switch (name) { case 'c': - process.exit(); + process.emit('SIGINT', 'SIGINT'); break; case 'z': From 6ac0ad9ea2e586fcec242ff8edd242a975d31d88 Mon Sep 17 00:00:00 2001 From: Jakub Romanczyk Date: Tue, 13 May 2025 15:21:59 +0200 Subject: [PATCH 3/4] chore: changeset --- .changeset/shy-worlds-type.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/shy-worlds-type.md diff --git a/.changeset/shy-worlds-type.md b/.changeset/shy-worlds-type.md new file mode 100644 index 000000000..1820db57a --- /dev/null +++ b/.changeset/shy-worlds-type.md @@ -0,0 +1,5 @@ +--- +"@callstack/repack": minor +--- + +Support `RSPACK_PROFILE` env var for obtaining traces from Rspack From 754c8d714d4f67d904c532b3eacd44199791100f Mon Sep 17 00:00:00 2001 From: Jakub Romanczyk Date: Tue, 13 May 2025 19:17:34 +0200 Subject: [PATCH 4/4] fix: tests --- .../src/commands/common/__tests__/setupInteractions.test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/repack/src/commands/common/__tests__/setupInteractions.test.ts b/packages/repack/src/commands/common/__tests__/setupInteractions.test.ts index 6882d60f2..70287e994 100644 --- a/packages/repack/src/commands/common/__tests__/setupInteractions.test.ts +++ b/packages/repack/src/commands/common/__tests__/setupInteractions.test.ts @@ -23,6 +23,11 @@ describe('setupInteractions', () => { } as unknown as Logger; mockProcess = { + on: (event: string) => { + if (event === 'SIGINT') { + mockProcess.exit(); + } + }, stdin: { setRawMode: jest.fn(), on: jest.fn(),