From 4ebdf5b1ae1858ffd66da317f8c177f75e9a6029 Mon Sep 17 00:00:00 2001 From: David Michon Date: Fri, 10 Feb 2023 15:16:06 -0800 Subject: [PATCH 1/4] [rush-sdk] Use rush-lib from parent process --- .../rush/rush-sdk-env_2023-02-10-23-15.json | 10 +++ common/reviews/api/rush-lib.api.md | 1 + libraries/rush-lib/package.json | 8 +- .../src/api/EnvironmentConfiguration.ts | 7 ++ libraries/rush-lib/src/api/Rush.ts | 2 + .../rush-lib/src/utilities/SetRushLibPath.ts | 12 +++ libraries/rush-sdk/src/index.ts | 32 ++++++- .../test/__snapshots__/script.test.ts.snap | 76 +++++++++++++++++ libraries/rush-sdk/src/test/script.test.ts | 83 ++++++++++++++++--- 9 files changed, 216 insertions(+), 15 deletions(-) create mode 100644 common/changes/@microsoft/rush/rush-sdk-env_2023-02-10-23-15.json create mode 100644 libraries/rush-lib/src/utilities/SetRushLibPath.ts create mode 100644 libraries/rush-sdk/src/test/__snapshots__/script.test.ts.snap diff --git a/common/changes/@microsoft/rush/rush-sdk-env_2023-02-10-23-15.json b/common/changes/@microsoft/rush/rush-sdk-env_2023-02-10-23-15.json new file mode 100644 index 00000000000..78282f100e6 --- /dev/null +++ b/common/changes/@microsoft/rush/rush-sdk-env_2023-02-10-23-15.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush", + "comment": "Add code path to @rushstack/rush-sdk for using the @microsoft/rush-lib version from a parent process.", + "type": "none" + } + ], + "packageName": "@microsoft/rush" +} \ No newline at end of file diff --git a/common/reviews/api/rush-lib.api.md b/common/reviews/api/rush-lib.api.md index 9aa7b02ce99..223255445a5 100644 --- a/common/reviews/api/rush-lib.api.md +++ b/common/reviews/api/rush-lib.api.md @@ -177,6 +177,7 @@ export enum EnvironmentVariableNames { RUSH_GIT_BINARY_PATH = "RUSH_GIT_BINARY_PATH", RUSH_GLOBAL_FOLDER = "RUSH_GLOBAL_FOLDER", RUSH_INVOKED_FOLDER = "RUSH_INVOKED_FOLDER", + RUSH_LIB_PATH = "RUSH_LIB_PATH", RUSH_PARALLELISM = "RUSH_PARALLELISM", RUSH_PNPM_STORE_PATH = "RUSH_PNPM_STORE_PATH", RUSH_PNPM_VERIFY_STORE_INTEGRITY = "RUSH_PNPM_VERIFY_STORE_INTEGRITY", diff --git a/libraries/rush-lib/package.json b/libraries/rush-lib/package.json index 2d9daf24009..5157e40d004 100644 --- a/libraries/rush-lib/package.json +++ b/libraries/rush-lib/package.json @@ -89,5 +89,11 @@ "publishOnlyDependencies": { "@rushstack/rush-amazon-s3-build-cache-plugin": "workspace:*", "@rushstack/rush-azure-storage-build-cache-plugin": "workspace:*" - } + }, + "sideEffects": [ + "lib-esnext/start-pnpm.js", + "lib-esnext/start.js", + "lib-esnext/startx.js", + "lib-esnext/utilities/SetRushLibPath.js" + ] } diff --git a/libraries/rush-lib/src/api/EnvironmentConfiguration.ts b/libraries/rush-lib/src/api/EnvironmentConfiguration.ts index 3bd511504b7..4ffab64b8e5 100644 --- a/libraries/rush-lib/src/api/EnvironmentConfiguration.ts +++ b/libraries/rush-lib/src/api/EnvironmentConfiguration.ts @@ -153,6 +153,12 @@ export enum EnvironmentVariableNames { */ RUSH_TAR_BINARY_PATH = 'RUSH_TAR_BINARY_PATH', + /** + * Explicitly specifies the path for the version of `\@microsoft/rush-lib` being executed. + * Will be set upon loading Rush. + */ + RUSH_LIB_PATH = 'RUSH_LIB_PATH', + /** * When Rush executes shell scripts, it sometimes changes the working directory to be a project folder or * the repository root folder. The original working directory (where the Rush command was invoked) is assigned @@ -441,6 +447,7 @@ export class EnvironmentConfiguration { break; case EnvironmentVariableNames.RUSH_INVOKED_FOLDER: + case EnvironmentVariableNames.RUSH_LIB_PATH: // Assigned by Rush itself break; diff --git a/libraries/rush-lib/src/api/Rush.ts b/libraries/rush-lib/src/api/Rush.ts index 5afe16a4738..3fcdc1cd221 100644 --- a/libraries/rush-lib/src/api/Rush.ts +++ b/libraries/rush-lib/src/api/Rush.ts @@ -10,6 +10,8 @@ import { PackageJsonLookup } from '@rushstack/node-core-library'; +import '../utilities/SetRushLibPath'; + import { RushCommandLineParser } from '../cli/RushCommandLineParser'; import { RushStartupBanner } from '../cli/RushStartupBanner'; import { RushXCommandLine } from '../cli/RushXCommandLine'; diff --git a/libraries/rush-lib/src/utilities/SetRushLibPath.ts b/libraries/rush-lib/src/utilities/SetRushLibPath.ts new file mode 100644 index 00000000000..ec18843e67b --- /dev/null +++ b/libraries/rush-lib/src/utilities/SetRushLibPath.ts @@ -0,0 +1,12 @@ +import { PackageJsonLookup } from '@rushstack/node-core-library'; + +import { EnvironmentVariableNames } from '../api/EnvironmentConfiguration'; + +if (!process.env[EnvironmentVariableNames.RUSH_LIB_PATH]) { + const rootDir: string | undefined = PackageJsonLookup.instance.tryGetPackageFolderFor(__dirname); + if (rootDir) { + // Route to the 'main' field of package.json + const rushLibIndex: string = require.resolve(rootDir, { paths: [] }); + process.env[EnvironmentVariableNames.RUSH_LIB_PATH] = rushLibIndex; + } +} diff --git a/libraries/rush-sdk/src/index.ts b/libraries/rush-sdk/src/index.ts index 3a05b4f1102..fff0233b61b 100644 --- a/libraries/rush-sdk/src/index.ts +++ b/libraries/rush-sdk/src/index.ts @@ -15,7 +15,7 @@ import { } from '@rushstack/node-core-library'; import type { SpawnSyncReturns } from 'child_process'; -const RUSH_LIB_NAME: string = '@microsoft/rush-lib'; +const RUSH_LIB_NAME: '@microsoft/rush-lib' = '@microsoft/rush-lib'; const verboseEnabled: boolean = typeof process !== 'undefined' && process.env.RUSH_SDK_DEBUG === '1'; const terminal: Terminal = new Terminal( @@ -28,6 +28,7 @@ type RushLibModuleType = Record; declare const global: NodeJS.Global & typeof globalThis & { ___rush___rushLibModule?: RushLibModuleType; + ___rush___rushLibModuleFromEnvironment?: RushLibModuleType; ___rush___rushLibModuleFromInstallAndRunRush?: RushLibModuleType; }; @@ -46,7 +47,9 @@ function _require(moduleName: string): TResult { // SCENARIO 1: Rush's PluginManager has initialized "rush-sdk" with Rush's own instance of rush-lib. // The Rush host process will assign "global.___rush___rushLibModule" before loading the plugin. let rushLibModule: RushLibModuleType | undefined = - global.___rush___rushLibModule || global.___rush___rushLibModuleFromInstallAndRunRush; + global.___rush___rushLibModule || + global.___rush___rushLibModuleFromEnvironment || + global.___rush___rushLibModuleFromInstallAndRunRush; let errorMessage: string = ''; // SCENARIO 2: The project importing "rush-sdk" has installed its own instance of "rush-lib" @@ -89,7 +92,30 @@ if (rushLibModule === undefined) { } } -// SCENARIO 3: A tool or script depends on "rush-sdk", and is meant to be used inside a monorepo folder. +// SCENARIO 3: A tool or script has been invoked as a child process by an instance of "rush-lib" and can use the +// version that invoked it. In this case, use process.env.RUSH_LIB_PATH to find "rush-lib". +if (rushLibModule === undefined) { + const rushLibVariable: 'RUSH_LIB_PATH' = 'RUSH_LIB_PATH'; + const rushLibPath: string | undefined = process.env[rushLibVariable]; + if (rushLibPath) { + terminal.writeVerboseLine( + `Try to load ${RUSH_LIB_NAME} from process.env.${rushLibVariable} from caller package` + ); + try { + rushLibModule = _require(rushLibPath); + } catch (error) { + terminal.writeVerboseLine(`Filed to load ${RUSH_LIB_NAME} via process.env.${rushLibVariable}`); + } + + if (rushLibModule !== undefined) { + // to track which scenario is active and how it got initialized. + global.___rush___rushLibModuleFromEnvironment = rushLibModule; + terminal.writeVerboseLine(`Loaded ${RUSH_LIB_NAME} from process.env.${rushLibVariable}`); + } + } +} + +// SCENARIO 4: A standalone tool or script depends on "rush-sdk", and is meant to be used inside a monorepo folder. // In this case, we can use install-run-rush.js to obtain the appropriate rush-lib version for the monorepo. if (rushLibModule === undefined) { try { diff --git a/libraries/rush-sdk/src/test/__snapshots__/script.test.ts.snap b/libraries/rush-sdk/src/test/__snapshots__/script.test.ts.snap new file mode 100644 index 00000000000..2425c8c2688 --- /dev/null +++ b/libraries/rush-sdk/src/test/__snapshots__/script.test.ts.snap @@ -0,0 +1,76 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`@rushstack/rush-sdk Should load via env when Rush has loaded (for child processes): stderr 1`] = `""`; + +exports[`@rushstack/rush-sdk Should load via env when Rush has loaded (for child processes): stdout 1`] = ` +"Try to load @microsoft/rush-lib from process.env.RUSH_LIB_PATH from caller package +Loaded @microsoft/rush-lib from process.env.RUSH_LIB_PATH +[ + '_rushSdk_loadInternalModule', + 'ApprovedPackagesConfiguration', + 'ApprovedPackagesItem', + 'ApprovedPackagesPolicy', + 'BuildCacheConfiguration', + 'BumpType', + 'ChangeManager', + 'CommonVersionsConfiguration', + 'CredentialCache', + 'DependencyType', + 'EnvironmentConfiguration', + 'EnvironmentVariableNames', + 'Event', + 'EventHooks', + 'ExperimentsConfiguration', + 'FileSystemBuildCacheProvider', + 'IndividualVersionPolicy', + 'LockStepVersionPolicy', + 'LookupByPath', + 'NpmOptionsConfiguration', + 'Operation', + 'OperationStatus', + 'PackageJsonDependency', + 'PackageJsonEditor', + 'PackageManager', + 'PackageManagerOptionsConfigurationBase', + 'PhasedCommandHooks', + 'PnpmOptionsConfiguration', + 'ProjectChangeAnalyzer', + 'RepoStateFile', + 'Rush', + 'RushConfiguration', + 'RushConfigurationProject', + 'RushConstants', + 'RushLifecycleHooks', + 'RushSession', + 'RushUserConfiguration', + 'VersionPolicy', + 'VersionPolicyConfiguration', + 'VersionPolicyDefinitionName', + 'YarnOptionsConfiguration', + '_LastInstallFlag', + '_OperationMetadataManager', + '_OperationStateFile', + '_RushGlobalFolder', + '_RushInternals' +]" +`; + +exports[`@rushstack/rush-sdk Should load via global (for plugins): stderr 1`] = `""`; + +exports[`@rushstack/rush-sdk Should load via global (for plugins): stdout 1`] = `"[ '_rushSdk_loadInternalModule', 'foo' ]"`; + +exports[`@rushstack/rush-sdk Should load via install-run (for standalone tools): stderr 1`] = `""`; + +exports[`@rushstack/rush-sdk Should load via install-run (for standalone tools): stdout 1`] = ` +"Trying to load @microsoft/rush-lib installed by install-run-rush +Loaded @microsoft/rush-lib installed by install-run-rush +[ '_rushSdk_loadInternalModule', 'foo' ]" +`; + +exports[`@rushstack/rush-sdk Should load via process.env.RUSH_LIB_PATH (for child processes): stderr 1`] = `""`; + +exports[`@rushstack/rush-sdk Should load via process.env.RUSH_LIB_PATH (for child processes): stdout 1`] = ` +"Try to load @microsoft/rush-lib from process.env.RUSH_LIB_PATH from caller package +Loaded @microsoft/rush-lib from process.env.RUSH_LIB_PATH +[ '_rushSdk_loadInternalModule', 'foo' ]" +`; diff --git a/libraries/rush-sdk/src/test/script.test.ts b/libraries/rush-sdk/src/test/script.test.ts index 78de0ca3843..ba37f69feb2 100644 --- a/libraries/rush-sdk/src/test/script.test.ts +++ b/libraries/rush-sdk/src/test/script.test.ts @@ -8,8 +8,70 @@ const mockRushLibPath: string = `${__dirname}/fixture/mock-rush-lib.js`; const coreLibPath: string = require.resolve('@rushstack/node-core-library'); -describe('used in script', () => { - it('should work when used in script', () => { +describe('@rushstack/rush-sdk', () => { + it('Should load via global (for plugins)', () => { + const result = Executable.spawnSync( + 'node', + [ + '-e', + ` +global.___rush___rushLibModule = { foo: 1 }; +console.log(Object.keys(require(${JSON.stringify(rushSdkPath)})));` + ], + { + currentWorkingDirectory: mockPackageFolder, + environment: { + ...process.env, + RUSH_SDK_DEBUG: '1' + } + } + ); + expect(result.stderr.trim()).toMatchSnapshot('stderr'); + expect(result.stdout.trim()).toMatchSnapshot('stdout'); + expect(result.status).toBe(0); + }); + + it('Should load via env when Rush has loaded (for child processes)', () => { + const result = Executable.spawnSync( + 'node', + [ + '-e', + ` +require('@microsoft/rush-lib'); +console.log(Object.keys(require(${JSON.stringify(rushSdkPath)})));` + ], + { + currentWorkingDirectory: mockPackageFolder, + environment: { + ...process.env, + RUSH_SDK_DEBUG: '1' + } + } + ); + expect(result.stderr.trim()).toMatchSnapshot('stderr'); + expect(result.stdout.trim()).toMatchSnapshot('stdout'); + expect(result.status).toBe(0); + }); + + it('Should load via process.env.RUSH_LIB_PATH (for child processes)', () => { + const result = Executable.spawnSync( + 'node', + ['-e', `console.log(Object.keys(require(${JSON.stringify(rushSdkPath)})));`], + { + currentWorkingDirectory: mockPackageFolder, + environment: { + ...process.env, + RUSH_SDK_DEBUG: '1', + RUSH_LIB_PATH: mockRushLibPath + } + } + ); + expect(result.stderr.trim()).toMatchSnapshot('stderr'); + expect(result.stdout.trim()).toMatchSnapshot('stdout'); + expect(result.status).toBe(0); + }); + + it('Should load via install-run (for standalone tools)', () => { const result = Executable.spawnSync( 'node', [ @@ -24,20 +86,19 @@ const mockResolveModule = (options) => { return originalResolveModule(options); } Import.resolveModule = mockResolveModule; -console.log(require(${JSON.stringify(rushSdkPath)})); +console.log(Object.keys(require(${JSON.stringify(rushSdkPath)}))); ` ], { - currentWorkingDirectory: mockPackageFolder + currentWorkingDirectory: mockPackageFolder, + environment: { + ...process.env, + RUSH_SDK_DEBUG: '1' + } } ); - expect(result.stderr.trim()).toMatchInlineSnapshot(`""`); - expect(result.stdout.trim()).toMatchInlineSnapshot(` -"{ - _rushSdk_loadInternalModule: [Function: _rushSdk_loadInternalModule], - foo: [Getter] -}" -`); + expect(result.stderr.trim()).toMatchSnapshot('stderr'); + expect(result.stdout.trim()).toMatchSnapshot('stdout'); expect(result.status).toBe(0); }); }); From 24dbcffdbb4fc5ddf06830be904317ad509a7468 Mon Sep 17 00:00:00 2001 From: David Michon Date: Fri, 10 Feb 2023 15:21:10 -0800 Subject: [PATCH 2/4] Update --- libraries/rush-sdk/src/test/script.test.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libraries/rush-sdk/src/test/script.test.ts b/libraries/rush-sdk/src/test/script.test.ts index ba37f69feb2..ffe209484a2 100644 --- a/libraries/rush-sdk/src/test/script.test.ts +++ b/libraries/rush-sdk/src/test/script.test.ts @@ -22,7 +22,8 @@ console.log(Object.keys(require(${JSON.stringify(rushSdkPath)})));` currentWorkingDirectory: mockPackageFolder, environment: { ...process.env, - RUSH_SDK_DEBUG: '1' + RUSH_SDK_DEBUG: '1', + RUSH_LIB_PATH: '' // Need to clear if invoked via Rush } } ); @@ -44,7 +45,8 @@ console.log(Object.keys(require(${JSON.stringify(rushSdkPath)})));` currentWorkingDirectory: mockPackageFolder, environment: { ...process.env, - RUSH_SDK_DEBUG: '1' + RUSH_SDK_DEBUG: '1', + RUSH_LIB_PATH: '' // Need to clear if invoked via Rush } } ); @@ -93,7 +95,8 @@ console.log(Object.keys(require(${JSON.stringify(rushSdkPath)}))); currentWorkingDirectory: mockPackageFolder, environment: { ...process.env, - RUSH_SDK_DEBUG: '1' + RUSH_SDK_DEBUG: '1', + RUSH_LIB_PATH: '' // Need to clear if invoked via Rush } } ); From dacf9363af316dfe13fea22beed9ecca007c159b Mon Sep 17 00:00:00 2001 From: David Michon Date: Fri, 10 Feb 2023 15:24:45 -0800 Subject: [PATCH 3/4] Update readme --- libraries/rush-sdk/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/rush-sdk/README.md b/libraries/rush-sdk/README.md index 6f1cc9bfdc1..11874b94af5 100644 --- a/libraries/rush-sdk/README.md +++ b/libraries/rush-sdk/README.md @@ -10,7 +10,9 @@ The **@rushstack/rush-sdk** package acts as a lightweight proxy for accessing th 2. When authoring unit tests for a Rush plugin, developers should add **@microsoft/rush-lib** to their **package.json** `devDependencies`. In this context, **@rushstack/rush-sdk** will resolve to that instance for testing purposes. -3. For scripts and tools that are designed to be used in a Rush monorepo, in the future **@rushstack/rush-sdk** will automatically invoke **install-run-rush.js** and load the local installation. This ensures that tools load a compatible version of the Rush engine for the given branch. Once this is implemented, **@rushstack/rush-sdk** can replace **@microsoft/rush-lib** entirely as the official API interface, with the latter serving as the underlying implementation. +3. For projects within a monorepo that use **@rushstack/rush-sdk** during their build process, child processes will use the version of Rush that invoked them. + +4. For scripts and tools that are designed to be used in a Rush monorepo, in the future **@rushstack/rush-sdk** will automatically invoke **install-run-rush.js** and load the local installation. This ensures that tools load a compatible version of the Rush engine for the given branch. Once this is implemented, **@rushstack/rush-sdk** can replace **@microsoft/rush-lib** entirely as the official API interface, with the latter serving as the underlying implementation. The **@rushstack/rush-sdk** API declarations are identical to the corresponding version of **@microsoft/rush-lib**. @@ -42,8 +44,8 @@ Example 2: How to import an internal API: // WARNING: INTERNAL APIS MAY CHANGE AT ANY TIME -- USE THIS AT YOUR OWN RISK: // Important: Since we're calling an internal API, we need to use the unbundled .d.ts files -// instead of the normal .d.ts rollup -import { RushConfiguration } from '@rushstack/rush-sdk/lib/'; +// instead of the normal .d.ts rollup, otherwise TypeScript will complain about a type mismatch. +import { RushConfiguration } from '@rushstack/rush-sdk/lib/index'; const config = RushConfiguration.loadFromDefaultLocation(); console.log(config.commonFolder); From 1625aa31e369445e9178b9e56c884298ec2ea5f4 Mon Sep 17 00:00:00 2001 From: David Michon Date: Thu, 16 Feb 2023 16:31:21 -0800 Subject: [PATCH 4/4] Apply suggestions from code review --- .../changes/@microsoft/rush/rush-sdk-env_2023-02-10-23-15.json | 2 +- libraries/rush-lib/src/api/EnvironmentConfiguration.ts | 2 +- libraries/rush-sdk/README.md | 2 +- libraries/rush-sdk/src/index.ts | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/common/changes/@microsoft/rush/rush-sdk-env_2023-02-10-23-15.json b/common/changes/@microsoft/rush/rush-sdk-env_2023-02-10-23-15.json index 78282f100e6..12dfee64831 100644 --- a/common/changes/@microsoft/rush/rush-sdk-env_2023-02-10-23-15.json +++ b/common/changes/@microsoft/rush/rush-sdk-env_2023-02-10-23-15.json @@ -2,7 +2,7 @@ "changes": [ { "packageName": "@microsoft/rush", - "comment": "Add code path to @rushstack/rush-sdk for using the @microsoft/rush-lib version from a parent process.", + "comment": "Add code path to @rushstack/rush-sdk for inheriting @microsoft/rush-lib location from a parent process via the RUSH_LIB_PATH environment variable.", "type": "none" } ], diff --git a/libraries/rush-lib/src/api/EnvironmentConfiguration.ts b/libraries/rush-lib/src/api/EnvironmentConfiguration.ts index 4ffab64b8e5..639e64b6e80 100644 --- a/libraries/rush-lib/src/api/EnvironmentConfiguration.ts +++ b/libraries/rush-lib/src/api/EnvironmentConfiguration.ts @@ -154,7 +154,7 @@ export enum EnvironmentVariableNames { RUSH_TAR_BINARY_PATH = 'RUSH_TAR_BINARY_PATH', /** - * Explicitly specifies the path for the version of `\@microsoft/rush-lib` being executed. + * Explicitly specifies the path for the version of `@microsoft/rush-lib` being executed. * Will be set upon loading Rush. */ RUSH_LIB_PATH = 'RUSH_LIB_PATH', diff --git a/libraries/rush-sdk/README.md b/libraries/rush-sdk/README.md index 11874b94af5..1cdb6b7e5c7 100644 --- a/libraries/rush-sdk/README.md +++ b/libraries/rush-sdk/README.md @@ -10,7 +10,7 @@ The **@rushstack/rush-sdk** package acts as a lightweight proxy for accessing th 2. When authoring unit tests for a Rush plugin, developers should add **@microsoft/rush-lib** to their **package.json** `devDependencies`. In this context, **@rushstack/rush-sdk** will resolve to that instance for testing purposes. -3. For projects within a monorepo that use **@rushstack/rush-sdk** during their build process, child processes will use the version of Rush that invoked them. +3. For projects within a monorepo that use **@rushstack/rush-sdk** during their build process, child processes will inherit the installation of Rush that invoked them. This is communicated using the `RUSH_LIB_PATH` environment variable. 4. For scripts and tools that are designed to be used in a Rush monorepo, in the future **@rushstack/rush-sdk** will automatically invoke **install-run-rush.js** and load the local installation. This ensures that tools load a compatible version of the Rush engine for the given branch. Once this is implemented, **@rushstack/rush-sdk** can replace **@microsoft/rush-lib** entirely as the official API interface, with the latter serving as the underlying implementation. diff --git a/libraries/rush-sdk/src/index.ts b/libraries/rush-sdk/src/index.ts index fff0233b61b..0407765841e 100644 --- a/libraries/rush-sdk/src/index.ts +++ b/libraries/rush-sdk/src/index.ts @@ -104,7 +104,8 @@ if (rushLibModule === undefined) { try { rushLibModule = _require(rushLibPath); } catch (error) { - terminal.writeVerboseLine(`Filed to load ${RUSH_LIB_NAME} via process.env.${rushLibVariable}`); + // Log this as a warning, since it is unexpected to define an incorrect value of the variable. + terminal.writeWarningLine(`Failed to load ${RUSH_LIB_NAME} via process.env.${rushLibVariable}`); } if (rushLibModule !== undefined) {