Skip to content

feat(dts): support dts.alias #1135

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 30, 2025
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: 1 addition & 0 deletions packages/core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1416,6 +1416,7 @@ const composeDtsConfig = async (
abortOnError: dts?.abortOnError,
dtsExtension: dts?.autoExtension ? dtsExtension : '.d.ts',
autoExternal: getAutoExternalDefaultValue(format, autoExternal),
alias: dts?.alias,
banner: banner?.dts,
footer: footer?.dts,
redirect: redirect?.dts,
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ export type Dts =
* @see {@link https://rslib.rs/config/lib/dts#dtsautoextension}
*/
autoExtension?: boolean;
/**
* Set the alias for declaration files, similar to the `compilerOptions.paths` option in `tsconfig.json`.
* @defaultValue `{}`
* @see {@link https://rslib.rs/config/lib/dts#dtsalias}
*/
alias?: Record<string, string>;
}
| boolean;

Expand Down
19 changes: 19 additions & 0 deletions packages/plugin-dts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,25 @@ pluginDts({
});
```

### alias

- **Type:** `Record<string, string>`
- **Default:** `{}`

Configure the path alias for declaration files.

`alias` will be merged with `compilerOptions.paths` configured in `tsconfig.json` and `alias` has a higher priority.

In most cases, you don't need to use `alias`, but consider using it when you need to use path alias only in declaration files without wanting to affect JavaScript outputs. For example, map the declaration file of `foo` to `./compiled/foo`.

```js
pluginDts({
alias: {
foo: './compiled/foo',
},
});
```

### autoExternal

- **Type:** `boolean`
Expand Down
17 changes: 16 additions & 1 deletion packages/plugin-dts/src/dts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import { logger } from '@rsbuild/core';
import color from 'picocolors';
import type { DtsEntry, DtsGenOptions } from './index';
import { emitDts } from './tsc';
import { calcLongestCommonPath, ensureTempDeclarationDir } from './utils';
import {
calcLongestCommonPath,
ensureTempDeclarationDir,
mergeAliasWithTsConfigPaths,
} from './utils';

const isObject = (obj: unknown): obj is Record<string, any> =>
Object.prototype.toString.call(obj) === '[object Object]';
Expand Down Expand Up @@ -123,6 +127,7 @@ export async function generateDts(data: DtsGenOptions): Promise<void> {
isWatch,
dtsExtension = '.d.ts',
autoExternal = true,
alias = {},
userExternals,
apiExtractorOptions,
banner,
Expand All @@ -136,6 +141,15 @@ export async function generateDts(data: DtsGenOptions): Promise<void> {
logger.start(`generating declaration files... ${color.gray(`(${name})`)}`);
}

// merge alias and tsconfig paths
const paths = mergeAliasWithTsConfigPaths(
tsConfigResult.options.paths,
alias,
);
if (Object.keys(paths).length > 0) {
tsConfigResult.options.paths = paths;
}

const { options: rawCompilerOptions, fileNames } = tsConfigResult;

// The longest common path of all non-declaration input files.
Expand Down Expand Up @@ -240,6 +254,7 @@ export async function generateDts(data: DtsGenOptions): Promise<void> {
dtsExtension,
redirect,
rootDir,
paths,
banner,
footer,
},
Expand Down
2 changes: 2 additions & 0 deletions packages/plugin-dts/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export type PluginDtsOptions = {
build?: boolean;
abortOnError?: boolean;
dtsExtension?: string;
alias?: Record<string, string>;
autoExternal?:
| boolean
| {
Expand Down Expand Up @@ -91,6 +92,7 @@ export const pluginDts = (options: PluginDtsOptions = {}): RsbuildPlugin => ({
options.redirect = options.redirect ?? {};
options.redirect.path = options.redirect.path ?? true;
options.redirect.extension = options.redirect.extension ?? false;
options.alias = options.alias ?? {};

const dtsPromises: Promise<TaskResult>[] = [];
let promisesResult: TaskResult[] = [];
Expand Down
9 changes: 9 additions & 0 deletions packages/plugin-dts/src/tsc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export type EmitDtsOptions = {
dtsExtension: string;
rootDir: string;
redirect: DtsRedirect;
paths: Record<string, string[]>;
banner?: string;
footer?: string;
};
Expand All @@ -33,6 +34,7 @@ async function handleDiagnosticsAndProcessFiles(
dtsExtension: string,
redirect: DtsRedirect,
rootDir: string,
paths: Record<string, string[]>,
banner?: string,
footer?: string,
name?: string,
Expand All @@ -54,6 +56,7 @@ async function handleDiagnosticsAndProcessFiles(
redirect,
configPath,
rootDir,
paths,
banner,
footer,
);
Expand Down Expand Up @@ -89,6 +92,7 @@ export async function emitDts(
rootDir,
banner,
footer,
paths,
redirect,
} = options;
const {
Expand Down Expand Up @@ -147,6 +151,7 @@ export async function emitDts(
redirect,
configPath,
rootDir,
paths,
banner,
footer,
);
Expand All @@ -162,6 +167,7 @@ export async function emitDts(
redirect,
configPath,
rootDir,
paths,
banner,
footer,
);
Expand Down Expand Up @@ -230,6 +236,7 @@ export async function emitDts(
dtsExtension,
redirect,
rootDir,
paths,
banner,
footer,
name,
Expand Down Expand Up @@ -291,6 +298,7 @@ export async function emitDts(
dtsExtension,
redirect,
rootDir,
paths,
banner,
footer,
name,
Expand Down Expand Up @@ -326,6 +334,7 @@ export async function emitDts(
redirect,
configPath,
rootDir,
paths,
banner,
footer,
);
Expand Down
32 changes: 30 additions & 2 deletions packages/plugin-dts/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,33 @@ export function loadTsconfig(tsconfigPath: string): ts.ParsedCommandLine {
return configFileContent;
}

export function mergeAliasWithTsConfigPaths(
paths: Record<string, string[]> | undefined,
alias: Record<string, string> = {},
): Record<string, string[]> {
const mergedPaths: Record<string, string[]> = {};

if (alias && typeof alias === 'object' && Object.keys(alias).length > 0) {
for (const [key, value] of Object.entries(alias)) {
if (typeof value === 'string' && value.trim()) {
mergedPaths[key] = [value];
}
}
}

if (paths) {
for (const [key, value] of Object.entries(paths)) {
if (Array.isArray(value) && value.length > 0) {
if (!mergedPaths[key]) {
mergedPaths[key] = [...value];
}
}
}
}

return Object.keys(mergedPaths).length > 0 ? mergedPaths : {};
}

export const TEMP_FOLDER = '.rslib';
export const TEMP_DTS_DIR: string = `${TEMP_FOLDER}/declarations`;

Expand Down Expand Up @@ -395,6 +422,7 @@ export async function processDtsFiles(
redirect: DtsRedirect,
tsconfigPath: string,
rootDir: string,
paths: Record<string, string[]>,
banner?: string,
footer?: string,
): Promise<void> {
Expand All @@ -412,11 +440,11 @@ export async function processDtsFiles(
return;
}

const { absoluteBaseUrl, paths, addMatchAll } = result;
const { absoluteBaseUrl, addMatchAll } = result;
const mainFields: string[] = [];
/**
* resolve paths priorities:
* see https://github.com/jonaskello/tsconfig-paths/blob/098e066632f5b9f35c956803fe60d17ffc60b688/src/match-path-sync.ts#L18-L26
* see https://github.com/jonaskello/tsconfig-paths/blob/098e066632f5b9f35c956803fe60d17ffc60b688/src/try-path.ts#L11-L17
*/
matchPath = createMatchPath(
absoluteBaseUrl,
Expand Down
89 changes: 88 additions & 1 deletion packages/plugin-dts/tests/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect, test } from '@rstest/core';
import { prettyTime } from '../src/utils';
import { mergeAliasWithTsConfigPaths, prettyTime } from '../src/utils';

test('should pretty time correctly', () => {
expect(prettyTime(0.0012)).toEqual('0.001 s');
Expand All @@ -12,3 +12,90 @@ test('should pretty time correctly', () => {
expect(prettyTime(1234)).toEqual('20 m 34 s');
expect(prettyTime(1234.5)).toEqual('20 m 34.5 s');
});

test('mergeAliasWithTsConfigPaths should handle empty inputs', () => {
// Both undefined
expect(
mergeAliasWithTsConfigPaths(undefined, undefined),
).toMatchInlineSnapshot({}, '{}');

// Empty objects
expect(mergeAliasWithTsConfigPaths({}, {})).toMatchInlineSnapshot({}, '{}');

// One empty, one undefined
expect(mergeAliasWithTsConfigPaths({}, undefined)).toMatchInlineSnapshot(
{},
'{}',
);
expect(mergeAliasWithTsConfigPaths(undefined, {})).toMatchInlineSnapshot(
{},
'{}',
);
});

test('mergeAliasWithTsConfigPaths should handle paths only', () => {
const paths = {
'@/*': ['./src/*'],
};

const result = mergeAliasWithTsConfigPaths(paths, undefined);

expect(result).toMatchInlineSnapshot(`
{
"@/*": [
"./src/*",
],
}
`);
});

test('mergeAliasWithTsConfigPaths should handle alias only', () => {
const alias = {
'@': './src',
};

const result = mergeAliasWithTsConfigPaths(undefined, alias);

expect(result).toMatchInlineSnapshot(`
{
"@": [
"./src",
],
}
`);
});

test('mergeAliasWithTsConfigPaths should handle alias overriding paths', () => {
const paths = {
'@utils/*': ['./lib/utils/*'],
'@/*': ['./src/*'],
};

const alias = {
'@utils/*': './src/utils',
'@components': './src/components',
};

const result = mergeAliasWithTsConfigPaths(paths, alias);

expect(Object.keys(result)).toMatchInlineSnapshot(`
[
"@utils/*",
"@components",
"@/*",
]
`);
expect(Object.values(result)).toMatchInlineSnapshot(`
[
[
"./src/utils",
],
[
"./src/components",
],
[
"./src/*",
],
]
`);
});
4 changes: 2 additions & 2 deletions pnpm-lock.yaml

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

1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ packages:
- 'tests/**'
- 'examples/**'
- '!**/compiled/**'
- '!**/compile/**'
- '!**/dist-types/**'

onlyBuiltDependencies:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export declare function logger(): {
(...data: any[]): void;
(message?: any, ...optionalParams: any[]): void;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function logger() {
return console.log;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "prebundle-pkg",
"version": "0.0.0",
"private": true
}
6 changes: 6 additions & 0 deletions tests/integration/dts/bundle-false/alias/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "dts-bundle-false-alias-test",
"version": "1.0.0",
"private": true,
"type": "module"
}
Loading
Loading