rsbuild-plugin-dts 0.21.4 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -326,6 +326,55 @@ pluginDts({
326
326
  }
327
327
  ```
328
328
 
329
+ ### isolated
330
+
331
+ - **Type:** `boolean`
332
+ - **Default:** `false`
333
+
334
+ Whether to generate declaration files with [isolatedDeclarations](https://www.typescriptlang.org/tsconfig/#isolatedDeclarations).
335
+
336
+ > This option was added in Rslib v0.22.0 and is currently still experimental.
337
+
338
+ When enabled, `rsbuild-plugin-dts` uses Rspack's built-in SWC fast_dts capability to generate declaration files directly during the build.
339
+
340
+ ```js
341
+ pluginDts({
342
+ isolated: true,
343
+ });
344
+ ```
345
+
346
+ When enabling this option, we recommend also enabling `isolatedDeclarations` in `tsconfig.json` so TypeScript can surface code that does not satisfy the `isolatedDeclarations` constraints earlier.
347
+
348
+ ```json
349
+ {
350
+ "compilerOptions": {
351
+ "isolatedDeclarations": true
352
+ }
353
+ }
354
+ ```
355
+
356
+ #### Use cases
357
+
358
+ By default, `rsbuild-plugin-dts` generates declaration files through the TypeScript Compiler API, and it can also generate declaration files by enabling [tsgo](#tsgo). Unlike these two approaches, `isolated` does not perform full type checking during declaration file generation, so it is more suitable for use with another independent, high-performance type checking workflow.
359
+
360
+ For example, in a monorepo project, you can use this option together with [`rslint --type-check`](https://rslint.rs/guide/type-checking):
361
+
362
+ - Use `rslint --type-check` as the unified type checking step.
363
+ - Use `isolated` to quickly output declaration files when building each package.
364
+
365
+ This preserves full type checking while reducing the cost of repeatedly running TypeScript type analysis, loading TypeScript or `tsgo` related packages, and loading native bindings during each package build, making the overall build pipeline lighter.
366
+
367
+ #### Output scope
368
+
369
+ `isolated` generates declaration files based on the module dependency graph during the Rspack build. Only entry modules and modules referenced by entry modules will generate corresponding declaration files. If a file is not included in the build dependency graph, it will not automatically generate declaration files like TypeScript does. If you want declaration files to be generated for these files as well, add them to [source.entry](https://rslib.rs/config/rsbuild/source#sourceentry), or make sure they are referenced by an existing entry.
370
+
371
+ #### Usage constraints
372
+
373
+ - `isolated` is currently only available when `pluginDts` is used through Rslib, because it requires Rslib's built-in RslibPlugin.
374
+ - `isolated` cannot be enabled together with [tsgo](#tsgo).
375
+ - `isolated` cannot be enabled together with [build](#build).
376
+ - When `isolated` is enabled, [abortOnError](#abortonerror) cannot be set to `false`.
377
+
329
378
  ## Contributing
330
379
 
331
380
  Please read the [Contributing Guide](https://github.com/web-infra-dev/rslib/blob/main/CONTRIBUTING.md).
@@ -1,10 +1,10 @@
1
1
  import node_fs from "node:fs";
2
+ import node_path, { basename, dirname, extname, isAbsolute, join, normalize, relative as external_node_path_relative, resolve } from "node:path";
3
+ import { logger } from "@rsbuild/core";
2
4
  import promises from "node:fs/promises";
3
5
  import { createRequire } from "node:module";
4
6
  import { platform } from "node:os";
5
- import node_path, { basename, dirname, extname, isAbsolute, join, normalize, relative as external_node_path_relative, resolve } from "node:path";
6
7
  import { styleText } from "node:util";
7
- import { logger } from "@rsbuild/core";
8
8
  import * as __rspack_external_fs from "fs";
9
9
  import { readdir, readdirSync, realpath, realpathSync, stat as external_fs_stat, statSync } from "fs";
10
10
  import { basename as external_path_basename, dirname as external_path_dirname, isAbsolute as external_path_isAbsolute, normalize as external_path_normalize, posix, relative as external_path_relative, resolve as external_path_resolve, sep as external_path_sep } from "path";
@@ -1842,7 +1842,7 @@ const walkSync = (state, crawlPath, directoryPath, currentDepth, callback$1)=>{
1842
1842
  }
1843
1843
  callback$1(entries, directoryPath, currentDepth);
1844
1844
  };
1845
- function build(isSynchronous) {
1845
+ function dist_build(isSynchronous) {
1846
1846
  return isSynchronous ? walkSync : walkAsync;
1847
1847
  }
1848
1848
  var Queue = class {
@@ -1927,7 +1927,7 @@ var Walker = class {
1927
1927
  this.getArray = build$4(options);
1928
1928
  this.groupFiles = build$3(options);
1929
1929
  this.resolveSymlink = build$2(options, this.isSynchronous);
1930
- this.walkDirectory = build(this.isSynchronous);
1930
+ this.walkDirectory = dist_build(this.isSynchronous);
1931
1931
  }
1932
1932
  start() {
1933
1933
  this.pushDirectory(this.root, this.state.paths, this.state.options.filters);
@@ -3666,7 +3666,25 @@ async function redirectDtsImports(dtsFile, dtsExtension, redirect, matchPath, ou
3666
3666
  const ext = extname(redirectImportPath);
3667
3667
  const shouldAddExtension = redirectImportPath !== importPath || importPath.startsWith('.') || isAbsolute(importPath);
3668
3668
  if (ext) if (JS_EXTENSIONS_PATTERN.test(redirectImportPath)) {
3669
- if (redirect.extension) redirectImportPath = redirectImportPath.replace(/\.[^.]+$/, jsExtension);
3669
+ if (redirect.extension) {
3670
+ let redirectJsExtension = jsExtension;
3671
+ const pathWithoutExtension = redirectImportPath.replace(/\.[^.]+$/, '');
3672
+ let importDtsExtension;
3673
+ if ([
3674
+ '.mjs',
3675
+ '.mjsx',
3676
+ '.mts',
3677
+ '.mtsx'
3678
+ ].includes(ext)) importDtsExtension = '.d.mts';
3679
+ else if ([
3680
+ '.cjs',
3681
+ '.cjsx',
3682
+ '.cts',
3683
+ '.ctsx'
3684
+ ].includes(ext)) importDtsExtension = '.d.cts';
3685
+ if (importDtsExtension && await pathExists(join(dirname(dtsFile), `${pathWithoutExtension}${importDtsExtension}`))) redirectJsExtension = '.d.mts' === importDtsExtension ? '.mjs' : '.cjs';
3686
+ redirectImportPath = redirectImportPath.replace(/\.[^.]+$/, redirectJsExtension);
3687
+ }
3670
3688
  } else redirectImportPath = await addExtension(redirect, dtsFile, redirectImportPath, jsExtension, dtsExtension);
3671
3689
  else if (shouldAddExtension) redirectImportPath = await addExtension(redirect, dtsFile, redirectImportPath, jsExtension, dtsExtension);
3672
3690
  const normalizedRedirectImportPath = redirectImportPath.split(node_path.sep).join('/');
@@ -3756,6 +3774,21 @@ async function cleanDtsFiles(cwd, dir) {
3756
3774
  force: true
3757
3775
  })));
3758
3776
  }
3777
+ async function rewriteDtsExtensions(cwd, dir, dtsExtension, bundle, hasDeclarationMap = false) {
3778
+ if (bundle) return;
3779
+ const dtsFiles = await globDtsFiles(cwd, dir, [
3780
+ '/**/*.d.ts',
3781
+ '/**/*.d.ts.map'
3782
+ ]);
3783
+ await Promise.all(dtsFiles.map(async (file)=>{
3784
+ const contents = await promises.readFile(file, 'utf8');
3785
+ const newFileName = renameDtsFile(file, dtsExtension, bundle);
3786
+ const newContents = updateDeclarationMapContent(file, contents, dtsExtension, bundle, hasDeclarationMap);
3787
+ if (file === newFileName && contents === newContents) return;
3788
+ await promises.writeFile(newFileName, newContents);
3789
+ if (file !== newFileName) await promises.unlink(file);
3790
+ }));
3791
+ }
3759
3792
  async function cleanTsBuildInfoFile(tsconfigPath, compilerOptions) {
3760
3793
  const tsconfigDir = dirname(tsconfigPath);
3761
3794
  const { outDir, rootDir, tsBuildInfoFile } = compilerOptions;
@@ -3791,4 +3824,157 @@ function updateDeclarationMapContent(fileName, content, dtsExtension, bundle, ha
3791
3824
  if (fileName.endsWith('.d.ts.map')) return content.replace(/("file":"[^"]*)\.d\.ts"/g, `$1${dtsExtension}"`);
3792
3825
  return content;
3793
3826
  }
3794
- export { addBannerAndFooter, calcLongestCommonPath, cleanDtsFiles, cleanTsBuildInfoFile, clearTempDeclarationDir, color, ensureTempDeclarationDir, getDtsEmitPath, getTimeCost, globDtsFiles, loadTsconfig, mergeAliasWithTsConfigPaths, processDtsFiles, processSourceEntry, renameDtsFile, ts, updateDeclarationMapContent, warnIfOutside };
3827
+ const dts_isObject = (obj)=>'[object Object]' === Object.prototype.toString.call(obj);
3828
+ const DEFAULT_EXCLUDED_PACKAGES = [
3829
+ '@types/react'
3830
+ ];
3831
+ const calcBundledPackages = (options)=>{
3832
+ const { cwd, autoExternal, userExternals, overrideBundledPackages } = options;
3833
+ if (overrideBundledPackages) return overrideBundledPackages;
3834
+ let pkgJson;
3835
+ try {
3836
+ const content = node_fs.readFileSync(join(cwd, 'package.json'), 'utf-8');
3837
+ pkgJson = JSON.parse(content);
3838
+ } catch {
3839
+ logger.warn('The type of third-party packages will not be bundled due to read package.json failed');
3840
+ return [];
3841
+ }
3842
+ const externalOptions = autoExternal ? {
3843
+ dependencies: true,
3844
+ peerDependencies: true,
3845
+ optionalDependencies: true,
3846
+ devDependencies: false,
3847
+ ...true === autoExternal ? {} : autoExternal
3848
+ } : {
3849
+ dependencies: false,
3850
+ peerDependencies: false,
3851
+ devDependencies: false
3852
+ };
3853
+ const getUserExternalsKeys = (value)=>{
3854
+ if (!value) return [];
3855
+ if ('string' == typeof value || value instanceof RegExp) return [
3856
+ value
3857
+ ];
3858
+ if (Array.isArray(value)) return value.flatMap((v)=>getUserExternalsKeys(v));
3859
+ if (dts_isObject(userExternals)) return Object.keys(userExternals);
3860
+ return [];
3861
+ };
3862
+ const externals = getUserExternalsKeys(userExternals);
3863
+ const allDeps = [];
3864
+ for (const type of [
3865
+ 'dependencies',
3866
+ 'peerDependencies',
3867
+ 'devDependencies'
3868
+ ]){
3869
+ const deps = pkgJson[type] && Object.keys(pkgJson[type]);
3870
+ if (deps) {
3871
+ if (externalOptions[type]) externals.push(...deps);
3872
+ allDeps.push(...deps);
3873
+ }
3874
+ }
3875
+ const bundledPackages = allDeps.filter((d)=>!externals.some((e)=>'string' == typeof e ? d === e : e.test(d)));
3876
+ const filteredBundledPackages = Array.from(new Set(bundledPackages)).filter((pkg)=>!DEFAULT_EXCLUDED_PACKAGES.includes(pkg));
3877
+ return filteredBundledPackages;
3878
+ };
3879
+ async function prepareDtsContext(data) {
3880
+ const { bundle, dtsEntry, dtsEmitPath, tsconfigPath, tsConfigResult, name, cwd, build, alias = {} } = data;
3881
+ const paths = mergeAliasWithTsConfigPaths(tsConfigResult.options.paths, alias);
3882
+ if (Object.keys(paths).length > 0) tsConfigResult.options.paths = paths;
3883
+ const { options: rawCompilerOptions, fileNames } = tsConfigResult;
3884
+ const rootDir = rawCompilerOptions.rootDir ?? (rawCompilerOptions.composite ? dirname(tsconfigPath) : await calcLongestCommonPath(fileNames.filter((fileName)=>!/\.d\.(ts|mts|cts)$/.test(fileName)))) ?? dirname(tsconfigPath);
3885
+ const resolvedDtsEmitPath = normalize(resolve(dirname(tsconfigPath), dtsEmitPath));
3886
+ if (build) {
3887
+ if (bundle) throw Error('Can not set "dts.bundle: true" when "dts.build: true"');
3888
+ if ((!rawCompilerOptions.outDir || normalize(rawCompilerOptions.outDir) !== resolvedDtsEmitPath) && (!rawCompilerOptions.declarationDir || normalize(rawCompilerOptions.declarationDir) !== resolvedDtsEmitPath)) throw Error(`Please set "declarationDir": "${dtsEmitPath}" in ${color.underline(tsconfigPath)} to keep it same as "dts.distPath" or "output.distPath" field in lib config.`);
3889
+ }
3890
+ const declarationDir = bundle ? ensureTempDeclarationDir(cwd, name) : dtsEmitPath;
3891
+ const bundledDtsEntries = bundle ? dtsEntry.map((entryObj)=>{
3892
+ const { name: entryName, path: entryPath } = entryObj;
3893
+ if (!entryPath) return null;
3894
+ const entrySourcePath = isAbsolute(entryPath) ? entryPath : join(cwd, entryPath);
3895
+ const relativePath = external_node_path_relative(rootDir, dirname(entrySourcePath));
3896
+ const newPath = join(declarationDir, relativePath, basename(entrySourcePath)).replace(/\.(js|mjs|jsx|ts|mts|tsx|cjs|cts|cjsx|ctsx|mjsx|mtsx)$/, '.d.ts');
3897
+ return {
3898
+ name: entryName,
3899
+ path: newPath
3900
+ };
3901
+ }).filter(Boolean) : [];
3902
+ return {
3903
+ declarationDir,
3904
+ rootDir,
3905
+ paths,
3906
+ bundledDtsEntries
3907
+ };
3908
+ }
3909
+ async function bundleDtsIfNeeded(data, context) {
3910
+ const { bundle, name, cwd, dtsEmitPath, tsconfigPath, dtsExtension = '.d.ts', autoExternal = true, userExternals, apiExtractorOptions, banner, footer } = data;
3911
+ if (!bundle) return;
3912
+ const { bundledDtsEntries } = context;
3913
+ const { bundleDts } = await import("./apiExtractor.js");
3914
+ await bundleDts({
3915
+ name,
3916
+ cwd,
3917
+ distPath: dtsEmitPath,
3918
+ dtsEntry: bundledDtsEntries,
3919
+ tsconfigPath,
3920
+ dtsExtension,
3921
+ banner,
3922
+ footer,
3923
+ bundledPackages: calcBundledPackages({
3924
+ cwd,
3925
+ autoExternal,
3926
+ userExternals,
3927
+ overrideBundledPackages: apiExtractorOptions?.bundledPackages
3928
+ })
3929
+ });
3930
+ }
3931
+ async function generateDts(data) {
3932
+ const { bundle, tsconfigPath, tsConfigResult, name, cwd, build, isWatch, dtsExtension = '.d.ts', banner, footer, redirect = {
3933
+ path: true,
3934
+ extension: false
3935
+ }, loggerLevel } = data;
3936
+ logger.level = loggerLevel;
3937
+ if (!isWatch) logger.start(`generating declaration files... ${color.dim(`(${name})`)}`);
3938
+ const preparedDtsContext = await prepareDtsContext(data);
3939
+ const { declarationDir, rootDir, paths } = preparedDtsContext;
3940
+ const onComplete = async (isSuccess)=>{
3941
+ if (isSuccess) try {
3942
+ await bundleDtsIfNeeded(data, preparedDtsContext);
3943
+ } catch (e) {
3944
+ logger.error(e);
3945
+ }
3946
+ };
3947
+ const emitDts = data.tsgo ? await import("./tsgo.js").then((mod)=>mod.emitDtsTsgo) : await import("./tsc.js").then((mod)=>mod.emitDtsTsc);
3948
+ const hasError = await emitDts({
3949
+ name,
3950
+ cwd,
3951
+ configPath: tsconfigPath,
3952
+ tsConfigResult,
3953
+ declarationDir,
3954
+ dtsExtension,
3955
+ redirect,
3956
+ rootDir,
3957
+ paths,
3958
+ banner,
3959
+ footer
3960
+ }, onComplete, bundle, isWatch, build);
3961
+ if (data.tsgo) {
3962
+ if (!hasError) await bundleDtsIfNeeded(data, preparedDtsContext);
3963
+ } else if (!isWatch) await bundleDtsIfNeeded(data, preparedDtsContext);
3964
+ }
3965
+ process.on('disconnect', ()=>{
3966
+ process.exit();
3967
+ });
3968
+ process.on('message', async (data)=>{
3969
+ if (!data.cwd) return;
3970
+ try {
3971
+ await generateDts(data);
3972
+ } catch (e) {
3973
+ logger.error(e);
3974
+ process.send('error');
3975
+ process.exit(1);
3976
+ }
3977
+ process.send('success');
3978
+ if (!data.isWatch) process.exit();
3979
+ });
3980
+ export { DEFAULT_EXCLUDED_PACKAGES, addBannerAndFooter, bundleDtsIfNeeded, calcBundledPackages, cleanDtsFiles, cleanTsBuildInfoFile, clearTempDeclarationDir, color, generateDts, getDtsEmitPath, getTimeCost, loadTsconfig, prepareDtsContext, processDtsFiles, processSourceEntry, renameDtsFile, rewriteDtsExtensions, ts, updateDeclarationMapContent, warnIfOutside };
@@ -1,7 +1,7 @@
1
1
  import node_fs from "node:fs";
2
2
  import { join, relative } from "node:path";
3
3
  import { logger } from "@rsbuild/core";
4
- import { color, addBannerAndFooter, getTimeCost } from "./350.js";
4
+ import { color, addBannerAndFooter, getTimeCost } from "./981.js";
5
5
  const logPrefixApiExtractor = color.dim('[api-extractor]');
6
6
  async function bundleDts(options) {
7
7
  let apiExtractor;
@@ -0,0 +1,4 @@
1
+ import type { PluginDtsOptions } from './index.js';
2
+ export type DtsGenerationBackend = 'isolated' | 'tsc' | 'tsgo';
3
+ export declare function validateExplicitIsolatedDtsOptions(options: Pick<PluginDtsOptions, 'isolated' | 'tsgo' | 'build' | 'abortOnError'>): void;
4
+ export declare function resolveDtsGenerationBackend(options: Pick<PluginDtsOptions, 'isolated' | 'tsgo' | 'build' | 'abortOnError'>): DtsGenerationBackend;
package/dist/dts.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { DtsGenOptions } from './index.js';
1
+ import type { DtsEntry, DtsGenOptions } from './index.js';
2
2
  export declare const DEFAULT_EXCLUDED_PACKAGES: string[];
3
3
  type CalculateBundledPackagesOptions = {
4
4
  cwd: string;
@@ -7,5 +7,13 @@ type CalculateBundledPackagesOptions = {
7
7
  overrideBundledPackages?: string[];
8
8
  };
9
9
  export declare const calcBundledPackages: (options: CalculateBundledPackagesOptions) => string[];
10
+ export type PreparedDtsContext = {
11
+ declarationDir: string;
12
+ rootDir: string;
13
+ paths: Record<string, string[]>;
14
+ bundledDtsEntries: Required<DtsEntry>[];
15
+ };
16
+ export declare function prepareDtsContext(data: DtsGenOptions): Promise<PreparedDtsContext>;
17
+ export declare function bundleDtsIfNeeded(data: DtsGenOptions, context: Pick<PreparedDtsContext, 'bundledDtsEntries'>): Promise<void>;
10
18
  export declare function generateDts(data: DtsGenOptions): Promise<void>;
11
19
  export {};
package/dist/dts.js CHANGED
@@ -1,147 +1 @@
1
- import node_fs from "node:fs";
2
- import { basename, dirname, isAbsolute, join, normalize, relative, resolve } from "node:path";
3
- import { logger } from "@rsbuild/core";
4
- import { ensureTempDeclarationDir, color, calcLongestCommonPath, mergeAliasWithTsConfigPaths } from "./350.js";
5
- const isObject = (obj)=>'[object Object]' === Object.prototype.toString.call(obj);
6
- const DEFAULT_EXCLUDED_PACKAGES = [
7
- '@types/react'
8
- ];
9
- const calcBundledPackages = (options)=>{
10
- const { cwd, autoExternal, userExternals, overrideBundledPackages } = options;
11
- if (overrideBundledPackages) return overrideBundledPackages;
12
- let pkgJson;
13
- try {
14
- const content = node_fs.readFileSync(join(cwd, 'package.json'), 'utf-8');
15
- pkgJson = JSON.parse(content);
16
- } catch {
17
- logger.warn('The type of third-party packages will not be bundled due to read package.json failed');
18
- return [];
19
- }
20
- const externalOptions = autoExternal ? {
21
- dependencies: true,
22
- peerDependencies: true,
23
- optionalDependencies: true,
24
- devDependencies: false,
25
- ...true === autoExternal ? {} : autoExternal
26
- } : {
27
- dependencies: false,
28
- peerDependencies: false,
29
- devDependencies: false
30
- };
31
- const getUserExternalsKeys = (value)=>{
32
- if (!value) return [];
33
- if ('string' == typeof value || value instanceof RegExp) return [
34
- value
35
- ];
36
- if (Array.isArray(value)) return value.flatMap((v)=>getUserExternalsKeys(v));
37
- if (isObject(userExternals)) return Object.keys(userExternals);
38
- return [];
39
- };
40
- const externals = getUserExternalsKeys(userExternals);
41
- const allDeps = [];
42
- for (const type of [
43
- 'dependencies',
44
- 'peerDependencies',
45
- 'devDependencies'
46
- ]){
47
- const deps = pkgJson[type] && Object.keys(pkgJson[type]);
48
- if (deps) {
49
- if (externalOptions[type]) externals.push(...deps);
50
- allDeps.push(...deps);
51
- }
52
- }
53
- const bundledPackages = allDeps.filter((d)=>!externals.some((e)=>'string' == typeof e ? d === e : e.test(d)));
54
- const filteredBundledPackages = Array.from(new Set(bundledPackages)).filter((pkg)=>!DEFAULT_EXCLUDED_PACKAGES.includes(pkg));
55
- return filteredBundledPackages;
56
- };
57
- async function generateDts(data) {
58
- const { bundle, dtsEntry, dtsEmitPath, tsconfigPath, tsConfigResult, name, cwd, build, isWatch, dtsExtension = '.d.ts', autoExternal = true, alias = {}, userExternals, apiExtractorOptions, banner, footer, redirect = {
59
- path: true,
60
- extension: false
61
- }, tsgo, loggerLevel } = data;
62
- logger.level = loggerLevel;
63
- if (!isWatch) logger.start(`generating declaration files... ${color.dim(`(${name})`)}`);
64
- const paths = mergeAliasWithTsConfigPaths(tsConfigResult.options.paths, alias);
65
- if (Object.keys(paths).length > 0) tsConfigResult.options.paths = paths;
66
- const { options: rawCompilerOptions, fileNames } = tsConfigResult;
67
- const rootDir = rawCompilerOptions.rootDir ?? (rawCompilerOptions.composite ? dirname(tsconfigPath) : await calcLongestCommonPath(fileNames.filter((fileName)=>!/\.d\.(ts|mts|cts)$/.test(fileName)))) ?? dirname(tsconfigPath);
68
- const resolvedDtsEmitPath = normalize(resolve(dirname(tsconfigPath), dtsEmitPath));
69
- if (build) {
70
- if (bundle) throw Error('Can not set "dts.bundle: true" when "dts.build: true"');
71
- if ((!rawCompilerOptions.outDir || normalize(rawCompilerOptions.outDir) !== resolvedDtsEmitPath) && (!rawCompilerOptions.declarationDir || normalize(rawCompilerOptions.declarationDir) !== resolvedDtsEmitPath)) throw Error(`Please set "declarationDir": "${dtsEmitPath}" in ${color.underline(tsconfigPath)} to keep it same as "dts.distPath" or "output.distPath" field in lib config.`);
72
- }
73
- const declarationDir = bundle ? ensureTempDeclarationDir(cwd, name) : dtsEmitPath;
74
- let dtsEntries = [];
75
- if (bundle) dtsEntries = dtsEntry.map((entryObj)=>{
76
- const { name: entryName, path: entryPath } = entryObj;
77
- if (!entryPath) return null;
78
- const entrySourcePath = isAbsolute(entryPath) ? entryPath : join(cwd, entryPath);
79
- const relativePath = relative(rootDir, dirname(entrySourcePath));
80
- const newPath = join(declarationDir, relativePath, basename(entrySourcePath)).replace(/\.(js|mjs|jsx|ts|mts|tsx|cjs|cts|cjsx|ctsx|mjsx|mtsx)$/, '.d.ts');
81
- return {
82
- name: entryName,
83
- path: newPath
84
- };
85
- }).filter(Boolean);
86
- const bundleDtsIfNeeded = async ()=>{
87
- if (bundle) {
88
- const { bundleDts } = await import("./apiExtractor.js");
89
- await bundleDts({
90
- name,
91
- cwd,
92
- distPath: dtsEmitPath,
93
- dtsEntry: dtsEntries,
94
- tsconfigPath,
95
- dtsExtension,
96
- banner,
97
- footer,
98
- bundledPackages: calcBundledPackages({
99
- cwd,
100
- autoExternal,
101
- userExternals,
102
- overrideBundledPackages: apiExtractorOptions?.bundledPackages
103
- })
104
- });
105
- }
106
- };
107
- const onComplete = async (isSuccess)=>{
108
- if (isSuccess) try {
109
- await bundleDtsIfNeeded();
110
- } catch (e) {
111
- logger.error(e);
112
- }
113
- };
114
- const emitDts = tsgo ? await import("./tsgo.js").then((mod)=>mod.emitDtsTsgo) : await import("./tsc.js").then((mod)=>mod.emitDtsTsc);
115
- const hasError = await emitDts({
116
- name,
117
- cwd,
118
- configPath: tsconfigPath,
119
- tsConfigResult,
120
- declarationDir,
121
- dtsExtension,
122
- redirect,
123
- rootDir,
124
- paths,
125
- banner,
126
- footer
127
- }, onComplete, bundle, isWatch, build);
128
- if (tsgo) {
129
- if (!hasError) await bundleDtsIfNeeded();
130
- } else if (!isWatch) await bundleDtsIfNeeded();
131
- }
132
- process.on('disconnect', ()=>{
133
- process.exit();
134
- });
135
- process.on('message', async (data)=>{
136
- if (!data.cwd) return;
137
- try {
138
- await generateDts(data);
139
- } catch (e) {
140
- logger.error(e);
141
- process.send('error');
142
- process.exit(1);
143
- }
144
- process.send('success');
145
- if (!data.isWatch) process.exit();
146
- });
147
- export { DEFAULT_EXCLUDED_PACKAGES, calcBundledPackages, generateDts };
1
+ export { DEFAULT_EXCLUDED_PACKAGES, bundleDtsIfNeeded, calcBundledPackages, generateDts, prepareDtsContext } from "./981.js";
package/dist/index.d.ts CHANGED
@@ -14,6 +14,7 @@ export type PluginDtsOptions = {
14
14
  abortOnError?: boolean;
15
15
  dtsExtension?: string;
16
16
  alias?: Record<string, string>;
17
+ isolated?: boolean;
17
18
  autoExternal?: boolean | {
18
19
  dependencies?: boolean;
19
20
  optionalDependencies?: boolean;
@@ -29,7 +30,7 @@ export type DtsEntry = {
29
30
  name: string;
30
31
  path: string;
31
32
  };
32
- export type DtsGenOptions = Omit<PluginDtsOptions, 'bundle'> & {
33
+ export type DtsGenOptions = Omit<PluginDtsOptions, 'bundle' | 'isolated'> & {
33
34
  bundle: boolean;
34
35
  name: string;
35
36
  cwd: string;
package/dist/index.js CHANGED
@@ -1,7 +1,59 @@
1
1
  import { fork } from "node:child_process";
2
- import { extname, join } from "node:path";
3
- import { logger } from "@rsbuild/core";
4
- import { processSourceEntry, cleanDtsFiles, clearTempDeclarationDir, getDtsEmitPath, cleanTsBuildInfoFile, color, warnIfOutside, ts, loadTsconfig } from "./350.js";
2
+ import { extname, join, normalize, resolve as external_node_path_resolve } from "node:path";
3
+ import { logger, rspack } from "@rsbuild/core";
4
+ import { loadTsconfig, processSourceEntry, bundleDtsIfNeeded, cleanDtsFiles, getDtsEmitPath, rewriteDtsExtensions, clearTempDeclarationDir, cleanTsBuildInfoFile, color, prepareDtsContext, warnIfOutside, ts, processDtsFiles } from "./981.js";
5
+ const applyIsolatedDtsOptions = (isolatedDtsContext)=>{
6
+ const bundlerConfig = isolatedDtsContext.bundlerConfig;
7
+ const RspackRslibPlugin = rspack.experiments.RslibPlugin;
8
+ const rslibPlugin = bundlerConfig?.plugins?.find((plugin)=>plugin instanceof RspackRslibPlugin);
9
+ if (!rslibPlugin?._args?.[0]) throw new Error('Can not enable "dts.isolated: true" without the built-in RslibPlugin.');
10
+ const emitDts = {
11
+ rootDir: normalize(external_node_path_resolve(isolatedDtsContext.cwd, isolatedDtsContext.rootDir)),
12
+ declarationDir: normalize(external_node_path_resolve(isolatedDtsContext.cwd, isolatedDtsContext.declarationDir))
13
+ };
14
+ rslibPlugin._args[0].emitDts = emitDts;
15
+ if (rslibPlugin._args[0].emitDts !== emitDts) throw new Error('Failed to configure Rspack isolated declaration emission.');
16
+ };
17
+ async function createIsolatedDtsContext(dtsGenOptions, bundlerConfig) {
18
+ const isolatedDtsContext = {
19
+ ...dtsGenOptions,
20
+ dtsExtension: dtsGenOptions.dtsExtension ?? '.d.ts',
21
+ bundlerConfig,
22
+ ...await prepareDtsContext(dtsGenOptions)
23
+ };
24
+ applyIsolatedDtsOptions(isolatedDtsContext);
25
+ return isolatedDtsContext;
26
+ }
27
+ async function processIsolatedDts(isolatedDtsContext, options = {}) {
28
+ const { logSuccess = true } = options;
29
+ await rewriteDtsExtensions(isolatedDtsContext.cwd, isolatedDtsContext.declarationDir, isolatedDtsContext.dtsExtension, isolatedDtsContext.bundle, isolatedDtsContext.tsConfigResult.options.declarationMap);
30
+ await processDtsFiles(isolatedDtsContext.bundle, isolatedDtsContext.cwd, isolatedDtsContext.declarationDir, isolatedDtsContext.dtsExtension, isolatedDtsContext.redirect ?? {
31
+ path: true,
32
+ extension: false
33
+ }, isolatedDtsContext.tsconfigPath, isolatedDtsContext.rootDir, isolatedDtsContext.paths, isolatedDtsContext.banner, isolatedDtsContext.footer);
34
+ if (!logSuccess) return;
35
+ if (isolatedDtsContext.bundle) {
36
+ if (!isolatedDtsContext.isWatch) logger.info(`declaration files prepared with isolated declaration ${color.dim(`(${isolatedDtsContext.name})`)}`);
37
+ try {
38
+ await bundleDtsIfNeeded(isolatedDtsContext, isolatedDtsContext);
39
+ } catch (error) {
40
+ if (!isolatedDtsContext.isWatch) throw error;
41
+ logger.error(error);
42
+ }
43
+ } else if (!isolatedDtsContext.isWatch) logger.ready(`declaration files generated with isolated declaration ${color.dim(`(${isolatedDtsContext.name})`)}`);
44
+ }
45
+ function validateExplicitIsolatedDtsOptions(options) {
46
+ if (true !== options.isolated) return;
47
+ if (options.tsgo) throw new Error('Can not set "dts.isolated: true" when "dts.tsgo: true".');
48
+ if (options.build) throw new Error('Can not set "dts.isolated: true" when "dts.build: true".');
49
+ if (false === options.abortOnError) throw new Error('Can not set "dts.abortOnError: false" when "dts.isolated: true".');
50
+ }
51
+ function resolveDtsGenerationBackend(options) {
52
+ validateExplicitIsolatedDtsOptions(options);
53
+ if (true === options.isolated) return 'isolated';
54
+ if (options.tsgo) return 'tsgo';
55
+ return 'tsc';
56
+ }
5
57
  const PLUGIN_DTS_NAME = 'rsbuild:dts';
6
58
  const pluginDts = (options = {})=>({
7
59
  name: PLUGIN_DTS_NAME,
@@ -25,8 +77,25 @@ const pluginDts = (options = {})=>({
25
77
  });
26
78
  let promiseResult;
27
79
  let childProcesses = [];
28
- api.onBeforeEnvironmentCompile(async ({ isWatch, isFirstCompile, environment })=>{
29
- if (!isFirstCompile && !options.tsgo) return;
80
+ const dtsBackend = resolveDtsGenerationBackend(options);
81
+ let dtsGenOptions;
82
+ let isolatedDtsContext;
83
+ api.modifyEnvironmentConfig((config, { mergeEnvironmentConfig })=>{
84
+ if (true !== options.isolated) return;
85
+ return mergeEnvironmentConfig(config, {
86
+ tools: {
87
+ swc: {
88
+ jsc: {
89
+ experimental: {
90
+ emitIsolatedDts: true
91
+ }
92
+ }
93
+ }
94
+ }
95
+ });
96
+ });
97
+ api.onBeforeEnvironmentCompile(async ({ isWatch, isFirstCompile, environment, bundlerConfig })=>{
98
+ if ('tsc' === dtsBackend && !isFirstCompile) return;
30
99
  const { config } = environment;
31
100
  const dtsEntry = processSourceEntry(bundle, config.source?.entry);
32
101
  const cwd = api.context.rootPath;
@@ -45,13 +114,8 @@ const pluginDts = (options = {})=>({
45
114
  warnIfOutside(cwd, outDir, 'outDir');
46
115
  if (false !== config.output.cleanDistPath) await cleanDtsFiles(cwd, dtsEmitPath);
47
116
  if (bundle) await clearTempDeclarationDir(cwd);
48
- if (composite || incremental || options.build) await cleanTsBuildInfoFile(tsconfigPath, rawCompilerOptions);
49
- const jsExtension = extname(import.meta.filename);
50
- const childProcess = fork(join(import.meta.dirname, `./dts${jsExtension}`), [], {
51
- stdio: 'inherit'
52
- });
53
- childProcesses.push(childProcess);
54
- const dtsGenOptions = {
117
+ if ('isolated' !== dtsBackend && (composite || incremental || options.build)) await cleanTsBuildInfoFile(tsconfigPath, rawCompilerOptions);
118
+ dtsGenOptions = {
55
119
  ...options,
56
120
  bundle,
57
121
  dtsEntry,
@@ -65,6 +129,18 @@ const pluginDts = (options = {})=>({
65
129
  isWatch,
66
130
  loggerLevel: loggerLevel
67
131
  };
132
+ if ('isolated' === dtsBackend) {
133
+ isolatedDtsContext = await createIsolatedDtsContext(dtsGenOptions, bundlerConfig);
134
+ dtsPromise = Promise.resolve({
135
+ status: 'success'
136
+ });
137
+ return;
138
+ }
139
+ const jsExtension = extname(import.meta.filename);
140
+ const childProcess = fork(join(import.meta.dirname, `./dts${jsExtension}`), [], {
141
+ stdio: 'inherit'
142
+ });
143
+ childProcesses.push(childProcess);
68
144
  childProcess.send(dtsGenOptions);
69
145
  dtsPromise = new Promise((resolve)=>{
70
146
  childProcess.on('message', (message)=>{
@@ -79,14 +155,31 @@ const pluginDts = (options = {})=>({
79
155
  });
80
156
  });
81
157
  api.onAfterBuild({
82
- handler: async ({ isFirstCompile })=>{
83
- if (!isFirstCompile) return;
158
+ handler: async ({ isFirstCompile, stats })=>{
159
+ if ('tsc' === dtsBackend && !isFirstCompile) return;
160
+ if (isolatedDtsContext) {
161
+ try {
162
+ await processIsolatedDts(isolatedDtsContext, {
163
+ logSuccess: !stats?.hasErrors()
164
+ });
165
+ promiseResult = {
166
+ status: 'success'
167
+ };
168
+ } catch (error) {
169
+ logger.error(error);
170
+ promiseResult = {
171
+ status: 'error',
172
+ errorMessage: `Error occurred in ${isolatedDtsContext.name} declaration files generation.`
173
+ };
174
+ }
175
+ return;
176
+ }
84
177
  promiseResult = await dtsPromise;
85
178
  },
86
179
  order: 'pre'
87
180
  });
88
181
  api.onAfterBuild(({ isFirstCompile })=>{
89
- if (!isFirstCompile) return;
182
+ if ('tsc' === dtsBackend && !isFirstCompile) return;
90
183
  if ('error' === promiseResult.status) {
91
184
  if (options.abortOnError) {
92
185
  const error = new Error(promiseResult.errorMessage);
@@ -0,0 +1,11 @@
1
+ import type { Rspack } from '@rsbuild/core';
2
+ import { type PreparedDtsContext } from './dts.js';
3
+ import type { DtsGenOptions } from './index.js';
4
+ export type IsolatedDtsContext = Omit<DtsGenOptions, 'dtsExtension'> & PreparedDtsContext & {
5
+ dtsExtension: string;
6
+ bundlerConfig: Rspack.Configuration | undefined;
7
+ };
8
+ export declare function createIsolatedDtsContext(dtsGenOptions: DtsGenOptions, bundlerConfig: Rspack.Configuration | undefined): Promise<IsolatedDtsContext>;
9
+ export declare function processIsolatedDts(isolatedDtsContext: IsolatedDtsContext, options?: {
10
+ logSuccess?: boolean;
11
+ }): Promise<void>;
package/dist/tsc.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { logger } from "@rsbuild/core";
2
- import { updateDeclarationMapContent, processDtsFiles, color, renameDtsFile, ts, getTimeCost } from "./350.js";
2
+ import { updateDeclarationMapContent, processDtsFiles, color, renameDtsFile, ts, getTimeCost } from "./981.js";
3
3
  const logPrefixTsc = color.dim('[tsc]');
4
4
  const formatHost = {
5
5
  getCanonicalFileName: (path)=>path,
package/dist/tsgo.js CHANGED
@@ -1,10 +1,9 @@
1
1
  import { spawn } from "node:child_process";
2
- import promises from "node:fs/promises";
3
2
  import { createRequire } from "node:module";
4
3
  import node_path from "node:path";
5
4
  import { pathToFileURL } from "node:url";
6
5
  import { logger } from "@rsbuild/core";
7
- import { updateDeclarationMapContent, processDtsFiles, color, renameDtsFile, globDtsFiles, getTimeCost } from "./350.js";
6
+ import { processDtsFiles, color, rewriteDtsExtensions, getTimeCost } from "./981.js";
8
7
  const tsgo_require = createRequire(import.meta.url);
9
8
  const logPrefixTsgo = color.dim('[tsgo]');
10
9
  const getTsgoBinPath = async ()=>{
@@ -30,21 +29,7 @@ const generateTsgoArgs = (configPath, declarationDir, build, isWatch)=>{
30
29
  return args;
31
30
  };
32
31
  async function handleDiagnosticsAndProcessFiles(isWatch, hasErrors, tsConfigResult, configPath, bundle, cwd, declarationDir, dtsExtension, redirect, rootDir, paths, banner, footer, name) {
33
- if (!bundle) {
34
- const dtsFiles = await globDtsFiles(cwd, declarationDir, [
35
- '/**/*.d.ts',
36
- '/**/*.d.ts.map'
37
- ]);
38
- await Promise.all(dtsFiles.map(async (file)=>{
39
- const contents = await promises.readFile(file, 'utf8');
40
- const newFileName = renameDtsFile(file, dtsExtension, bundle);
41
- const newContents = updateDeclarationMapContent(file, contents, dtsExtension, bundle, tsConfigResult.options.declarationMap);
42
- if (file !== newFileName || contents !== newContents) {
43
- await promises.writeFile(newFileName, newContents);
44
- await promises.unlink(file);
45
- }
46
- }));
47
- }
32
+ await rewriteDtsExtensions(cwd, declarationDir, dtsExtension, bundle, tsConfigResult.options.declarationMap);
48
33
  await processDtsFiles(bundle, cwd, declarationDir, dtsExtension, redirect, configPath, rootDir, paths, banner, footer);
49
34
  if (hasErrors && !isWatch) {
50
35
  const error = new Error(`Failed to generate declaration files. ${color.dim(`(${name})`)}`);
package/dist/utils.d.ts CHANGED
@@ -32,6 +32,7 @@ export declare function processSourceEntry(bundle: boolean, entryConfig: NonNull
32
32
  export declare function calcLongestCommonPath(absPaths: string[]): Promise<string | null>;
33
33
  export declare const globDtsFiles: (cwd: string, dir: string, patterns: string[]) => Promise<string[]>;
34
34
  export declare function cleanDtsFiles(cwd: string, dir: string): Promise<void>;
35
+ export declare function rewriteDtsExtensions(cwd: string, dir: string, dtsExtension: string, bundle: boolean, hasDeclarationMap?: boolean): Promise<void>;
35
36
  export declare function cleanTsBuildInfoFile(tsconfigPath: string, compilerOptions: CompilerOptions): Promise<void>;
36
37
  export declare function getDtsEmitPath(pathFromPlugin: string | undefined, declarationDir: string | undefined, distPathRoot: string): string;
37
38
  export declare function warnIfOutside(cwd: string, dir: string | undefined, label: string): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rsbuild-plugin-dts",
3
- "version": "0.21.4",
3
+ "version": "0.22.0",
4
4
  "description": "Rsbuild plugin that supports emitting declaration files for TypeScript.",
5
5
  "homepage": "https://rslib.rs",
6
6
  "bugs": {
@@ -30,12 +30,12 @@
30
30
  },
31
31
  "devDependencies": {
32
32
  "@microsoft/api-extractor": "^7.58.7",
33
- "@rsbuild/core": "~2.0.5",
34
- "@typescript/native-preview": "7.0.0-dev.20260503.1",
33
+ "@rsbuild/core": "~2.0.8",
34
+ "@typescript/native-preview": "7.0.0-dev.20260527.2",
35
35
  "magic-string": "^0.30.21",
36
36
  "prebundle": "1.6.4",
37
- "rsbuild-plugin-publint": "^0.3.4",
38
- "rslib": "npm:@rslib/core@0.21.3",
37
+ "rsbuild-plugin-publint": "^1.0.0",
38
+ "rslib": "npm:@rslib/core@0.21.5",
39
39
  "tinyglobby": "^0.2.16",
40
40
  "tsconfig-paths": "^4.2.0",
41
41
  "typescript": "^6.0.3",