Skip to content

Commit 94a9e64

Browse files
committed
refactor(CodeSigningPlugin): extract signAsset method for improved readability
1 parent dc3d3fe commit 94a9e64

File tree

2 files changed

+68
-27
lines changed

2 files changed

+68
-27
lines changed

packages/repack/src/plugins/CodeSigningPlugin/CodeSigningPlugin.ts

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,29 @@ export class CodeSigningPlugin {
3030
});
3131
}
3232

33+
private signAsset(
34+
asset: { source: { source(): string | Buffer } },
35+
privateKey: Buffer,
36+
beginMark: string,
37+
tokenBufferSize: number
38+
): Buffer {
39+
const source = asset.source.source();
40+
const content = Buffer.isBuffer(source) ? source : Buffer.from(source);
41+
42+
const hash = crypto
43+
.createHash('sha256')
44+
.update(content)
45+
.digest('hex');
46+
const token = jwt.sign({ hash }, privateKey, {
47+
algorithm: 'RS256',
48+
});
49+
50+
return Buffer.concat(
51+
[content, Buffer.from(beginMark), Buffer.from(token)],
52+
content.length + tokenBufferSize
53+
);
54+
}
55+
3356
apply(compiler: RspackCompiler): void;
3457
apply(compiler: WebpackCompiler): void;
3558

@@ -70,17 +93,14 @@ export class CodeSigningPlugin {
7093
compiler.hooks.thisCompilation.tap(
7194
'RepackCodeSigningPlugin',
7295
(compilation) => {
73-
// @ts-ignore — sources is available on both rspack and webpack compilers
7496
const { sources } = compiler.webpack;
7597
const mainBundleName = compilation.outputOptions.filename as string;
7698

7799
compilation.hooks.processAssets.tap(
78100
{
79101
name: 'RepackCodeSigningPlugin',
80-
// Sign at ANALYSE (2000) so assets are signed before any plugin
81-
// running at REPORT (5000) — e.g. withZephyr() — captures them.
82-
// The original assetEmitted hook fires after processAssets completes,
83-
// which is too late when Zephyr uploads assets at REPORT stage.
102+
// Sign at ANALYSE (2000) so later processAssets consumers,
103+
// such as Zephyr at REPORT (5000), receive already-signed assets
84104
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ANALYSE,
85105
},
86106
() => {
@@ -95,25 +115,12 @@ export class CodeSigningPlugin {
95115
const asset = compilation.getAsset(file);
96116
if (!asset) continue;
97117

98-
const source = asset.source.source();
99-
const content = Buffer.isBuffer(source)
100-
? source
101-
: Buffer.from(source);
102-
103118
logger.debug(`Signing ${file}`);
104-
/** generate bundle hash */
105-
const hash = crypto
106-
.createHash('sha256')
107-
.update(content)
108-
.digest('hex');
109-
/** generate token */
110-
const token = jwt.sign({ hash }, privateKey, {
111-
algorithm: 'RS256',
112-
});
113-
/** combine the bundle and the token */
114-
const signedBundle = Buffer.concat(
115-
[content, Buffer.from(BEGIN_CS_MARK), Buffer.from(token)],
116-
content.length + TOKEN_BUFFER_SIZE
119+
const signedBundle = this.signAsset(
120+
asset,
121+
privateKey,
122+
BEGIN_CS_MARK,
123+
TOKEN_BUFFER_SIZE
117124
);
118125

119126
compilation.updateAsset(

packages/repack/src/plugins/__tests__/CodeSigningPlugin.test.ts

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,17 +84,42 @@ describe('CodeSigningPlugin', () => {
8484
});
8585

8686
it('exposes signed chunk assets to processAssets REPORT (after ANALYSE signing)', async () => {
87+
const seenBeforeSigning: Record<string, string> = {};
8788
const seenAtReportStage: Record<string, string> = {};
8889

8990
const captureAtReportStage = {
9091
apply(compiler: Compiler) {
9192
compiler.hooks.thisCompilation.tap(
9293
'TestReportStageCapture',
9394
(compilation) => {
95+
const { PROCESS_ASSETS_STAGE_ANALYSE, PROCESS_ASSETS_STAGE_REPORT } =
96+
compiler.webpack.Compilation;
97+
98+
/** Immediately before CodeSigningPlugin (ANALYSE / 2000) so content is still unsigned. */
99+
const beforeSigningStage = PROCESS_ASSETS_STAGE_ANALYSE - 1;
100+
101+
compilation.hooks.processAssets.tap(
102+
{
103+
name: 'TestPreAnalyseCapture',
104+
stage: beforeSigningStage,
105+
},
106+
() => {
107+
for (const chunk of compilation.chunks) {
108+
for (const file of chunk.files) {
109+
const asset = compilation.getAsset(file);
110+
if (!asset) continue;
111+
const raw = asset.source.source();
112+
const buf = Buffer.isBuffer(raw) ? raw : Buffer.from(raw);
113+
seenBeforeSigning[file] = buf.toString();
114+
}
115+
}
116+
}
117+
);
118+
94119
compilation.hooks.processAssets.tap(
95120
{
96121
name: 'TestReportStageCapture',
97-
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_REPORT,
122+
stage: PROCESS_ASSETS_STAGE_REPORT,
98123
},
99124
() => {
100125
for (const chunk of compilation.chunks) {
@@ -128,9 +153,18 @@ describe('CodeSigningPlugin', () => {
128153
[captureAtReportStage]
129154
);
130155

131-
expect(
132-
seenAtReportStage['myChunk.chunk.bundle']?.match(BUNDLE_WITH_JWT_REGEX)
133-
).toBeTruthy();
156+
const chunkFile = 'myChunk.chunk.bundle';
157+
const before = seenBeforeSigning[chunkFile];
158+
const atReport = seenAtReportStage[chunkFile];
159+
160+
expect(before).toBeDefined();
161+
expect(atReport).toBeDefined();
162+
/** Regression guard: signing at ANALYSE must mutate assets before REPORT (not only on emit). */
163+
expect(before.includes('/* RCSSB */')).toBe(false);
164+
expect(atReport.includes('/* RCSSB */')).toBe(true);
165+
expect(atReport.length).toBeGreaterThan(before.length);
166+
167+
expect(atReport.match(BUNDLE_WITH_JWT_REGEX)).toBeTruthy();
134168
expect(
135169
seenAtReportStage['index.bundle']?.match(BUNDLE_WITH_JWT_REGEX)
136170
).toBeNull();

0 commit comments

Comments
 (0)