Skip to content

Commit cf169fc

Browse files
authored
[node-core-library] Add and use useNodeJSResolver option on Import.resolvePackage (#5160)
* [node-core-library] Add `useNodeJSResolver` to Import * [heft] Use `useNodeJSResolver` * [rush] `useNodeJSResolver: true` in Import.resolvePackage * [heft-jest] `useNodeJSResolver: true` in Import.resolvePackage * [serverless-stack] `useNodeJSResolver: true` in Import.resolvePackage * [storybook] `useNodeJSResolver: true` in Import.resolvePackage --------- Co-authored-by: David Michon <[email protected]>
1 parent 184f1bc commit cf169fc

File tree

18 files changed

+131
-45
lines changed

18 files changed

+131
-45
lines changed

apps/heft/src/configuration/RigPackageResolver.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ export class RigPackageResolver implements IRigPackageResolver {
8585
try {
8686
const resolvedPackageFolder: string = Import.resolvePackage({
8787
packageName: toolPackageName,
88-
baseFolderPath: this._buildFolder
88+
baseFolderPath: this._buildFolder,
89+
// Use the built-in Node.js resolver so we share the cache
90+
useNodeJSResolver: true
8991
});
9092
terminal.writeVerboseLine(
9193
`Resolved ${JSON.stringify(toolPackageName)} as a direct devDependency of the project.`
@@ -117,7 +119,9 @@ export class RigPackageResolver implements IRigPackageResolver {
117119
try {
118120
const resolvedPackageFolder: string = Import.resolvePackage({
119121
packageName: toolPackageName,
120-
baseFolderPath: path.dirname(rigPackageJsonPath)
122+
baseFolderPath: path.dirname(rigPackageJsonPath),
123+
// Use the built-in Node.js resolver so we share the cache
124+
useNodeJSResolver: true
121125
});
122126
terminal.writeVerboseLine(
123127
`Resolved ${JSON.stringify(toolPackageName)} as a dependency of the ` +
@@ -138,7 +142,9 @@ export class RigPackageResolver implements IRigPackageResolver {
138142
try {
139143
const resolvedPackageFolder: string = Import.resolvePackage({
140144
packageName: toolPackageName,
141-
baseFolderPath: this._buildFolder
145+
baseFolderPath: this._buildFolder,
146+
// Use the built-in Node.js resolver so we share the cache
147+
useNodeJSResolver: true
142148
});
143149
terminal.writeVerboseLine(
144150
`Resolved ${JSON.stringify(toolPackageName)} from "${resolvedPackageFolder}".`

apps/heft/src/utilities/CoreConfigFiles.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,9 @@ export class CoreConfigFiles {
104104
return Import.resolvePackage({
105105
packageName: propertyValue,
106106
baseFolderPath: configurationFileDirectory,
107-
allowSelfReference: true
107+
allowSelfReference: true,
108+
// Use the built-in Node.js resolver so we share the cache
109+
useNodeJSResolver: true
108110
});
109111
}
110112
};

apps/rush/src/start-dev.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ function includePlugin(pluginName: string, pluginPackageName?: string): void {
2020
pluginName: pluginName,
2121
pluginPackageFolder: Import.resolvePackage({
2222
packageName: pluginPackageName,
23-
baseFolderPath: __dirname
23+
baseFolderPath: __dirname,
24+
useNodeJSResolver: true
2425
})
2526
});
2627
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@microsoft/rush",
5+
"comment": "Use `useNodeJSResolver: true` in `Import.resolvePackage` calls.",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@microsoft/rush"
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@rushstack/heft-jest-plugin",
5+
"comment": "Use `useNodeJSResolver: true` in `Import.resolvePackage` calls.",
6+
"type": "minor"
7+
}
8+
],
9+
"packageName": "@rushstack/heft-jest-plugin"
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@rushstack/heft-serverless-stack-plugin",
5+
"comment": "Use `useNodeJSResolver: true` in `Import.resolvePackage` calls.",
6+
"type": "minor"
7+
}
8+
],
9+
"packageName": "@rushstack/heft-serverless-stack-plugin"
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@rushstack/heft-storybook-plugin",
5+
"comment": "Use `useNodeJSResolver: true` in `Import.resolvePackage` calls.",
6+
"type": "minor"
7+
}
8+
],
9+
"packageName": "@rushstack/heft-storybook-plugin"
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@rushstack/heft",
5+
"comment": "Use `useNodeJSResolver: true` in `Import.resolvePackage` calls.",
6+
"type": "minor"
7+
}
8+
],
9+
"packageName": "@rushstack/heft"
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@rushstack/node-core-library",
5+
"comment": "Add `useNodeJSResolver` option to `Import.resolvePackage` to rely on the built-in `require.resolve` and share its cache.",
6+
"type": "minor"
7+
}
8+
],
9+
"packageName": "@rushstack/node-core-library"
10+
}

common/reviews/api/node-core-library.api.md

+1
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ export interface IImportResolvePackageAsyncOptions extends IImportResolveAsyncOp
398398
// @public
399399
export interface IImportResolvePackageOptions extends IImportResolveOptions {
400400
packageName: string;
401+
useNodeJSResolver?: boolean;
401402
}
402403

403404
// @public

heft-plugins/heft-jest-plugin/src/JestPlugin.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -949,7 +949,7 @@ export default class JestPlugin implements IHeftTaskPlugin<IJestPluginOptions> {
949949
const resolvedPackagePath: string =
950950
packageName === PLUGIN_PACKAGE_NAME
951951
? PLUGIN_PACKAGE_FOLDER
952-
: Import.resolvePackage({ baseFolderPath: configDir, packageName });
952+
: Import.resolvePackage({ baseFolderPath: configDir, packageName, useNodeJSResolver: true });
953953
// First entry is the entire match
954954
const restOfPath: string = path.normalize(
955955
'./' + propertyValue.slice(packageDirMatches[0].length)

heft-plugins/heft-jest-plugin/src/patches/jestWorkerPatch.ts

+12-3
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,22 @@ function applyPatch(): void {
4343
try {
4444
let contextFolder: string = __dirname;
4545
// Resolve the "@jest/core" package relative to Heft
46-
contextFolder = Import.resolvePackage({ packageName: '@jest/core', baseFolderPath: contextFolder });
46+
contextFolder = Import.resolvePackage({
47+
packageName: '@jest/core',
48+
baseFolderPath: contextFolder,
49+
useNodeJSResolver: true
50+
});
4751
// Resolve the "@jest/reporters" package relative to "@jest/core"
48-
contextFolder = Import.resolvePackage({ packageName: '@jest/reporters', baseFolderPath: contextFolder });
52+
contextFolder = Import.resolvePackage({
53+
packageName: '@jest/reporters',
54+
baseFolderPath: contextFolder,
55+
useNodeJSResolver: true
56+
});
4957
// Resolve the "jest-worker" package relative to "@jest/reporters"
5058
const jestWorkerFolder: string = Import.resolvePackage({
5159
packageName: 'jest-worker',
52-
baseFolderPath: contextFolder
60+
baseFolderPath: contextFolder,
61+
useNodeJSResolver: true
5362
});
5463

5564
const baseWorkerPoolPath: string = path.join(jestWorkerFolder, 'build/base/BaseWorkerPool.js');

heft-plugins/heft-serverless-stack-plugin/src/ServerlessStackPlugin.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ export default class ServerlessStackPlugin implements IHeftTaskPlugin {
8484
try {
8585
sstCliPackagePath = Import.resolvePackage({
8686
packageName: SST_CLI_PACKAGE_NAME,
87-
baseFolderPath: options.heftConfiguration.buildFolderPath
87+
baseFolderPath: options.heftConfiguration.buildFolderPath,
88+
useNodeJSResolver: true
8889
});
8990
} catch (e) {
9091
this._logger.emitError(

heft-plugins/heft-storybook-plugin/src/StorybookPlugin.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,8 @@ export default class StorybookPlugin implements IHeftTaskPlugin<IStorybookPlugin
314314
try {
315315
storykitFolderPath = Import.resolvePackage({
316316
packageName: storykitPackageName,
317-
baseFolderPath: heftConfiguration.buildFolderPath
317+
baseFolderPath: heftConfiguration.buildFolderPath,
318+
useNodeJSResolver: true
318319
});
319320
} catch (ex) {
320321
throw new Error(`The ${taskSession.taskName} task cannot start: ` + (ex as Error).message);
@@ -328,7 +329,8 @@ export default class StorybookPlugin implements IHeftTaskPlugin<IStorybookPlugin
328329
try {
329330
storyBookCliPackage = Import.resolvePackage({
330331
packageName: cliPackageName,
331-
baseFolderPath: storykitFolderPath
332+
baseFolderPath: storykitFolderPath,
333+
useNodeJSResolver: true
332334
});
333335
} catch (ex) {
334336
throw new Error(`The ${taskSession.taskName} task cannot start: ` + (ex as Error).message);

libraries/node-core-library/src/Import.ts

+31-29
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,15 @@ export interface IImportResolvePackageOptions extends IImportResolveOptions {
121121
* The package name to resolve. For example "\@rushstack/node-core-library"
122122
*/
123123
packageName: string;
124+
125+
/**
126+
* If true, then the module path will be resolved using Node.js's built-in resolution algorithm.
127+
*
128+
* @remarks
129+
* This allows reusing Node's built-in resolver cache.
130+
* This implies `allowSelfReference: true`. The passed `getRealPath` will only be used on `baseFolderPath`.
131+
*/
132+
useNodeJSResolver?: boolean;
124133
}
125134

126135
/**
@@ -409,7 +418,14 @@ export class Import {
409418
* and a system module is found, then its name is returned without any file path.
410419
*/
411420
public static resolvePackage(options: IImportResolvePackageOptions): string {
412-
const { packageName, includeSystemModules, baseFolderPath, allowSelfReference, getRealPath } = options;
421+
const {
422+
packageName,
423+
includeSystemModules,
424+
baseFolderPath,
425+
allowSelfReference,
426+
getRealPath,
427+
useNodeJSResolver
428+
} = options;
413429

414430
if (includeSystemModules && Import._builtInModules.has(packageName)) {
415431
return packageName;
@@ -427,20 +443,17 @@ export class Import {
427443
PackageName.parse(packageName); // Ensure the package name is valid and doesn't contain a path
428444

429445
try {
430-
// Append a slash to the package name to ensure `resolve.sync` doesn't attempt to return a system package
431-
const resolvedPath: string = Resolve.sync(`${packageName}/`, {
432-
basedir: normalizedRootPath,
433-
preserveSymlinks: false,
434-
packageFilter: (pkg: Resolve.PackageJSON, pkgFile: string, dir: string): Resolve.PackageJSON => {
435-
// Hardwire "main" to point to a file that is guaranteed to exist.
436-
// This helps resolve packages such as @types/node that have no entry point.
437-
// And then we can use path.dirname() below to locate the package folder,
438-
// even if the real entry point was in an subfolder with arbitrary nesting.
439-
pkg.main = 'package.json';
440-
return pkg;
441-
},
442-
realpathSync: getRealPath
443-
});
446+
const resolvedPath: string = useNodeJSResolver
447+
? require.resolve(`${packageName}/package.json`, {
448+
paths: [normalizedRootPath]
449+
})
450+
: // Append `/package.json` to ensure `resolve.sync` doesn't attempt to return a system package, and to avoid
451+
// having to mess with the `packageFilter` option.
452+
Resolve.sync(`${packageName}/package.json`, {
453+
basedir: normalizedRootPath,
454+
preserveSymlinks: false,
455+
realpathSync: getRealPath
456+
});
444457

445458
const packagePath: string = path.dirname(resolvedPath);
446459
return packagePath;
@@ -501,23 +514,12 @@ export class Import {
501514
: undefined;
502515

503516
Resolve.default(
504-
// Append a slash to the package name to ensure `resolve` doesn't attempt to return a system package
505-
`${packageName}/`,
517+
// Append `/package.json` to ensure `resolve` doesn't attempt to return a system package, and to avoid
518+
// having to mess with the `packageFilter` option.
519+
`${packageName}/package.json`,
506520
{
507521
basedir: normalizedRootPath,
508522
preserveSymlinks: false,
509-
packageFilter: (
510-
pkg: Resolve.PackageJSON,
511-
pkgFile: string,
512-
dir: string
513-
): Resolve.PackageJSON => {
514-
// Hardwire "main" to point to a file that is guaranteed to exist.
515-
// This helps resolve packages such as @types/node that have no entry point.
516-
// And then we can use path.dirname() below to locate the package folder,
517-
// even if the real entry point was in an subfolder with arbitrary nesting.
518-
pkg.main = 'package.json';
519-
return pkg;
520-
},
521523
realpath: realPathFn
522524
},
523525
(error: Error | null, resolvedPath?: string) => {

libraries/node-core-library/src/test/__snapshots__/Import.test.ts.snap

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ exports[`Import resolveModule includeSystemModules throws on an attempt to resol
1010
1111
exports[`Import resolvePackage allowSelfReference fails to resolve a path inside this package with allowSelfReference turned on 1`] = `"The package name \\"@rushstack/node-core-library/lib/Constants.js\\" contains an invalid character: \\"/\\""`;
1212
13-
exports[`Import resolvePackage allowSelfReference throws on an attempt to reference this package without allowSelfReference turned on 1`] = `"Cannot find package \\"@rushstack/node-core-library\\" from \\"<packageRoot>/lib/test\\": Error: Cannot find module '@rushstack/node-core-library/' from '<dirname>'."`;
13+
exports[`Import resolvePackage allowSelfReference throws on an attempt to reference this package without allowSelfReference turned on 1`] = `"Cannot find package \\"@rushstack/node-core-library\\" from \\"<packageRoot>/lib/test\\": Error: Cannot find module '@rushstack/node-core-library/package.json' from '<dirname>'."`;
1414
1515
exports[`Import resolvePackage fails to resolve a path inside a dependency 1`] = `"The package name \\"@rushstack/heft/lib/start.js\\" contains an invalid character: \\"/\\""`;
1616
1717
exports[`Import resolvePackage fails to resolve a path inside a dependency of a dependency 1`] = `"The package name \\"@rushstack/ts-command-line/lib/Constants.js\\" contains an invalid character: \\"/\\""`;
1818
1919
exports[`Import resolvePackage includeSystemModules throws on an attempt to resolve a non-existing path inside a system module with includeSystemModules turned on 1`] = `"The package name \\"fs/foo/bar\\" contains an invalid character: \\"/\\""`;
2020
21-
exports[`Import resolvePackage includeSystemModules throws on an attempt to resolve a system module without includeSystemModules turned on 1`] = `"Cannot find package \\"fs\\" from \\"<packageRoot>/lib/test\\": Error: Cannot find module 'fs/' from '<dirname>'."`;
21+
exports[`Import resolvePackage includeSystemModules throws on an attempt to resolve a system module without includeSystemModules turned on 1`] = `"Cannot find package \\"fs\\" from \\"<packageRoot>/lib/test\\": Error: Cannot find module 'fs/package.json' from '<dirname>'."`;

libraries/rush-lib/src/pluginFramework/PluginManager.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ export class PluginManager {
7272
pluginName: builtInPluginName,
7373
pluginPackageFolder: Import.resolvePackage({
7474
packageName: pluginPackageName,
75-
baseFolderPath: __dirname
75+
baseFolderPath: __dirname,
76+
useNodeJSResolver: true
7677
})
7778
});
7879
}

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ function generateLibFilesRecursively(options: {
5151
export async function runAsync(): Promise<void> {
5252
const rushLibFolder: string = Import.resolvePackage({
5353
baseFolderPath: __dirname,
54-
packageName: '@microsoft/rush-lib'
54+
packageName: '@microsoft/rush-lib',
55+
useNodeJSResolver: true
5556
});
5657

5758
const stubsTargetPath: string = path.resolve(__dirname, '../lib');

0 commit comments

Comments
 (0)