Skip to content

Commit 3b55d49

Browse files
authored
feat: allow to access manifest data in plugin hooks (#5140)
1 parent 002381e commit 3b55d49

File tree

8 files changed

+156
-17
lines changed

8 files changed

+156
-17
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { build, dev } from '@e2e/helper';
2+
import { expect, test } from '@playwright/test';
3+
import type { RsbuildPluginAPI } from '@rsbuild/core';
4+
5+
const fixtures = __dirname;
6+
7+
test('should allow to access manifest data in environment context after prod build', async () => {
8+
let webManifest: Record<string, any> = {};
9+
let nodeManifest: Record<string, any> = {};
10+
11+
await build({
12+
cwd: fixtures,
13+
rsbuildConfig: {
14+
output: {
15+
manifest: true,
16+
filenameHash: false,
17+
},
18+
environments: {
19+
web: {},
20+
node: {
21+
output: {
22+
target: 'node',
23+
},
24+
},
25+
},
26+
plugins: [
27+
{
28+
name: 'test',
29+
setup(api: RsbuildPluginAPI) {
30+
api.onAfterBuild(({ environments }) => {
31+
if (environments.web.manifest) {
32+
webManifest = environments.web.manifest;
33+
}
34+
if (environments.node.manifest) {
35+
nodeManifest = environments.node.manifest;
36+
}
37+
});
38+
},
39+
},
40+
],
41+
},
42+
});
43+
44+
// main.js, index.html
45+
expect(Object.keys(webManifest.allFiles).length).toBe(2);
46+
expect(webManifest.entries.index).toMatchObject({
47+
initial: {
48+
js: ['/static/js/index.js'],
49+
},
50+
html: ['/index.html'],
51+
});
52+
53+
// main.js
54+
expect(Object.keys(nodeManifest.allFiles).length).toBe(1);
55+
expect(nodeManifest.entries.index).toMatchObject({
56+
initial: {
57+
js: ['/index.js'],
58+
},
59+
});
60+
});
61+
62+
test('should allow to access manifest data in environment context after dev build', async ({
63+
page,
64+
}) => {
65+
let webManifest: Record<string, any> = {};
66+
let nodeManifest: Record<string, any> = {};
67+
68+
const rsbuild = await dev({
69+
cwd: fixtures,
70+
page,
71+
rsbuildConfig: {
72+
output: {
73+
manifest: true,
74+
filenameHash: false,
75+
},
76+
environments: {
77+
web: {},
78+
node: {
79+
output: {
80+
target: 'node',
81+
},
82+
},
83+
},
84+
plugins: [
85+
{
86+
name: 'test',
87+
setup(api: RsbuildPluginAPI) {
88+
api.onDevCompileDone(({ environments }) => {
89+
if (environments.web.manifest) {
90+
webManifest = environments.web.manifest;
91+
}
92+
if (environments.node.manifest) {
93+
nodeManifest = environments.node.manifest;
94+
}
95+
});
96+
},
97+
},
98+
],
99+
},
100+
});
101+
102+
// main.js, main.js.map, index.html
103+
expect(Object.keys(webManifest.allFiles).length).toBe(3);
104+
expect(webManifest.entries.index).toMatchObject({
105+
initial: {
106+
js: ['/static/js/index.js'],
107+
},
108+
html: ['/index.html'],
109+
});
110+
111+
// main.js, main.js.map
112+
expect(Object.keys(nodeManifest.allFiles).length).toBe(2);
113+
expect(nodeManifest.entries.index).toMatchObject({
114+
initial: {
115+
js: ['/index.js'],
116+
},
117+
});
118+
119+
await rsbuild.close();
120+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log('hello!');

e2e/cases/output/manifest/index.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ test('should generate manifest file in output', async () => {
3030

3131
const manifest = JSON.parse(manifestContent);
3232

33-
// main.jsindex.html
33+
// main.js, index.html
3434
expect(Object.keys(manifest.allFiles).length).toBe(2);
3535

3636
expect(manifest.entries.index).toMatchObject({
@@ -63,7 +63,7 @@ test('should generate manifest file at specified path', async () => {
6363

6464
const parsed = JSON.parse(manifestContent);
6565

66-
// main.jsindex.html
66+
// main.js, index.html
6767
expect(Object.keys(parsed.allFiles).length).toBe(2);
6868
});
6969

@@ -91,7 +91,7 @@ test('should generate manifest file when target is node', async () => {
9191

9292
const manifest = JSON.parse(manifestContent);
9393

94-
// main.jsindex.html
94+
// main.js, index.html
9595
expect(Object.keys(manifest.allFiles).length).toBe(1);
9696

9797
expect(manifest.entries.index).toMatchObject({

e2e/cases/performance/resource-hints-prefetch/index.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ test('should generate prefetch link when prefetch is defined', async () => {
3434
name.endsWith('.html'),
3535
)!;
3636

37-
// test.jstest.cssimage.png
37+
// test.js, test.css, image.png
3838
expect(content.match(/rel="prefetch"/g)?.length).toBe(3);
3939

4040
expect(
@@ -226,7 +226,7 @@ test('should generate prefetch link by config (distinguish html)', async () => {
226226
name.endsWith('page1.html'),
227227
)!;
228228

229-
// icon.png、test.jstest.cssimage.png
229+
// icon.png、test.js, test.css, image.png
230230
expect(content.match(/rel="prefetch"/g)?.length).toBe(4);
231231

232232
const assetFileName = Object.keys(files).find((file) =>
@@ -245,7 +245,7 @@ test('should generate prefetch link by config (distinguish html)', async () => {
245245
name.endsWith('page2.html'),
246246
)!;
247247

248-
// test.jstest.cssimage.png
248+
// test.js, test.css, image.png
249249
expect(content2.match(/rel="prefetch"/g)?.length).toBe(3);
250250
});
251251

e2e/cases/performance/resource-hints-preload/index.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ test('should generate preload link when preload is defined', async () => {
3131
name.endsWith('.html'),
3232
)!;
3333

34-
// test.jstest.cssimage.png
34+
// test.js, test.css, image.png
3535
expect(content.match(/rel="preload"/g)?.length).toBe(3);
3636

3737
expect(
@@ -115,7 +115,7 @@ test('should generate preload link with crossOrigin', async () => {
115115
name.endsWith('.html'),
116116
)!;
117117

118-
// test.jstest.cssimage.png
118+
// test.js, test.css, image.png
119119
expect(content.match(/rel="preload"/g)?.length).toBe(3);
120120

121121
expect(
@@ -156,7 +156,7 @@ test('should generate preload link without crossOrigin when same origin', async
156156
name.endsWith('.html'),
157157
)!;
158158

159-
// test.jstest.cssimage.png
159+
// test.js, test.css, image.png
160160
expect(content.match(/rel="preload"/g)?.length).toBe(3);
161161

162162
expect(

packages/core/src/createContext.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export async function updateEnvironmentContext(
110110
const entry = config.source.entry ?? {};
111111
const htmlPaths = getEnvironmentHTMLPaths(entry, config);
112112

113-
const environmentContext = {
113+
const environmentContext: EnvironmentContext = {
114114
index,
115115
name,
116116
distPath: getAbsoluteDistPath(context.rootPath, config),
@@ -126,10 +126,14 @@ export async function updateEnvironmentContext(
126126
get(target, prop: keyof EnvironmentContext) {
127127
return target[prop];
128128
},
129-
set(_, prop: keyof EnvironmentContext) {
130-
logger.error(
131-
`EnvironmentContext is readonly, you can not assign to the "environment.${prop}" prop.`,
132-
);
129+
set(target, prop: keyof EnvironmentContext, newValue) {
130+
if (prop === 'manifest') {
131+
target[prop] = newValue;
132+
} else {
133+
logger.error(
134+
`EnvironmentContext is readonly, you can not assign to the "environment.${prop}" prop.`,
135+
);
136+
}
133137
return true;
134138
},
135139
});

packages/core/src/plugins/manifest.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { FileDescriptor } from '../../compiled/rspack-manifest-plugin';
22
import { isObject } from '../helpers';
33
import { recursiveChunkEntryNames } from '../rspack/resource-hints/doesChunkBelongToHtml';
44
import type {
5+
EnvironmentContext,
56
ManifestByEntry,
67
ManifestConfig,
78
ManifestData,
@@ -10,7 +11,11 @@ import type {
1011
} from '../types';
1112

1213
const generateManifest =
13-
(htmlPaths: Record<string, string>, manifestOptions: ManifestObjectConfig) =>
14+
(
15+
htmlPaths: Record<string, string>,
16+
manifestOptions: ManifestObjectConfig,
17+
environment: EnvironmentContext,
18+
) =>
1419
(_seed: Record<string, any>, files: FileDescriptor[]) => {
1520
const chunkEntries = new Map<string, FileDescriptor[]>();
1621

@@ -122,6 +127,7 @@ const generateManifest =
122127
});
123128

124129
if (isObject(generatedManifest)) {
130+
environment.manifest = generatedManifest;
125131
return generatedManifest;
126132
}
127133

@@ -130,6 +136,7 @@ const generateManifest =
130136
);
131137
}
132138

139+
environment.manifest = manifestData;
133140
return manifestData;
134141
};
135142

@@ -187,7 +194,7 @@ export const pluginManifest = (): RsbuildPlugin => ({
187194
fileName: manifestOptions.filename,
188195
filter,
189196
writeToFileEmit: isDev && writeToDisk !== true,
190-
generate: generateManifest(htmlPaths, manifestOptions),
197+
generate: generateManifest(htmlPaths, manifestOptions, environment),
191198
},
192199
]);
193200
});

packages/core/src/types/hooks.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { rspack } from '@rspack/core';
2-
import type { ChainIdentifier } from '..';
2+
import type { ChainIdentifier, ManifestData } from '..';
33
import type RspackChain from '../../compiled/rspack-chain';
44
import type { RsbuildDevServer } from '../server/devServer';
55
import type {
@@ -199,6 +199,13 @@ export type EnvironmentContext = {
199199
* The normalized Rsbuild config for the current environment.
200200
*/
201201
config: NormalizedEnvironmentConfig;
202+
/**
203+
* Manifest data. Only available when:
204+
* - The `output.manifest` config option is enabled
205+
* - The build process has completed, accessible in hooks like `onAfterBuild`,
206+
* `onDevCompileDone` and `onAfterEnvironmentCompile`
207+
*/
208+
manifest?: Record<string, unknown> | ManifestData;
202209
};
203210

204211
export type ModifyChainUtils = {

0 commit comments

Comments
 (0)