Skip to content

Commit 4a7bb3a

Browse files
committed
Move .d.ts files from lib-commonjs to lib-dts in rush-lib and rush-sdk
- Remove dTsFilesInputFolderName from DeepImportsPlugin config in rush-lib webpack - Update copyEmptyModules.js to only generate JS stubs without .d.ts copying - Refactor rush-sdk generate-stubs.ts to only create JS stubs - Add copy-files-plugin task to copy .d.ts files from rush-lib/lib-dts to rush-sdk/lib-dts - Declarations now live only in lib-dts, not duplicated in lib-commonjs
1 parent 88bcfbb commit 4a7bb3a

File tree

4 files changed

+67
-100
lines changed

4 files changed

+67
-100
lines changed

libraries/rush-lib/scripts/copyEmptyModules.js

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
const { FileSystem, Async, AsyncQueue } = require('@rushstack/node-core-library');
44

55
const JS_FILE_EXTENSION = '.js';
6-
const DTS_FILE_EXTENSION = '.d.ts';
76

87
module.exports = {
98
runAsync: async ({
@@ -15,17 +14,16 @@ module.exports = {
1514
// We're using a Webpack plugin called `@rushstack/webpack-deep-imports-plugin` to
1615
// examine all of the modules that are imported by the entrypoints (index, and the start* scripts)
1716
// to `rush-lib` and generate stub JS files in the `lib` folder that reference the original modules
18-
// in the webpack bundle. The plugin also copies the `.d.ts` files for those modules to the `lib` folder.
17+
// in the webpack bundle.
1918
//
2019
// A limitation of this approach is that only modules that contain runtime code end up in the Webpack
21-
// bundle, so only modules that contain runtime code get stubs and have their `.d.ts` files copied. This
20+
// bundle, so only modules that contain runtime code get stubs. This
2221
// creates a problem when a `.d.ts` file references a module that doesn't have runtime code (i.e. -
23-
// a `.d.ts` file that only contains types).
22+
// a `.ts` file that only contains types).
2423
//
2524
// This script looks through the `lib-intermediate-esm` folder for `.js` files that were produced by the TypeScript
2625
// compiler from `.ts` files that contain no runtime code and generates stub `.js` files for them in the
27-
// `lib` folder and copies the corresponding `.d.ts` files to the `lib`. This ensures that the `.d.ts`
28-
// files that end up in the `lib` folder don't have any unresolved imports. This is tested by the
26+
// `lib-commonjs` folder. This ensures that all modules have corresponding JS stubs. This is tested by the
2927
// `rush-lib-declaration-paths-test` project in the `build-tests`
3028

3129
function stripCommentsFromJsFile(jsFileText) {
@@ -44,7 +42,6 @@ module.exports = {
4442
}
4543

4644
const jsInFolderPath = `${buildFolderPath}/lib-intermediate-esm`;
47-
const dtsInFolderPath = `${buildFolderPath}/lib-dts`;
4845
const outCjsFolderPath = `${buildFolderPath}/lib-commonjs`;
4946
const emptyModuleBuffer = Buffer.from('module.exports = {};', 'utf8');
5047
const folderPathQueue = new AsyncQueue([undefined]);
@@ -70,15 +67,6 @@ module.exports = {
7067
await FileSystem.writeFileAsync(outJsPath, emptyModuleBuffer, {
7168
ensureFolderExists: true
7269
});
73-
74-
const relativeDtsPath =
75-
relativeItemPath.slice(0, -JS_FILE_EXTENSION.length) + DTS_FILE_EXTENSION;
76-
const inDtsPath = `${dtsInFolderPath}/${relativeDtsPath}`;
77-
const outDtsPath = `${outCjsFolderPath}/${relativeDtsPath}`;
78-
terminal.writeVerboseLine(`Copying ${inDtsPath} to ${outDtsPath}`);
79-
// We know this is a file, don't need the redundant checks in FileSystem.copyFileAsync
80-
const buffer = await FileSystem.readFileToBufferAsync(inDtsPath);
81-
await FileSystem.writeFileAsync(outDtsPath, buffer, { ensureFolderExists: true });
8270
}
8371
}
8472
}

libraries/rush-lib/webpack.config.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,7 @@ module.exports = () => {
100100
path: `${__dirname}/temp/build/webpack-dll/[name].json`,
101101
inFolderName: 'lib-intermediate-esm',
102102
outFolderName: 'lib-commonjs',
103-
pathsToIgnore: ['utilities/prompts/SearchListPrompt.js'],
104-
dTsFilesInputFolderName: 'lib-dts'
103+
pathsToIgnore: ['utilities/prompts/SearchListPrompt.js']
105104
})
106105
],
107106
{

libraries/rush-sdk/config/heft.json

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,18 @@
3131
"sourcePath": "./node_modules/@microsoft/rush-lib/dist",
3232
"includeGlobs": ["rush-lib.d.ts"],
3333
"destinationFolders": ["dist"]
34+
},
35+
{
36+
"sourcePath": "./node_modules/@microsoft/rush-lib/lib-dts",
37+
"destinationFolders": ["lib-dts"],
38+
"fileExtensions": [".d.ts"],
39+
"hardlink": true
3440
}
3541
]
3642
}
3743
}
3844
},
3945

40-
"typescript": {
41-
"taskDependencies": ["copy-rush-lib-types"]
42-
},
43-
4446
"webpack": {
4547
"taskDependencies": ["typescript"],
4648
"taskPlugin": {
@@ -49,7 +51,7 @@
4951
},
5052

5153
"generate-stubs": {
52-
"taskDependencies": ["typescript"],
54+
"taskDependencies": ["typescript", "copy-rush-lib-types"],
5355
"taskPlugin": {
5456
"pluginPackage": "@rushstack/heft",
5557
"pluginName": "run-script-plugin",

libraries/rush-sdk/src/generate-stubs.ts

Lines changed: 55 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -6,108 +6,89 @@ import * as path from 'node:path';
66
import type { IRunScriptOptions } from '@rushstack/heft';
77
import { Async, FileSystem, type FolderItem, Import, JsonFile, Path } from '@rushstack/node-core-library';
88

9-
interface IGenerateOptions {
9+
interface IGenerateJsStubsOptions {
1010
parentSourcePath: string;
11-
parentCjsTargetPath: string;
12-
parentDtsTargetPath: string;
11+
parentTargetPath: string;
1312
parentSrcImportPathWithSlash: string;
1413
libShimIndexPath: string;
1514
}
1615

17-
interface IFileTask {
18-
type: 'dts' | 'js';
19-
sourcePath: string;
16+
interface IJsStubTask {
17+
jsSourcePath: string;
2018
targetPath: string;
21-
srcImportPath?: string;
22-
shimPathLiteral?: string;
19+
srcImportPath: string;
20+
shimPathLiteral: string;
2321
}
2422

25-
async function* collectFileTasksAsync(options: IGenerateOptions): AsyncGenerator<IFileTask> {
26-
const {
27-
parentSourcePath,
28-
parentCjsTargetPath,
29-
parentDtsTargetPath,
30-
parentSrcImportPathWithSlash,
31-
libShimIndexPath
32-
} = options;
33-
const folderItems: FolderItem[] = await FileSystem.readFolderItemsAsync(options.parentSourcePath);
23+
/**
24+
* Walks rush-lib/lib-commonjs to collect JS stub tasks. These stubs redirect
25+
* require() calls through the rush-sdk shim so that the actual rush-lib module
26+
* is loaded at runtime.
27+
*/
28+
async function* collectJsStubTasksAsync(options: IGenerateJsStubsOptions): AsyncGenerator<IJsStubTask> {
29+
const { parentSourcePath, parentTargetPath, parentSrcImportPathWithSlash, libShimIndexPath } = options;
30+
const folderItems: FolderItem[] = await FileSystem.readFolderItemsAsync(parentSourcePath);
3431

3532
for (const folderItem of folderItems) {
3633
const itemName: string = folderItem.name;
3734
const sourcePath: string = `${parentSourcePath}/${itemName}`;
38-
const cjsTargetPath: string = `${parentCjsTargetPath}/${itemName}`;
39-
const dtsTargetPath: string = `${parentDtsTargetPath}/${itemName}`;
35+
const targetPath: string = `${parentTargetPath}/${itemName}`;
4036

4137
if (folderItem.isDirectory()) {
4238
// Ensure destination folder exists
43-
await FileSystem.ensureFolderAsync(cjsTargetPath);
39+
await FileSystem.ensureFolderAsync(targetPath);
4440
// Recursively yield tasks from subdirectory
45-
yield* collectFileTasksAsync({
41+
yield* collectJsStubTasksAsync({
4642
parentSourcePath: sourcePath,
47-
parentCjsTargetPath: cjsTargetPath,
48-
parentDtsTargetPath: dtsTargetPath,
43+
parentTargetPath: targetPath,
4944
parentSrcImportPathWithSlash: parentSrcImportPathWithSlash + itemName + '/',
5045
libShimIndexPath
5146
});
52-
} else if (folderItem.name.endsWith('.d.ts')) {
53-
yield {
54-
type: 'dts',
55-
sourcePath,
56-
targetPath: dtsTargetPath
57-
};
5847
} else if (folderItem.name.endsWith('.js')) {
5948
const srcImportPath: string = parentSrcImportPathWithSlash + path.parse(folderItem.name).name;
60-
const shimPath: string = path.relative(parentCjsTargetPath, libShimIndexPath);
49+
const shimPath: string = path.relative(parentTargetPath, libShimIndexPath);
6150
const shimPathLiteral: string = JSON.stringify(Path.convertToSlashes(shimPath));
6251

6352
yield {
64-
type: 'js',
65-
sourcePath,
66-
targetPath: cjsTargetPath,
53+
jsSourcePath: sourcePath,
54+
targetPath,
6755
srcImportPath,
6856
shimPathLiteral
6957
};
7058
}
7159
}
7260
}
7361

74-
async function processFileTaskAsync(task: IFileTask): Promise<void> {
75-
const { type, sourcePath, targetPath, srcImportPath, shimPathLiteral } = task;
76-
if (type === 'dts') {
77-
await FileSystem.copyFileAsync({
78-
sourcePath,
79-
destinationPath: targetPath
80-
});
81-
} else {
82-
const srcImportPathLiteral: string = JSON.stringify(srcImportPath);
83-
84-
let namedExportsAssignment: string = '';
85-
try {
86-
// Read the sidecar .exports.json file generated by DeepImportsPlugin to get module exports
87-
const exportsJsonPath: string = sourcePath.slice(0, -'.js'.length) + '.exports.json';
88-
const { moduleExports }: { moduleExports: string[] } = await JsonFile.loadAsync(exportsJsonPath);
89-
if (moduleExports.length > 0) {
90-
// Assign named exports after module.exports to ensure they're properly exposed for ESM imports
91-
namedExportsAssignment =
92-
'\n' + moduleExports.map((exportName) => `exports.${exportName} = _m.${exportName};`).join('\n');
93-
}
94-
} catch (e) {
95-
if (!FileSystem.isNotExistError(e)) {
96-
throw e;
97-
}
62+
async function processJsStubTaskAsync(task: IJsStubTask): Promise<void> {
63+
const { jsSourcePath, targetPath, srcImportPath, shimPathLiteral } = task;
64+
const srcImportPathLiteral: string = JSON.stringify(srcImportPath);
65+
66+
let namedExportsAssignment: string = '';
67+
try {
68+
// Read the sidecar .exports.json file generated by DeepImportsPlugin to get module exports
69+
const exportsJsonPath: string = jsSourcePath.slice(0, -'.js'.length) + '.exports.json';
70+
const { moduleExports }: { moduleExports: string[] } = await JsonFile.loadAsync(exportsJsonPath);
71+
if (moduleExports.length > 0) {
72+
// Assign named exports after module.exports to ensure they're properly exposed for ESM imports
73+
namedExportsAssignment =
74+
'\n' + moduleExports.map((exportName) => `exports.${exportName} = _m.${exportName};`).join('\n');
75+
}
76+
} catch (e) {
77+
if (!FileSystem.isNotExistError(e)) {
78+
throw e;
9879
}
99-
100-
await FileSystem.writeFileAsync(
101-
targetPath,
102-
// Example:
103-
// ```
104-
// const _m = require("../../../lib-shim/index")._rushSdk_loadInternalModule("logic/policy/GitEmailPolicy");
105-
// module.exports = _m;
106-
// exports.GitEmailPolicy = _m.GitEmailPolicy;
107-
// ```
108-
`const _m = require(${shimPathLiteral})._rushSdk_loadInternalModule(${srcImportPathLiteral});\nmodule.exports = _m;${namedExportsAssignment}\n`
109-
);
11080
}
81+
82+
await FileSystem.writeFileAsync(
83+
targetPath,
84+
// Example:
85+
// ```
86+
// const _m = require("../../../lib-shim/index")._rushSdk_loadInternalModule("logic/policy/GitEmailPolicy");
87+
// module.exports = _m;
88+
// exports.GitEmailPolicy = _m.GitEmailPolicy;
89+
// ```
90+
`const _m = require(${shimPathLiteral})._rushSdk_loadInternalModule(${srcImportPathLiteral});\nmodule.exports = _m;${namedExportsAssignment}\n`
91+
);
11192
}
11293

11394
// Entry point invoked by "runScript" action from config/heft.json
@@ -126,23 +107,20 @@ export async function runAsync(options: IRunScriptOptions): Promise<void> {
126107
});
127108

128109
const cjsStubsTargetPath: string = `${buildFolderPath}/lib-commonjs`;
129-
const dtsStubsTargetPath: string = `${buildFolderPath}/lib-dts`;
130-
terminal.writeLine(
131-
`generate-stubs: Generating stub files under ${cjsStubsTargetPath} and ${dtsStubsTargetPath}`
132-
);
110+
terminal.writeLine(`generate-stubs: Generating stub files under ${cjsStubsTargetPath}`);
133111

134112
// Ensure the target folder exists
135113
await FileSystem.ensureFolderAsync(cjsStubsTargetPath);
136114

137-
// Collect and process file tasks in parallel with controlled concurrency
138-
const tasks: AsyncGenerator<IFileTask> = collectFileTasksAsync({
115+
// Generate JS stubs from rush-lib/lib-commonjs (these redirect require() through the shim)
116+
// Note: .d.ts files are copied separately by the copy-rush-lib-types heft task from rush-lib/lib-dts
117+
const jsTasks: AsyncGenerator<IJsStubTask> = collectJsStubTasksAsync({
139118
parentSourcePath: `${rushLibFolder}/lib-commonjs`,
140-
parentCjsTargetPath: cjsStubsTargetPath,
141-
parentDtsTargetPath: dtsStubsTargetPath,
119+
parentTargetPath: cjsStubsTargetPath,
142120
parentSrcImportPathWithSlash: '',
143121
libShimIndexPath: `${buildFolderPath}/lib-shim/index.js`
144122
});
145-
await Async.forEachAsync(tasks, processFileTaskAsync, { concurrency: 50 });
123+
await Async.forEachAsync(jsTasks, processJsStubTaskAsync, { concurrency: 50 });
146124

147125
terminal.writeLine('generate-stubs: Completed successfully.');
148126
}

0 commit comments

Comments
 (0)