Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion e2e/cases/output/manifest-async-chunks/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ test('should generate manifest for async chunks correctly', async () => {
rsbuildConfig: {
output: {
manifest: true,
legalComments: 'none',
filenameHash: false,
},
performance: {
Expand Down
1 change: 0 additions & 1 deletion e2e/cases/output/manifest-generate/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ const rsbuildConfig: RsbuildConfig = {
};
},
},
legalComments: 'none',
sourceMap: false,
filenameHash: false,
},
Expand Down
91 changes: 77 additions & 14 deletions e2e/cases/output/manifest/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { build, dev } from '@e2e/helper';
import { build, dev, rspackOnlyTest } from '@e2e/helper';
import { expect, test } from '@playwright/test';

const fixtures = __dirname;

test('output.manifest', async () => {
test('should generate manifest file in output', async () => {
const rsbuild = await build({
cwd: fixtures,
rsbuildConfig: {
output: {
manifest: true,
legalComments: 'none',
filenameHash: false,
},
performance: {
Expand Down Expand Up @@ -42,13 +41,12 @@ test('output.manifest', async () => {
});
});

test('output.manifest set a path', async () => {
test('should generate manifest file at specified path', async () => {
await build({
cwd: fixtures,
rsbuildConfig: {
output: {
manifest: './custom/my-manifest.json',
legalComments: 'none',
filenameHash: false,
},
performance: {
Expand All @@ -69,7 +67,7 @@ test('output.manifest set a path', async () => {
expect(Object.keys(parsed.allFiles).length).toBe(2);
});

test('output.manifest when target is node', async () => {
test('should generate manifest file when target is node', async () => {
const rsbuild = await build({
cwd: fixtures,
rsbuildConfig: {
Expand All @@ -79,14 +77,8 @@ test('output.manifest when target is node', async () => {
},
target: 'node',
manifest: true,
legalComments: 'none',
filenameHash: false,
},
performance: {
chunkSplit: {
strategy: 'all-in-one',
},
},
},
});

Expand All @@ -109,7 +101,7 @@ test('output.manifest when target is node', async () => {
});
});

test('output.manifest should always write to disk when dev', async ({
test('should always write manifest to disk when in dev mode', async ({
page,
}) => {
const rsbuild = await dev({
Expand All @@ -121,7 +113,6 @@ test('output.manifest should always write to disk when dev', async ({
root: 'dist-dev',
},
manifest: true,
legalComments: 'none',
filenameHash: false,
},
performance: {
Expand All @@ -141,3 +132,75 @@ test('output.manifest should always write to disk when dev', async ({

await rsbuild.close();
});

test('should allow to filter files in manifest', async () => {
const rsbuild = await build({
cwd: fixtures,
rsbuildConfig: {
output: {
manifest: {
filter: (file) => file.name.endsWith('.js'),
},
filenameHash: false,
},
performance: {
chunkSplit: {
strategy: 'all-in-one',
},
},
},
});

const files = await rsbuild.unwrapOutputJSON();

const manifestContent =
files[Object.keys(files).find((file) => file.endsWith('manifest.json'))!];
const manifest = JSON.parse(manifestContent);

// main.js
expect(Object.keys(manifest.allFiles).length).toBe(1);

expect(manifest.entries.index).toMatchObject({
initial: {
js: ['/static/js/index.js'],
},
});
});

rspackOnlyTest(
'should allow to include license files in manifest',
async () => {
const rsbuild = await build({
cwd: fixtures,
rsbuildConfig: {
output: {
manifest: {
filter: () => true,
},
filenameHash: false,
},
performance: {
chunkSplit: {
strategy: 'all-in-one',
},
},
},
});

const files = await rsbuild.unwrapOutputJSON();

const manifestContent =
files[Object.keys(files).find((file) => file.endsWith('manifest.json'))!];
const manifest = JSON.parse(manifestContent);

expect(Object.keys(manifest.allFiles).length).toBe(3);

expect(manifest.entries.index).toMatchObject({
initial: {
js: ['/static/js/index.js'],
},
html: ['/index.html'],
assets: ['/static/js/index.js.LICENSE.txt'],
});
},
);
4 changes: 3 additions & 1 deletion e2e/cases/output/manifest/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
console.log('hello!');
import React from 'react';

console.log('hello!', React);
8 changes: 7 additions & 1 deletion packages/core/src/plugins/manifest.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { FileDescriptor } from 'rspack-manifest-plugin';
import type { FileDescriptor } from '../../compiled/rspack-manifest-plugin';
import { isObject } from '../helpers';
import { recursiveChunkEntryNames } from '../rspack/preload/helpers';
import type {
Expand Down Expand Up @@ -175,9 +175,15 @@ export const pluginManifest = (): RsbuildPlugin => ({
);
const { htmlPaths } = environment;

// Exclude `*.LICENSE.txt` files by default
const filter =
manifestOptions.filter ??
((file: FileDescriptor) => !file.name.endsWith('.LICENSE.txt'));

chain.plugin(CHAIN_ID.PLUGIN.MANIFEST).use(RspackManifestPlugin, [
{
fileName: manifestOptions.filename,
filter,
writeToFileEmit: isDev && writeToDisk !== true,
generate: generateManifest(htmlPaths, manifestOptions),
},
Expand Down
9 changes: 8 additions & 1 deletion packages/core/src/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type {
Filter as ProxyFilter,
} from '../../compiled/http-proxy-middleware/index.js';
import type RspackChain from '../../compiled/rspack-chain/index.js';
import type { FileDescriptor } from '../../compiled/rspack-manifest-plugin';
import type { BundleAnalyzerPlugin } from '../../compiled/webpack-bundle-analyzer/index.js';
import type {
ModifyBundlerChainUtils,
Expand Down Expand Up @@ -926,9 +927,15 @@ export type ManifestObjectConfig = {
* A custom function to generate the content of the manifest file.
*/
generate?: (params: {
files: import('rspack-manifest-plugin').FileDescriptor[];
files: FileDescriptor[];
manifestData: ManifestData;
}) => Record<string, unknown>;
/**
* Allows you to filter the files included in the manifest.
* The function receives a `file` parameter and returns `true` to keep the file, or `false` to exclude it.
* @default (file: FileDescriptor) => !file.name.endsWith('.LICENSE.txt')
*/
filter?: (file: FileDescriptor) => boolean;
};

export type ManifestConfig = string | boolean | ManifestObjectConfig;
Expand Down
54 changes: 54 additions & 0 deletions website/docs/en/config/output/manifest.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,57 @@ const files = [
},
];
```

### filter

- **Type:**

```ts
type ManifestFilter = (file: FileDescriptor) => boolean;
```

- **Default:** `file => !file.name.endsWith('.LICENSE.txt')`
- **Version:** `>= 1.2.0`

Allows you to filter the files included in the manifest. The function receives a `file` parameter and returns `true` to keep the file, or `false` to exclude it.

By default, `*.LICENSE.txt` files are excluded from the manifest, as these license files are only used to declare open source licenses and are not used at runtime.

For example, to only keep `*.js` files:

```ts title="rsbuild.config.ts"
export default {
output: {
manifest: {
filter: (file) => file.name.endsWith('.js'),
},
},
};
```

The generated manifest file will only include `*.js` files:

```json title="dist/manifest.json"
{
"allFiles": ["/static/js/index.[hash].js"],
"entries": {
"index": {
"initial": {
"js": ["/static/js/index.[hash].js"]
}
}
}
}
```

Or include all files:

```ts title="rsbuild.config.ts"
export default {
output: {
manifest: {
filter: () => true,
},
},
};
```
54 changes: 54 additions & 0 deletions website/docs/zh/config/output/manifest.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,57 @@ const files = [
},
];
```

### filter

- **类型:**

```ts
type ManifestFilter = (file: FileDescriptor) => boolean;
```

- **默认值:** `file => !file.name.endsWith('.LICENSE.txt')`
- **版本:** `>= 1.2.0`

允许你过滤包含在 manifest 中的文件。该函数接收一个 `file` 参数,返回 `true` 表示保留该文件,返回 `false` 表示不保留该文件。

默认情况下,`*.LICENSE.txt` 文件不会被包含在 manifest 文件中,因为这些许可证文件仅用于声明开源协议,不会在运行时被使用。

例如,仅保留 `*.js` 文件:

```ts title="rsbuild.config.ts"
export default {
output: {
manifest: {
filter: (file) => file.name.endsWith('.js'),
},
},
};
```

生成的 manifest 文件中仅会包含 `*.js` 文件:

```json title="dist/manifest.json"
{
"allFiles": ["/static/js/index.[hash].js"],
"entries": {
"index": {
"initial": {
"js": ["/static/js/index.[hash].js"]
}
}
}
}
```

或者是包含所有文件:

```ts title="rsbuild.config.ts"
export default {
output: {
manifest: {
filter: () => true,
},
},
};
```
Loading