Skip to content

Commit e7a95b8

Browse files
authored
feat(dts): support override bundledPackages (#1025)
1 parent 64ba3b3 commit e7a95b8

File tree

13 files changed

+247
-24
lines changed

13 files changed

+247
-24
lines changed

packages/core/src/types/config.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,16 @@ export type Dts =
5454
* @defaultValue `false`
5555
* @see {@link https://rslib.rs/config/lib/dts#dtsbundle}
5656
*/
57-
bundle?: boolean;
57+
bundle?:
58+
| boolean
59+
| {
60+
/**
61+
* Specifies the dependencies whose declaration files should be bundled.
62+
* @defaultValue {@link https://rslib.rs/config/lib/dts#dtsbundlebundledpackages}
63+
* @see {@link https://rslib.rs/config/lib/dts#dtsbundlebundledpackages}
64+
*/
65+
bundledPackages?: string[];
66+
};
5867
/**
5968
* The output directory of declaration files.
6069
* @defaultValue {@link https://rslib.rs/config/lib/dts#default-value}

packages/plugin-dts/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,33 @@ pluginDts({
5454
});
5555
```
5656

57+
#### bundle.bundledPackages
58+
59+
- **Type:** `string[]`
60+
61+
Specifies the dependencies whose declaration files should be bundled. This configuration is passed to the [bundledPackages](https://api-extractor.com/pages/configs/api-extractor_json/#bundledpackages) option of `@microsoft/api-extractor`.
62+
63+
By default, `rsbuild-plugin-dts` determines externalized dependencies based on the following configurations.
64+
65+
- [autoExternal](#autoexternal) configuration
66+
- [output.externals](https://rsbuild.rs/config/output/externals) configuration
67+
68+
Direct dependencies (declared in `package.json`) that are not externalized will be automatically added to `bundledPackages`, and their declaration files will be bundled into the final output.
69+
70+
When the default behavior does not meet the requirements, you can explicitly specify the dependencies whose declaration files need to be bundled through `bundle.bundledPackages`. After setting this configuration, the above default behavior will be completely overwritten.
71+
72+
This is typically used for bundling transitive dependencies (dependencies of direct dependencies). For example, if the project directly depends on `foo`, and `foo` depends on `bar`, you can bundle both `foo` and `bar`'s declaration files as follows:
73+
74+
```js
75+
pluginDts({
76+
bundle: {
77+
bundledPackages: ['foo', 'bar'],
78+
},
79+
});
80+
```
81+
82+
> `bundledPackages` can be specified with the [minimatch](https://www.npmjs.com/package/minimatch) syntax, but will only match the declared direct dependencies in `package.json`.
83+
5784
### distPath
5885

5986
- **Type:** `string`

packages/plugin-dts/src/dts.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,16 @@ const isObject = (obj: unknown): obj is Record<string, any> =>
1919

2020
// use !externals
2121
export const calcBundledPackages = (options: {
22-
autoExternal: DtsGenOptions['autoExternal'];
2322
cwd: string;
23+
autoExternal: DtsGenOptions['autoExternal'];
2424
userExternals?: DtsGenOptions['userExternals'];
25+
overrideBundledPackages?: string[];
2526
}): string[] => {
26-
const { autoExternal, cwd, userExternals } = options;
27+
const { cwd, autoExternal, userExternals, overrideBundledPackages } = options;
28+
29+
if (overrideBundledPackages) {
30+
return overrideBundledPackages;
31+
}
2732

2833
let pkgJson: {
2934
dependencies?: Record<string, string>;
@@ -119,6 +124,7 @@ export async function generateDts(data: DtsGenOptions): Promise<void> {
119124
dtsExtension = '.d.ts',
120125
autoExternal = true,
121126
userExternals,
127+
apiExtractorOptions,
122128
banner,
123129
footer,
124130
redirect = {
@@ -213,9 +219,10 @@ export async function generateDts(data: DtsGenOptions): Promise<void> {
213219
banner,
214220
footer,
215221
bundledPackages: calcBundledPackages({
216-
autoExternal,
217222
cwd,
223+
autoExternal,
218224
userExternals,
225+
overrideBundledPackages: apiExtractorOptions?.bundledPackages,
219226
}),
220227
});
221228
}

packages/plugin-dts/src/index.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@ export type DtsRedirect = {
2222
extension?: boolean;
2323
};
2424

25+
export type ApiExtractorOptions = {
26+
bundledPackages?: string[];
27+
};
28+
2529
export type PluginDtsOptions = {
26-
bundle?: boolean;
30+
bundle?: boolean | ApiExtractorOptions;
2731
distPath?: string;
2832
build?: boolean;
2933
abortOnError?: boolean;
@@ -46,7 +50,8 @@ export type DtsEntry = {
4650
path?: string;
4751
};
4852

49-
export type DtsGenOptions = PluginDtsOptions & {
53+
export type DtsGenOptions = Omit<PluginDtsOptions, 'bundle'> & {
54+
bundle: boolean;
5055
name: string;
5156
cwd: string;
5257
isWatch: boolean;
@@ -56,6 +61,7 @@ export type DtsGenOptions = PluginDtsOptions & {
5661
tsconfigPath: string;
5762
tsConfigResult: ts.ParsedCommandLine;
5863
userExternals?: NonNullable<RsbuildConfig['output']>['externals'];
64+
apiExtractorOptions?: ApiExtractorOptions;
5965
};
6066

6167
interface TaskResult {
@@ -71,7 +77,15 @@ export const pluginDts = (options: PluginDtsOptions = {}): RsbuildPlugin => ({
7177
name: PLUGIN_DTS_NAME,
7278

7379
setup(api) {
74-
options.bundle = options.bundle ?? false;
80+
let apiExtractorOptions = {};
81+
82+
if (options.bundle && typeof options.bundle === 'object') {
83+
apiExtractorOptions = {
84+
...options.bundle,
85+
};
86+
}
87+
88+
const bundle = !!options.bundle;
7589
options.abortOnError = options.abortOnError ?? true;
7690
options.build = options.build ?? false;
7791
options.redirect = options.redirect ?? {};
@@ -93,10 +107,7 @@ export const pluginDts = (options: PluginDtsOptions = {}): RsbuildPlugin => ({
93107
// @microsoft/api-extractor only support single entry to bundle declaration files
94108
// see https://github.com/microsoft/rushstack/issues/1596#issuecomment-546790721
95109
// we support multiple entries by calling api-extractor multiple times
96-
const dtsEntry = processSourceEntry(
97-
options.bundle!,
98-
config.source?.entry,
99-
);
110+
const dtsEntry = processSourceEntry(bundle, config.source?.entry);
100111

101112
const cwd = api.context.rootPath;
102113
const tsconfigPath = ts.findConfigFile(
@@ -132,7 +143,7 @@ export const pluginDts = (options: PluginDtsOptions = {}): RsbuildPlugin => ({
132143
}
133144

134145
// clean .rslib temp folder
135-
if (options.bundle) {
146+
if (bundle) {
136147
await clearTempDeclarationDir(cwd);
137148
}
138149

@@ -150,9 +161,11 @@ export const pluginDts = (options: PluginDtsOptions = {}): RsbuildPlugin => ({
150161

151162
const dtsGenOptions: DtsGenOptions = {
152163
...options,
164+
bundle,
153165
dtsEntry,
154166
dtsEmitPath,
155167
userExternals: config.output.externals,
168+
apiExtractorOptions,
156169
tsconfigPath,
157170
tsConfigResult,
158171
name: environment.name,

packages/plugin-dts/tests/external.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,18 @@ describe('should calcBundledPackages correctly', () => {
105105

106106
expect(result).toEqual([]);
107107
});
108+
109+
it('overrides with bundledPackages', () => {
110+
vi.spyOn(fs, 'readFileSync').mockImplementation(() =>
111+
JSON.stringify(commonPkgJson),
112+
);
113+
114+
const result = calcBundledPackages({
115+
autoExternal: true,
116+
cwd: 'pkg/to/root',
117+
overrideBundledPackages: ['foo', '@bar/*'],
118+
});
119+
120+
expect(result).toEqual(['foo', '@bar/*']);
121+
});
108122
});

pnpm-lock.yaml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "dts-bundle-bundled-packages-test",
3+
"version": "1.0.0",
4+
"private": true,
5+
"type": "module",
6+
"devDependencies": {
7+
"@vitest/expect": "3.1.4"
8+
}
9+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { defineConfig } from '@rslib/core';
2+
import { generateBundleEsmConfig } from 'test-helper';
3+
4+
export default defineConfig({
5+
lib: [
6+
generateBundleEsmConfig({
7+
dts: {
8+
bundle: true,
9+
},
10+
output: {
11+
distPath: {
12+
root: './dist/esm/default',
13+
},
14+
},
15+
}),
16+
generateBundleEsmConfig({
17+
dts: {
18+
bundle: {
19+
bundledPackages: [],
20+
},
21+
},
22+
output: {
23+
distPath: {
24+
root: './dist/esm/override-empty-array',
25+
},
26+
},
27+
}),
28+
generateBundleEsmConfig({
29+
dts: {
30+
bundle: {
31+
bundledPackages: ['@vitest/expect', '@vitest/utils'],
32+
},
33+
},
34+
output: {
35+
distPath: {
36+
root: './dist/esm/override-array-string',
37+
},
38+
},
39+
}),
40+
],
41+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from '@vitest/expect';
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"extends": "@rslib/tsconfig/base",
3+
"compilerOptions": {
4+
"baseUrl": "./"
5+
},
6+
"include": ["src"]
7+
}

0 commit comments

Comments
 (0)