Skip to content

Commit 6b7cade

Browse files
authored
feat(dts): support dts.alias (#1135)
1 parent a6ec086 commit 6b7cade

File tree

22 files changed

+301
-6
lines changed

22 files changed

+301
-6
lines changed

packages/core/src/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,6 +1414,7 @@ const composeDtsConfig = async (
14141414
abortOnError: dts?.abortOnError,
14151415
dtsExtension: dts?.autoExtension ? dtsExtension : '.d.ts',
14161416
autoExternal: getAutoExternalDefaultValue(format, autoExternal),
1417+
alias: dts?.alias,
14171418
banner: banner?.dts,
14181419
footer: footer?.dts,
14191420
redirect: redirect?.dts,

packages/core/src/types/config.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ export type Dts =
8888
* @see {@link https://rslib.rs/config/lib/dts#dtsautoextension}
8989
*/
9090
autoExtension?: boolean;
91+
/**
92+
* Set the alias for declaration files, similar to the `compilerOptions.paths` option in `tsconfig.json`.
93+
* @defaultValue `{}`
94+
* @see {@link https://rslib.rs/config/lib/dts#dtsalias}
95+
*/
96+
alias?: Record<string, string>;
9197
}
9298
| boolean;
9399

packages/plugin-dts/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,25 @@ pluginDts({
136136
});
137137
```
138138

139+
### alias
140+
141+
- **Type:** `Record<string, string>`
142+
- **Default:** `{}`
143+
144+
Configure the path alias for declaration files.
145+
146+
`alias` will be merged with `compilerOptions.paths` configured in `tsconfig.json` and `alias` has a higher priority.
147+
148+
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`.
149+
150+
```js
151+
pluginDts({
152+
alias: {
153+
foo: './compiled/foo',
154+
},
155+
});
156+
```
157+
139158
### autoExternal
140159

141160
- **Type:** `boolean`

packages/plugin-dts/src/dts.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ import { logger } from '@rsbuild/core';
1212
import color from 'picocolors';
1313
import type { DtsEntry, DtsGenOptions } from './index';
1414
import { emitDts } from './tsc';
15-
import { calcLongestCommonPath, ensureTempDeclarationDir } from './utils';
15+
import {
16+
calcLongestCommonPath,
17+
ensureTempDeclarationDir,
18+
mergeAliasWithTsConfigPaths,
19+
} from './utils';
1620

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

144+
// merge alias and tsconfig paths
145+
const paths = mergeAliasWithTsConfigPaths(
146+
tsConfigResult.options.paths,
147+
alias,
148+
);
149+
if (Object.keys(paths).length > 0) {
150+
tsConfigResult.options.paths = paths;
151+
}
152+
139153
const { options: rawCompilerOptions, fileNames } = tsConfigResult;
140154

141155
// The longest common path of all non-declaration input files.
@@ -240,6 +254,7 @@ export async function generateDts(data: DtsGenOptions): Promise<void> {
240254
dtsExtension,
241255
redirect,
242256
rootDir,
257+
paths,
243258
banner,
244259
footer,
245260
},

packages/plugin-dts/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export type PluginDtsOptions = {
3232
build?: boolean;
3333
abortOnError?: boolean;
3434
dtsExtension?: string;
35+
alias?: Record<string, string>;
3536
autoExternal?:
3637
| boolean
3738
| {
@@ -91,6 +92,7 @@ export const pluginDts = (options: PluginDtsOptions = {}): RsbuildPlugin => ({
9192
options.redirect = options.redirect ?? {};
9293
options.redirect.path = options.redirect.path ?? true;
9394
options.redirect.extension = options.redirect.extension ?? false;
95+
options.alias = options.alias ?? {};
9496

9597
const dtsPromises: Promise<TaskResult>[] = [];
9698
let promisesResult: TaskResult[] = [];

packages/plugin-dts/src/tsc.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export type EmitDtsOptions = {
2121
dtsExtension: string;
2222
rootDir: string;
2323
redirect: DtsRedirect;
24+
paths: Record<string, string[]>;
2425
banner?: string;
2526
footer?: string;
2627
};
@@ -33,6 +34,7 @@ async function handleDiagnosticsAndProcessFiles(
3334
dtsExtension: string,
3435
redirect: DtsRedirect,
3536
rootDir: string,
37+
paths: Record<string, string[]>,
3638
banner?: string,
3739
footer?: string,
3840
name?: string,
@@ -54,6 +56,7 @@ async function handleDiagnosticsAndProcessFiles(
5456
redirect,
5557
configPath,
5658
rootDir,
59+
paths,
5760
banner,
5861
footer,
5962
);
@@ -89,6 +92,7 @@ export async function emitDts(
8992
rootDir,
9093
banner,
9194
footer,
95+
paths,
9296
redirect,
9397
} = options;
9498
const {
@@ -147,6 +151,7 @@ export async function emitDts(
147151
redirect,
148152
configPath,
149153
rootDir,
154+
paths,
150155
banner,
151156
footer,
152157
);
@@ -162,6 +167,7 @@ export async function emitDts(
162167
redirect,
163168
configPath,
164169
rootDir,
170+
paths,
165171
banner,
166172
footer,
167173
);
@@ -230,6 +236,7 @@ export async function emitDts(
230236
dtsExtension,
231237
redirect,
232238
rootDir,
239+
paths,
233240
banner,
234241
footer,
235242
name,
@@ -291,6 +298,7 @@ export async function emitDts(
291298
dtsExtension,
292299
redirect,
293300
rootDir,
301+
paths,
294302
banner,
295303
footer,
296304
name,
@@ -326,6 +334,7 @@ export async function emitDts(
326334
redirect,
327335
configPath,
328336
rootDir,
337+
paths,
329338
banner,
330339
footer,
331340
);

packages/plugin-dts/src/utils.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,33 @@ export function loadTsconfig(tsconfigPath: string): ts.ParsedCommandLine {
5151
return configFileContent;
5252
}
5353

54+
export function mergeAliasWithTsConfigPaths(
55+
paths: Record<string, string[]> | undefined,
56+
alias: Record<string, string> = {},
57+
): Record<string, string[]> {
58+
const mergedPaths: Record<string, string[]> = {};
59+
60+
if (alias && typeof alias === 'object' && Object.keys(alias).length > 0) {
61+
for (const [key, value] of Object.entries(alias)) {
62+
if (typeof value === 'string' && value.trim()) {
63+
mergedPaths[key] = [value];
64+
}
65+
}
66+
}
67+
68+
if (paths) {
69+
for (const [key, value] of Object.entries(paths)) {
70+
if (Array.isArray(value) && value.length > 0) {
71+
if (!mergedPaths[key]) {
72+
mergedPaths[key] = [...value];
73+
}
74+
}
75+
}
76+
}
77+
78+
return Object.keys(mergedPaths).length > 0 ? mergedPaths : {};
79+
}
80+
5481
export const TEMP_FOLDER = '.rslib';
5582
export const TEMP_DTS_DIR: string = `${TEMP_FOLDER}/declarations`;
5683

@@ -395,6 +422,7 @@ export async function processDtsFiles(
395422
redirect: DtsRedirect,
396423
tsconfigPath: string,
397424
rootDir: string,
425+
paths: Record<string, string[]>,
398426
banner?: string,
399427
footer?: string,
400428
): Promise<void> {
@@ -412,11 +440,11 @@ export async function processDtsFiles(
412440
return;
413441
}
414442

415-
const { absoluteBaseUrl, paths, addMatchAll } = result;
443+
const { absoluteBaseUrl, addMatchAll } = result;
416444
const mainFields: string[] = [];
417445
/**
418446
* resolve paths priorities:
419-
* see https://github.com/jonaskello/tsconfig-paths/blob/098e066632f5b9f35c956803fe60d17ffc60b688/src/match-path-sync.ts#L18-L26
447+
* see https://github.com/jonaskello/tsconfig-paths/blob/098e066632f5b9f35c956803fe60d17ffc60b688/src/try-path.ts#L11-L17
420448
*/
421449
matchPath = createMatchPath(
422450
absoluteBaseUrl,

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

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect, test } from '@rstest/core';
2-
import { prettyTime } from '../src/utils';
2+
import { mergeAliasWithTsConfigPaths, prettyTime } from '../src/utils';
33

44
test('should pretty time correctly', () => {
55
expect(prettyTime(0.0012)).toEqual('0.001 s');
@@ -12,3 +12,90 @@ test('should pretty time correctly', () => {
1212
expect(prettyTime(1234)).toEqual('20 m 34 s');
1313
expect(prettyTime(1234.5)).toEqual('20 m 34.5 s');
1414
});
15+
16+
test('mergeAliasWithTsConfigPaths should handle empty inputs', () => {
17+
// Both undefined
18+
expect(
19+
mergeAliasWithTsConfigPaths(undefined, undefined),
20+
).toMatchInlineSnapshot({}, '{}');
21+
22+
// Empty objects
23+
expect(mergeAliasWithTsConfigPaths({}, {})).toMatchInlineSnapshot({}, '{}');
24+
25+
// One empty, one undefined
26+
expect(mergeAliasWithTsConfigPaths({}, undefined)).toMatchInlineSnapshot(
27+
{},
28+
'{}',
29+
);
30+
expect(mergeAliasWithTsConfigPaths(undefined, {})).toMatchInlineSnapshot(
31+
{},
32+
'{}',
33+
);
34+
});
35+
36+
test('mergeAliasWithTsConfigPaths should handle paths only', () => {
37+
const paths = {
38+
'@/*': ['./src/*'],
39+
};
40+
41+
const result = mergeAliasWithTsConfigPaths(paths, undefined);
42+
43+
expect(result).toMatchInlineSnapshot(`
44+
{
45+
"@/*": [
46+
"./src/*",
47+
],
48+
}
49+
`);
50+
});
51+
52+
test('mergeAliasWithTsConfigPaths should handle alias only', () => {
53+
const alias = {
54+
'@': './src',
55+
};
56+
57+
const result = mergeAliasWithTsConfigPaths(undefined, alias);
58+
59+
expect(result).toMatchInlineSnapshot(`
60+
{
61+
"@": [
62+
"./src",
63+
],
64+
}
65+
`);
66+
});
67+
68+
test('mergeAliasWithTsConfigPaths should handle alias overriding paths', () => {
69+
const paths = {
70+
'@utils/*': ['./lib/utils/*'],
71+
'@/*': ['./src/*'],
72+
};
73+
74+
const alias = {
75+
'@utils/*': './src/utils',
76+
'@components': './src/components',
77+
};
78+
79+
const result = mergeAliasWithTsConfigPaths(paths, alias);
80+
81+
expect(Object.keys(result)).toMatchInlineSnapshot(`
82+
[
83+
"@utils/*",
84+
"@components",
85+
"@/*",
86+
]
87+
`);
88+
expect(Object.values(result)).toMatchInlineSnapshot(`
89+
[
90+
[
91+
"./src/utils",
92+
],
93+
[
94+
"./src/components",
95+
],
96+
[
97+
"./src/*",
98+
],
99+
]
100+
`);
101+
});

pnpm-lock.yaml

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ packages:
55
- 'tests/**'
66
- 'examples/**'
77
- '!**/compiled/**'
8+
- '!**/compile/**'
89
- '!**/dist-types/**'
910

1011
onlyBuiltDependencies:

0 commit comments

Comments
 (0)