Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
11 changes: 10 additions & 1 deletion packages/core/src/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,16 @@ export type Dts =
* @defaultValue `false`
* @see {@link https://rslib.rs/config/lib/dts#dtsbundle}
*/
bundle?: boolean;
bundle?:
| boolean
| {
/**
* Specifies the dependencies whose declaration files should be bundled.
* @defaultValue {@link https://rslib.rs/config/lib/dts#dtsbundlebundledpackages}
* @see {@link https://rslib.rs/config/lib/dts#dtsbundlebundledpackages}
*/
bundledPackages?: string[];
};
/**
* The output directory of declaration files.
* @defaultValue {@link https://rslib.rs/config/lib/dts#default-value}
Expand Down
27 changes: 27 additions & 0 deletions packages/plugin-dts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,33 @@ pluginDts({
});
```

#### bundle.bundledPackages

- **Type:** `string[]`

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`.

By default, `rsbuild-plugin-dts` determines externalized dependencies based on the following configurations.

- [autoExternal](#autoexternal) configuration
- [output.externals](https://rsbuild.rs/config/output/externals) configuration

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.

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.

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:

```js
pluginDts({
bundle: {
bundledPackages: ['foo', 'bar'],
},
});
```

> `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`.

### distPath

- **Type:** `string`
Expand Down
13 changes: 10 additions & 3 deletions packages/plugin-dts/src/dts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,16 @@ const isObject = (obj: unknown): obj is Record<string, any> =>

// use !externals
export const calcBundledPackages = (options: {
autoExternal: DtsGenOptions['autoExternal'];
cwd: string;
autoExternal: DtsGenOptions['autoExternal'];
userExternals?: DtsGenOptions['userExternals'];
overrideBundledPackages?: string[];
}): string[] => {
const { autoExternal, cwd, userExternals } = options;
const { cwd, autoExternal, userExternals, overrideBundledPackages } = options;

if (overrideBundledPackages) {
return overrideBundledPackages;
}

let pkgJson: {
dependencies?: Record<string, string>;
Expand Down Expand Up @@ -119,6 +124,7 @@ export async function generateDts(data: DtsGenOptions): Promise<void> {
dtsExtension = '.d.ts',
autoExternal = true,
userExternals,
apiExtractorOptions,
banner,
footer,
redirect = {
Expand Down Expand Up @@ -213,9 +219,10 @@ export async function generateDts(data: DtsGenOptions): Promise<void> {
banner,
footer,
bundledPackages: calcBundledPackages({
autoExternal,
cwd,
autoExternal,
userExternals,
overrideBundledPackages: apiExtractorOptions?.bundledPackages,
}),
});
}
Expand Down
29 changes: 21 additions & 8 deletions packages/plugin-dts/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@ export type DtsRedirect = {
extension?: boolean;
};

export type ApiExtractorOptions = {
bundledPackages?: string[];
};

export type PluginDtsOptions = {
bundle?: boolean;
bundle?: boolean | ApiExtractorOptions;
distPath?: string;
build?: boolean;
abortOnError?: boolean;
Expand All @@ -46,7 +50,8 @@ export type DtsEntry = {
path?: string;
};

export type DtsGenOptions = PluginDtsOptions & {
export type DtsGenOptions = Omit<PluginDtsOptions, 'bundle'> & {
bundle: boolean;
name: string;
cwd: string;
isWatch: boolean;
Expand All @@ -56,6 +61,7 @@ export type DtsGenOptions = PluginDtsOptions & {
tsconfigPath: string;
tsConfigResult: ts.ParsedCommandLine;
userExternals?: NonNullable<RsbuildConfig['output']>['externals'];
apiExtractorOptions?: ApiExtractorOptions;
};

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

setup(api) {
options.bundle = options.bundle ?? false;
let apiExtractorOptions = {};

if (options.bundle && typeof options.bundle === 'object') {
apiExtractorOptions = {
...options.bundle,
};
}

const bundle = !!options.bundle;
options.abortOnError = options.abortOnError ?? true;
options.build = options.build ?? false;
options.redirect = options.redirect ?? {};
Expand All @@ -93,10 +107,7 @@ export const pluginDts = (options: PluginDtsOptions = {}): RsbuildPlugin => ({
// @microsoft/api-extractor only support single entry to bundle declaration files
// see https://github.com/microsoft/rushstack/issues/1596#issuecomment-546790721
// we support multiple entries by calling api-extractor multiple times
const dtsEntry = processSourceEntry(
options.bundle!,
config.source?.entry,
);
const dtsEntry = processSourceEntry(bundle, config.source?.entry);

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

// clean .rslib temp folder
if (options.bundle) {
if (bundle) {
await clearTempDeclarationDir(cwd);
}

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

const dtsGenOptions: DtsGenOptions = {
...options,
bundle,
dtsEntry,
dtsEmitPath,
userExternals: config.output.externals,
apiExtractorOptions,
tsconfigPath,
tsConfigResult,
name: environment.name,
Expand Down
14 changes: 14 additions & 0 deletions packages/plugin-dts/tests/external.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,18 @@ describe('should calcBundledPackages correctly', () => {

expect(result).toEqual([]);
});

it('overrides with bundledPackages', () => {
vi.spyOn(fs, 'readFileSync').mockImplementation(() =>
JSON.stringify(commonPkgJson),
);

const result = calcBundledPackages({
autoExternal: true,
cwd: 'pkg/to/root',
overrideBundledPackages: ['foo', '@bar/*'],
});

expect(result).toEqual(['foo', '@bar/*']);
});
});
6 changes: 6 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions tests/integration/dts/bundle/bundled-packages/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "dts-bundle-bundled-packages-test",
"version": "1.0.0",
"private": true,
"type": "module",
"devDependencies": {
"@vitest/expect": "3.1.4"
}
}
41 changes: 41 additions & 0 deletions tests/integration/dts/bundle/bundled-packages/rslib.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { defineConfig } from '@rslib/core';
import { generateBundleEsmConfig } from 'test-helper';

export default defineConfig({
lib: [
generateBundleEsmConfig({
dts: {
bundle: true,
},
output: {
distPath: {
root: './dist/esm/default',
},
},
}),
generateBundleEsmConfig({
dts: {
bundle: {
bundledPackages: [],
},
},
output: {
distPath: {
root: './dist/esm/override-empty-array',
},
},
}),
generateBundleEsmConfig({
dts: {
bundle: {
bundledPackages: ['@vitest/expect', '@vitest/utils'],
},
},
output: {
distPath: {
root: './dist/esm/override-array-string',
},
},
}),
],
});
1 change: 1 addition & 0 deletions tests/integration/dts/bundle/bundled-packages/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from '@vitest/expect';
7 changes: 7 additions & 0 deletions tests/integration/dts/bundle/bundled-packages/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "@rslib/tsconfig/base",
"compilerOptions": {
"baseUrl": "./"
},
"include": ["src"]
}
27 changes: 27 additions & 0 deletions tests/integration/dts/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,33 @@ describe('dts when bundle: true', () => {

restore();
});

test('override with bundledPackages', async () => {
const fixturePath = join(__dirname, 'bundle', 'bundled-packages');
const { entries } = await buildAndGetResults({
fixturePath,
type: 'dts',
});

// default
expect(entries.esm0).toContain(
`import { Constructable } from '@vitest/utils';`,
);

// override empty array
expect(entries.esm1).toMatchInlineSnapshot(`
"
export * from "@vitest/expect";
export { }
"
`);

// override with bundledPackages
expect(entries.esm2).not.toContain(
`import { Constructable } from '@vitest/utils';`,
);
});
});

describe('dts when build: true', () => {
Expand Down
41 changes: 36 additions & 5 deletions website/docs/en/config/lib/dts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,11 @@ If you want to customize the declaration files generation, you can set the `dts`

### dts.bundle

- **Type:** `boolean`
- **Type:** `boolean | { bundledPackages?: string[] }`
- **Default:** `false`

Whether to bundle the declaration files.

#### Example

If you want to [bundle declaration files](/guide/advanced/dts#bundle-declaration-files) files, you should:

1. Install [@microsoft/api-extractor](https://www.npmjs.com/package/@microsoft/api-extractor) as a development dependency, which is the underlying tool used for bundling declaration files.
Expand All @@ -87,9 +85,42 @@ export default {
};
```

#### Handle third-party packages
#### dts.bundle.bundledPackages

- **Type:** `string[]`

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`.

By default, Rslib determines externalized dependencies based on the following configurations. For details, refer to [Handle third-party dependencies](/guide/advanced/third-party-deps).

- [autoExternal](/config/lib/auto-external) configuration
- [output.externals](/config/rsbuild/output#outputexternals) configuration

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.

When the default behavior does not meet the requirements, you can explicitly specify the dependencies whose declaration files need to be bundled through `dts.bundle.bundledPackages`. After setting this configuration, the above default behavior will be completely overwritten.

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:

```ts title="rslib.config.ts"
export default {
lib: [
{
format: 'esm',
dts: {
// [!code highlight:3]
bundle: {
bundledPackages: ['foo', 'bar'],
},
},
},
],
};
```

When we bundle declaration files, we should specify which third-party package types need to be bundled, refer to the [Handle Third-Party Dependencies](/guide/advanced/third-party-deps) documentation for more details about externals related configurations.
::: note
`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`.
:::

### dts.distPath

Expand Down
Loading
Loading