From d7aa2ede819355ccf20da0e5af8ee8de76e7cd98 Mon Sep 17 00:00:00 2001 From: zxfan Date: Wed, 4 Dec 2024 20:40:13 +0800 Subject: [PATCH 1/8] Support fallback syntax in .npmrc file --- ...back_syntax_in_npmrc_2024-12-04-13-22.json | 10 +++ .../rush-lib/src/utilities/npmrcUtilities.ts | 83 +++++++++++++------ .../src/utilities/test/npmrcUtilities.test.ts | 38 +++++++++ 3 files changed, 104 insertions(+), 27 deletions(-) create mode 100644 common/changes/@microsoft/rush/feature-support_fallback_syntax_in_npmrc_2024-12-04-13-22.json create mode 100644 libraries/rush-lib/src/utilities/test/npmrcUtilities.test.ts diff --git a/common/changes/@microsoft/rush/feature-support_fallback_syntax_in_npmrc_2024-12-04-13-22.json b/common/changes/@microsoft/rush/feature-support_fallback_syntax_in_npmrc_2024-12-04-13-22.json new file mode 100644 index 00000000000..312c8fbab59 --- /dev/null +++ b/common/changes/@microsoft/rush/feature-support_fallback_syntax_in_npmrc_2024-12-04-13-22.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush", + "comment": "Support fallback syntax in .npmrc file", + "type": "none" + } + ], + "packageName": "@microsoft/rush" +} \ No newline at end of file diff --git a/libraries/rush-lib/src/utilities/npmrcUtilities.ts b/libraries/rush-lib/src/utilities/npmrcUtilities.ts index e3bd9026e21..07c6731776d 100644 --- a/libraries/rush-lib/src/utilities/npmrcUtilities.ts +++ b/libraries/rush-lib/src/utilities/npmrcUtilities.ts @@ -22,27 +22,11 @@ export interface ILogger { // create a global _combinedNpmrc for cache purpose const _combinedNpmrcMap: Map = new Map(); -function _trimNpmrcFile(options: { - sourceNpmrcPath: string; - linesToPrepend?: string[]; - linesToAppend?: string[]; -}): string { - const { sourceNpmrcPath, linesToPrepend, linesToAppend } = options; - const combinedNpmrcFromCache: string | undefined = _combinedNpmrcMap.get(sourceNpmrcPath); - if (combinedNpmrcFromCache !== undefined) { - return combinedNpmrcFromCache; - } - let npmrcFileLines: string[] = []; - if (linesToPrepend) { - npmrcFileLines.push(...linesToPrepend); - } - if (fs.existsSync(sourceNpmrcPath)) { - npmrcFileLines.push(...fs.readFileSync(sourceNpmrcPath).toString().split('\n')); - } - if (linesToAppend) { - npmrcFileLines.push(...linesToAppend); - } - npmrcFileLines = npmrcFileLines.map((line) => (line || '').trim()); +export function addMissingEnvPrefix(line: string): string { + return '; MISSING ENVIRONMENT VARIABLE: ' + line +} + +export function trimNpmrcFileLines(npmrcFileLines: string[], env: NodeJS.ProcessEnv): string[] { const resultLines: string[] = []; // This finds environment variable tokens that look like "${VAR_NAME}" @@ -66,11 +50,30 @@ function _trimNpmrcFile(options: { const environmentVariables: string[] | null = line.match(expansionRegExp); if (environmentVariables) { for (const token of environmentVariables) { - // Remove the leading "${" and the trailing "}" from the token - const environmentVariableName: string = token.substring(2, token.length - 1); - - // Is the environment variable defined? - if (!process.env[environmentVariableName]) { + /** + * Remove the leading "${" and the trailing "}" from the token + * + * ${nameString} -> nameString + * ${nameString-fallbackString} -> name-fallbackString + * ${nameString:-fallbackString} -> name:-fallbackString + */ + const nameWithFallback: string = token.substring(2, token.length - 1); + + /** + * Get the environment variable name and fallback value. + * + * name fallback + * nameString -> nameString undefined + * nameString-fallbackString -> nameString fallbackString + * nameString:-fallbackString -> nameString fallbackString + */ + const matched: string[] | null = nameWithFallback.match(/^([^:-]+)(?:\:?-(.+))?$/); + // matched: [originStr, variableName, fallback] + const name: string = matched?.[1] ?? nameWithFallback; + const fallback: string | undefined = matched?.[2]; + + // Is the environment variable and fallback value defined. + if (!env[name] && !fallback) { // No, so trim this line lineShouldBeTrimmed = true; break; @@ -82,11 +85,37 @@ function _trimNpmrcFile(options: { if (lineShouldBeTrimmed) { // Example output: // "; MISSING ENVIRONMENT VARIABLE: //my-registry.com/npm/:_authToken=${MY_AUTH_TOKEN}" - resultLines.push('; MISSING ENVIRONMENT VARIABLE: ' + line); + resultLines.push(addMissingEnvPrefix(line)); } else { resultLines.push(line); } } + return resultLines; +} + +function _trimNpmrcFile(options: { + sourceNpmrcPath: string; + linesToPrepend?: string[]; + linesToAppend?: string[]; +}): string { + const { sourceNpmrcPath, linesToPrepend, linesToAppend } = options; + const combinedNpmrcFromCache: string | undefined = _combinedNpmrcMap.get(sourceNpmrcPath); + if (combinedNpmrcFromCache !== undefined) { + return combinedNpmrcFromCache; + } + let npmrcFileLines: string[] = []; + if (linesToPrepend) { + npmrcFileLines.push(...linesToPrepend); + } + if (fs.existsSync(sourceNpmrcPath)) { + npmrcFileLines.push(...fs.readFileSync(sourceNpmrcPath).toString().split('\n')); + } + if (linesToAppend) { + npmrcFileLines.push(...linesToAppend); + } + npmrcFileLines = npmrcFileLines.map((line) => (line || '').trim()); + + const resultLines: string[] = trimNpmrcFileLines(npmrcFileLines, process.env); const combinedNpmrc: string = resultLines.join('\n'); diff --git a/libraries/rush-lib/src/utilities/test/npmrcUtilities.test.ts b/libraries/rush-lib/src/utilities/test/npmrcUtilities.test.ts new file mode 100644 index 00000000000..499f316dd39 --- /dev/null +++ b/libraries/rush-lib/src/utilities/test/npmrcUtilities.test.ts @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { addMissingEnvPrefix, trimNpmrcFileLines } from '../npmrcUtilities'; + +describe('npmrcUtilities', () => { + it(trimNpmrcFileLines.name, () => { + // Normal + expect(trimNpmrcFileLines(['var1=${foo}'], {})).toEqual([addMissingEnvPrefix('var1=${foo}')]); + expect(trimNpmrcFileLines(['var1=${foo}'], { foo: 'test' })).toEqual(['var1=${foo}']); + expect(trimNpmrcFileLines(['var1=${foo-fallback_value}'], {})).toEqual(['var1=${foo-fallback_value}']); + expect(trimNpmrcFileLines(['var1=${foo-fallback_value}'], { foo: 'test' })).toEqual([ + 'var1=${foo-fallback_value}' + ]); + expect(trimNpmrcFileLines(['var1=${foo:-fallback_value}'], {})).toEqual(['var1=${foo:-fallback_value}']); + expect(trimNpmrcFileLines(['var1=${foo:-fallback_value}'], { foo: 'test' })).toEqual([ + 'var1=${foo:-fallback_value}' + ]); + + // Multiple environment variables + expect(trimNpmrcFileLines(['var1=${foo}-${bar}'], { foo: 'test' })).toEqual([addMissingEnvPrefix('var1=${foo}-${bar}')]); + expect(trimNpmrcFileLines(['var1=${foo}-${bar}'], { bar: 'test' })).toEqual([addMissingEnvPrefix('var1=${foo}-${bar}')]); + expect(trimNpmrcFileLines(['var1=${foo}-${bar}'], { foo: 'test', bar: 'test' })).toEqual(['var1=${foo}-${bar}']); + expect(trimNpmrcFileLines(['var1=${foo:-fallback_value}-${bar-fallback_value}'], {})).toEqual(['var1=${foo:-fallback_value}-${bar-fallback_value}']); + + // Multiline + expect(trimNpmrcFileLines(['var1=${foo}', 'var2=${bar}'], { foo: 'test' })).toEqual(['var1=${foo}', addMissingEnvPrefix('var2=${bar}')]); + expect(trimNpmrcFileLines(['var1=${foo}', 'var2=${bar}'], { foo: 'test', bar: 'test' })).toEqual(['var1=${foo}', 'var2=${bar}']); + expect(trimNpmrcFileLines(['var1=${foo}', 'var2=${bar-fallback_value}'], { foo: 'test' })).toEqual(['var1=${foo}', 'var2=${bar-fallback_value}']); + expect(trimNpmrcFileLines(['var1=${foo:-fallback_value}', 'var2=${bar-fallback_value}'], {})).toEqual(['var1=${foo:-fallback_value}', 'var2=${bar-fallback_value}']); + + // Malformed + expect(trimNpmrcFileLines(['var1=${foo_fallback_value}'], {})).toEqual([addMissingEnvPrefix('var1=${foo_fallback_value}')]); + expect(trimNpmrcFileLines(['var1=${foo:fallback_value}'], {})).toEqual([addMissingEnvPrefix('var1=${foo:fallback_value}')]); + expect(trimNpmrcFileLines(['var1=${foo:_fallback_value}'], {})).toEqual([addMissingEnvPrefix('var1=${foo:_fallback_value}')]); + expect(trimNpmrcFileLines(['var1=${foo'], {})).toEqual(['var1=${foo']); + }); +}); From 1b682b943b7aa832c5112c8e5fbb6e9130928fc8 Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Tue, 10 Dec 2024 14:56:00 -0500 Subject: [PATCH 2/8] Simplify diff. --- .../rush-lib/src/utilities/npmrcUtilities.ts | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/libraries/rush-lib/src/utilities/npmrcUtilities.ts b/libraries/rush-lib/src/utilities/npmrcUtilities.ts index 07c6731776d..839e6251290 100644 --- a/libraries/rush-lib/src/utilities/npmrcUtilities.ts +++ b/libraries/rush-lib/src/utilities/npmrcUtilities.ts @@ -22,8 +22,40 @@ export interface ILogger { // create a global _combinedNpmrc for cache purpose const _combinedNpmrcMap: Map = new Map(); +function _trimNpmrcFile(options: { + sourceNpmrcPath: string; + linesToPrepend?: string[]; + linesToAppend?: string[]; +}): string { + const { sourceNpmrcPath, linesToPrepend, linesToAppend } = options; + const combinedNpmrcFromCache: string | undefined = _combinedNpmrcMap.get(sourceNpmrcPath); + if (combinedNpmrcFromCache !== undefined) { + return combinedNpmrcFromCache; + } + let npmrcFileLines: string[] = []; + if (linesToPrepend) { + npmrcFileLines.push(...linesToPrepend); + } + if (fs.existsSync(sourceNpmrcPath)) { + npmrcFileLines.push(...fs.readFileSync(sourceNpmrcPath).toString().split('\n')); + } + if (linesToAppend) { + npmrcFileLines.push(...linesToAppend); + } + npmrcFileLines = npmrcFileLines.map((line) => (line || '').trim()); + + const resultLines: string[] = trimNpmrcFileLines(npmrcFileLines, process.env); + + const combinedNpmrc: string = resultLines.join('\n'); + + //save the cache + _combinedNpmrcMap.set(sourceNpmrcPath, combinedNpmrc); + + return combinedNpmrc; +} + export function addMissingEnvPrefix(line: string): string { - return '; MISSING ENVIRONMENT VARIABLE: ' + line + return '; MISSING ENVIRONMENT VARIABLE: ' + line; } export function trimNpmrcFileLines(npmrcFileLines: string[], env: NodeJS.ProcessEnv): string[] { @@ -93,38 +125,6 @@ export function trimNpmrcFileLines(npmrcFileLines: string[], env: NodeJS.Process return resultLines; } -function _trimNpmrcFile(options: { - sourceNpmrcPath: string; - linesToPrepend?: string[]; - linesToAppend?: string[]; -}): string { - const { sourceNpmrcPath, linesToPrepend, linesToAppend } = options; - const combinedNpmrcFromCache: string | undefined = _combinedNpmrcMap.get(sourceNpmrcPath); - if (combinedNpmrcFromCache !== undefined) { - return combinedNpmrcFromCache; - } - let npmrcFileLines: string[] = []; - if (linesToPrepend) { - npmrcFileLines.push(...linesToPrepend); - } - if (fs.existsSync(sourceNpmrcPath)) { - npmrcFileLines.push(...fs.readFileSync(sourceNpmrcPath).toString().split('\n')); - } - if (linesToAppend) { - npmrcFileLines.push(...linesToAppend); - } - npmrcFileLines = npmrcFileLines.map((line) => (line || '').trim()); - - const resultLines: string[] = trimNpmrcFileLines(npmrcFileLines, process.env); - - const combinedNpmrc: string = resultLines.join('\n'); - - //save the cache - _combinedNpmrcMap.set(sourceNpmrcPath, combinedNpmrc); - - return combinedNpmrc; -} - /** * As a workaround, copyAndTrimNpmrcFile() copies the .npmrc file to the target folder, and also trims * unusable lines from the .npmrc file. From f8516fdebad62ab843b39d8b4dc6ba64d3a23974 Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Tue, 10 Dec 2024 15:01:04 -0500 Subject: [PATCH 3/8] Simplify tests. --- .../rush-lib/src/utilities/npmrcUtilities.ts | 6 +- .../src/utilities/test/npmrcUtilities.test.ts | 122 ++++++++++++++---- 2 files changed, 100 insertions(+), 28 deletions(-) diff --git a/libraries/rush-lib/src/utilities/npmrcUtilities.ts b/libraries/rush-lib/src/utilities/npmrcUtilities.ts index 839e6251290..424793b6e63 100644 --- a/libraries/rush-lib/src/utilities/npmrcUtilities.ts +++ b/libraries/rush-lib/src/utilities/npmrcUtilities.ts @@ -54,10 +54,6 @@ function _trimNpmrcFile(options: { return combinedNpmrc; } -export function addMissingEnvPrefix(line: string): string { - return '; MISSING ENVIRONMENT VARIABLE: ' + line; -} - export function trimNpmrcFileLines(npmrcFileLines: string[], env: NodeJS.ProcessEnv): string[] { const resultLines: string[] = []; @@ -117,7 +113,7 @@ export function trimNpmrcFileLines(npmrcFileLines: string[], env: NodeJS.Process if (lineShouldBeTrimmed) { // Example output: // "; MISSING ENVIRONMENT VARIABLE: //my-registry.com/npm/:_authToken=${MY_AUTH_TOKEN}" - resultLines.push(addMissingEnvPrefix(line)); + resultLines.push('; MISSING ENVIRONMENT VARIABLE: ' + line); } else { resultLines.push(line); } diff --git a/libraries/rush-lib/src/utilities/test/npmrcUtilities.test.ts b/libraries/rush-lib/src/utilities/test/npmrcUtilities.test.ts index 499f316dd39..9a87a2eac6a 100644 --- a/libraries/rush-lib/src/utilities/test/npmrcUtilities.test.ts +++ b/libraries/rush-lib/src/utilities/test/npmrcUtilities.test.ts @@ -1,38 +1,114 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { addMissingEnvPrefix, trimNpmrcFileLines } from '../npmrcUtilities'; +import { trimNpmrcFileLines } from '../npmrcUtilities'; describe('npmrcUtilities', () => { it(trimNpmrcFileLines.name, () => { // Normal - expect(trimNpmrcFileLines(['var1=${foo}'], {})).toEqual([addMissingEnvPrefix('var1=${foo}')]); - expect(trimNpmrcFileLines(['var1=${foo}'], { foo: 'test' })).toEqual(['var1=${foo}']); - expect(trimNpmrcFileLines(['var1=${foo-fallback_value}'], {})).toEqual(['var1=${foo-fallback_value}']); - expect(trimNpmrcFileLines(['var1=${foo-fallback_value}'], { foo: 'test' })).toEqual([ - 'var1=${foo-fallback_value}' - ]); - expect(trimNpmrcFileLines(['var1=${foo:-fallback_value}'], {})).toEqual(['var1=${foo:-fallback_value}']); - expect(trimNpmrcFileLines(['var1=${foo:-fallback_value}'], { foo: 'test' })).toEqual([ - 'var1=${foo:-fallback_value}' - ]); + expect(trimNpmrcFileLines(['var1=${foo}'], {})).toMatchInlineSnapshot(` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}", +] +`); + expect(trimNpmrcFileLines(['var1=${foo}'], { foo: 'test' })).toMatchInlineSnapshot(` +Array [ + "var1=\${foo}", +] +`); + expect(trimNpmrcFileLines(['var1=${foo-fallback_value}'], {})).toMatchInlineSnapshot(` +Array [ + "var1=\${foo-fallback_value}", +] +`); + expect(trimNpmrcFileLines(['var1=${foo-fallback_value}'], { foo: 'test' })).toMatchInlineSnapshot(` +Array [ + "var1=\${foo-fallback_value}", +] +`); + expect(trimNpmrcFileLines(['var1=${foo:-fallback_value}'], {})).toMatchInlineSnapshot(` +Array [ + "var1=\${foo:-fallback_value}", +] +`); + expect(trimNpmrcFileLines(['var1=${foo:-fallback_value}'], { foo: 'test' })).toMatchInlineSnapshot(` +Array [ + "var1=\${foo:-fallback_value}", +] +`); // Multiple environment variables - expect(trimNpmrcFileLines(['var1=${foo}-${bar}'], { foo: 'test' })).toEqual([addMissingEnvPrefix('var1=${foo}-${bar}')]); - expect(trimNpmrcFileLines(['var1=${foo}-${bar}'], { bar: 'test' })).toEqual([addMissingEnvPrefix('var1=${foo}-${bar}')]); - expect(trimNpmrcFileLines(['var1=${foo}-${bar}'], { foo: 'test', bar: 'test' })).toEqual(['var1=${foo}-${bar}']); - expect(trimNpmrcFileLines(['var1=${foo:-fallback_value}-${bar-fallback_value}'], {})).toEqual(['var1=${foo:-fallback_value}-${bar-fallback_value}']); + expect(trimNpmrcFileLines(['var1=${foo}-${bar}'], { foo: 'test' })).toMatchInlineSnapshot(` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}-\${bar}", +] +`); + expect(trimNpmrcFileLines(['var1=${foo}-${bar}'], { bar: 'test' })).toMatchInlineSnapshot(` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}-\${bar}", +] +`); + expect(trimNpmrcFileLines(['var1=${foo}-${bar}'], { foo: 'test', bar: 'test' })).toMatchInlineSnapshot(` +Array [ + "var1=\${foo}-\${bar}", +] +`); + expect(trimNpmrcFileLines(['var1=${foo:-fallback_value}-${bar-fallback_value}'], {})) + .toMatchInlineSnapshot(` +Array [ + "var1=\${foo:-fallback_value}-\${bar-fallback_value}", +] +`); // Multiline - expect(trimNpmrcFileLines(['var1=${foo}', 'var2=${bar}'], { foo: 'test' })).toEqual(['var1=${foo}', addMissingEnvPrefix('var2=${bar}')]); - expect(trimNpmrcFileLines(['var1=${foo}', 'var2=${bar}'], { foo: 'test', bar: 'test' })).toEqual(['var1=${foo}', 'var2=${bar}']); - expect(trimNpmrcFileLines(['var1=${foo}', 'var2=${bar-fallback_value}'], { foo: 'test' })).toEqual(['var1=${foo}', 'var2=${bar-fallback_value}']); - expect(trimNpmrcFileLines(['var1=${foo:-fallback_value}', 'var2=${bar-fallback_value}'], {})).toEqual(['var1=${foo:-fallback_value}', 'var2=${bar-fallback_value}']); + expect(trimNpmrcFileLines(['var1=${foo}', 'var2=${bar}'], { foo: 'test' })).toMatchInlineSnapshot(` +Array [ + "var1=\${foo}", + "; MISSING ENVIRONMENT VARIABLE: var2=\${bar}", +] +`); + expect(trimNpmrcFileLines(['var1=${foo}', 'var2=${bar}'], { foo: 'test', bar: 'test' })) + .toMatchInlineSnapshot(` +Array [ + "var1=\${foo}", + "var2=\${bar}", +] +`); + expect(trimNpmrcFileLines(['var1=${foo}', 'var2=${bar-fallback_value}'], { foo: 'test' })) + .toMatchInlineSnapshot(` +Array [ + "var1=\${foo}", + "var2=\${bar-fallback_value}", +] +`); + expect(trimNpmrcFileLines(['var1=${foo:-fallback_value}', 'var2=${bar-fallback_value}'], {})) + .toMatchInlineSnapshot(` +Array [ + "var1=\${foo:-fallback_value}", + "var2=\${bar-fallback_value}", +] +`); // Malformed - expect(trimNpmrcFileLines(['var1=${foo_fallback_value}'], {})).toEqual([addMissingEnvPrefix('var1=${foo_fallback_value}')]); - expect(trimNpmrcFileLines(['var1=${foo:fallback_value}'], {})).toEqual([addMissingEnvPrefix('var1=${foo:fallback_value}')]); - expect(trimNpmrcFileLines(['var1=${foo:_fallback_value}'], {})).toEqual([addMissingEnvPrefix('var1=${foo:_fallback_value}')]); - expect(trimNpmrcFileLines(['var1=${foo'], {})).toEqual(['var1=${foo']); + expect(trimNpmrcFileLines(['var1=${foo_fallback_value}'], {})).toMatchInlineSnapshot(` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo_fallback_value}", +] +`); + expect(trimNpmrcFileLines(['var1=${foo:fallback_value}'], {})).toMatchInlineSnapshot(` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo:fallback_value}", +] +`); + expect(trimNpmrcFileLines(['var1=${foo:_fallback_value}'], {})).toMatchInlineSnapshot(` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo:_fallback_value}", +] +`); + expect(trimNpmrcFileLines(['var1=${foo'], {})).toMatchInlineSnapshot(` +Array [ + "var1=\${foo", +] +`); }); }); From f7e657428bc3e2302400cd341e4845488f7ed1e1 Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Tue, 10 Dec 2024 15:06:56 -0500 Subject: [PATCH 4/8] More minor cleanup. --- libraries/rush-lib/src/utilities/npmrcUtilities.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libraries/rush-lib/src/utilities/npmrcUtilities.ts b/libraries/rush-lib/src/utilities/npmrcUtilities.ts index 424793b6e63..a2f1eff4072 100644 --- a/libraries/rush-lib/src/utilities/npmrcUtilities.ts +++ b/libraries/rush-lib/src/utilities/npmrcUtilities.ts @@ -32,16 +32,20 @@ function _trimNpmrcFile(options: { if (combinedNpmrcFromCache !== undefined) { return combinedNpmrcFromCache; } + let npmrcFileLines: string[] = []; if (linesToPrepend) { npmrcFileLines.push(...linesToPrepend); } + if (fs.existsSync(sourceNpmrcPath)) { npmrcFileLines.push(...fs.readFileSync(sourceNpmrcPath).toString().split('\n')); } + if (linesToAppend) { npmrcFileLines.push(...linesToAppend); } + npmrcFileLines = npmrcFileLines.map((line) => (line || '').trim()); const resultLines: string[] = trimNpmrcFileLines(npmrcFileLines, process.env); @@ -97,11 +101,11 @@ export function trimNpmrcFileLines(npmrcFileLines: string[], env: NodeJS.Process */ const matched: string[] | null = nameWithFallback.match(/^([^:-]+)(?:\:?-(.+))?$/); // matched: [originStr, variableName, fallback] - const name: string = matched?.[1] ?? nameWithFallback; + const environmentVariableName: string = matched?.[1] ?? nameWithFallback; const fallback: string | undefined = matched?.[2]; // Is the environment variable and fallback value defined. - if (!env[name] && !fallback) { + if (!env[environmentVariableName] && !fallback) { // No, so trim this line lineShouldBeTrimmed = true; break; @@ -142,6 +146,7 @@ interface INpmrcTrimOptions { linesToPrepend?: string[]; linesToAppend?: string[]; } + function _copyAndTrimNpmrcFile(options: INpmrcTrimOptions): string { const { logger, sourceNpmrcPath, targetNpmrcPath, linesToPrepend, linesToAppend } = options; logger.info(`Transforming ${sourceNpmrcPath}`); // Verbose @@ -202,6 +207,7 @@ export function syncNpmrc(options: ISyncNpmrcOptions): string | undefined { if (!fs.existsSync(targetNpmrcFolder)) { fs.mkdirSync(targetNpmrcFolder, { recursive: true }); } + return _copyAndTrimNpmrcFile({ sourceNpmrcPath, targetNpmrcPath, From 27187246be988e6b0790cfa11bf9ada5683a2f06 Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Tue, 10 Dec 2024 15:07:32 -0500 Subject: [PATCH 5/8] fixup! More minor cleanup. --- libraries/rush-lib/src/utilities/npmrcUtilities.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/rush-lib/src/utilities/npmrcUtilities.ts b/libraries/rush-lib/src/utilities/npmrcUtilities.ts index a2f1eff4072..5be35c4ebac 100644 --- a/libraries/rush-lib/src/utilities/npmrcUtilities.ts +++ b/libraries/rush-lib/src/utilities/npmrcUtilities.ts @@ -122,6 +122,7 @@ export function trimNpmrcFileLines(npmrcFileLines: string[], env: NodeJS.Process resultLines.push(line); } } + return resultLines; } From e0d470635ec920c72512cfb23e98d8c50957fdab Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Tue, 10 Dec 2024 15:48:25 -0500 Subject: [PATCH 6/8] Add isPnpm property. --- ...rt_fallback_syntax_in_npmrc_2024-12-10-20-47.json | 10 ++++++++++ common/reviews/api/rush-lib.api.md | 1 + libraries/rush-lib/src/api/RushConfiguration.ts | 9 +++++++++ libraries/rush-lib/src/cli/RushXCommandLine.ts | 2 +- libraries/rush-lib/src/cli/actions/DeployAction.ts | 2 +- libraries/rush-lib/src/cli/actions/InstallAction.ts | 2 +- libraries/rush-lib/src/cli/actions/PublishAction.ts | 2 +- .../src/cli/scriptActions/PhasedScriptAction.ts | 5 +---- libraries/rush-lib/src/logic/Autoinstaller.ts | 2 +- .../rush-lib/src/logic/InstallManagerFactory.ts | 2 +- .../rush-lib/src/logic/ProjectChangeAnalyzer.ts | 6 ++---- libraries/rush-lib/src/logic/PurgeManager.ts | 2 +- libraries/rush-lib/src/logic/RepoStateFile.ts | 4 ++-- libraries/rush-lib/src/logic/SetupChecks.ts | 2 +- .../rush-lib/src/logic/StandardScriptUpdater.ts | 2 +- .../rush-lib/src/logic/base/BaseInstallManager.ts | 12 ++++++------ .../src/logic/installManager/InstallHelpers.ts | 4 ++-- .../src/logic/installManager/RushInstallManager.ts | 6 +++--- .../logic/installManager/WorkspaceInstallManager.ts | 11 ++++------- 19 files changed, 49 insertions(+), 37 deletions(-) create mode 100644 common/changes/@microsoft/rush/feature-support_fallback_syntax_in_npmrc_2024-12-10-20-47.json diff --git a/common/changes/@microsoft/rush/feature-support_fallback_syntax_in_npmrc_2024-12-10-20-47.json b/common/changes/@microsoft/rush/feature-support_fallback_syntax_in_npmrc_2024-12-10-20-47.json new file mode 100644 index 00000000000..ed724209996 --- /dev/null +++ b/common/changes/@microsoft/rush/feature-support_fallback_syntax_in_npmrc_2024-12-10-20-47.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush", + "comment": "Add an `.isPnpm` property to `RushConfiguration` that is set to true if the package manager for the Rush repo is PNPM.", + "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 2ca78be7635..2fe19c11b06 100644 --- a/common/reviews/api/rush-lib.api.md +++ b/common/reviews/api/rush-lib.api.md @@ -1222,6 +1222,7 @@ export class RushConfiguration { readonly gitTagSeparator: string | undefined; readonly gitVersionBumpCommitMessage: string | undefined; readonly hotfixChangeEnabled: boolean; + readonly isPnpm: boolean; static loadFromConfigurationFile(rushJsonFilename: string): RushConfiguration; // (undocumented) static loadFromDefaultLocation(options?: ITryFindRushJsonLocationOptions): RushConfiguration; diff --git a/libraries/rush-lib/src/api/RushConfiguration.ts b/libraries/rush-lib/src/api/RushConfiguration.ts index deee29cb4ae..feac52b0c18 100644 --- a/libraries/rush-lib/src/api/RushConfiguration.ts +++ b/libraries/rush-lib/src/api/RushConfiguration.ts @@ -251,6 +251,11 @@ export class RushConfiguration { */ public readonly packageManager!: PackageManagerName; + /** + * If true, the repository is using PNPM as its package manager. + */ + public readonly isPnpm!: boolean; + /** * {@inheritdoc PackageManager} * @@ -698,16 +703,20 @@ export class RushConfiguration { // TODO: Add an actual "packageManager" field in rush.json const packageManagerFields: string[] = []; + this.isPnpm = false; if (rushConfigurationJson.npmVersion) { this.packageManager = 'npm'; this.packageManagerOptions = this.npmOptions; packageManagerFields.push('npmVersion'); } + if (rushConfigurationJson.pnpmVersion) { this.packageManager = 'pnpm'; + this.isPnpm = true; this.packageManagerOptions = this.pnpmOptions; packageManagerFields.push('pnpmVersion'); } + if (rushConfigurationJson.yarnVersion) { this.packageManager = 'yarn'; this.packageManagerOptions = this.yarnOptions; diff --git a/libraries/rush-lib/src/cli/RushXCommandLine.ts b/libraries/rush-lib/src/cli/RushXCommandLine.ts index ee7ddad1115..a5a49f964fc 100644 --- a/libraries/rush-lib/src/cli/RushXCommandLine.ts +++ b/libraries/rush-lib/src/cli/RushXCommandLine.ts @@ -224,7 +224,7 @@ export class RushXCommandLine { }); const terminal: ITerminal = new Terminal(terminalProvider); - if (rushConfiguration?.packageManager === 'pnpm' && rushConfiguration?.experimentsConfiguration) { + if (rushConfiguration?.isPnpm && rushConfiguration?.experimentsConfiguration) { const { configuration: experiments } = rushConfiguration?.experimentsConfiguration; if (experiments?.usePnpmSyncForInjectedDependencies) { diff --git a/libraries/rush-lib/src/cli/actions/DeployAction.ts b/libraries/rush-lib/src/cli/actions/DeployAction.ts index c254184840e..5f67445dc8a 100644 --- a/libraries/rush-lib/src/cli/actions/DeployAction.ts +++ b/libraries/rush-lib/src/cli/actions/DeployAction.ts @@ -159,7 +159,7 @@ export class DeployAction extends BaseRushAction { } const projects: RushConfigurationProject[] = this.rushConfiguration.projects; - if (this.rushConfiguration.packageManager === 'pnpm') { + if (this.rushConfiguration.isPnpm) { const currentlyInstalledVariant: string | undefined = await this.rushConfiguration.getCurrentlyInstalledVariantAsync(); for (const project of projects) { diff --git a/libraries/rush-lib/src/cli/actions/InstallAction.ts b/libraries/rush-lib/src/cli/actions/InstallAction.ts index 945c12337ca..51835887cd0 100644 --- a/libraries/rush-lib/src/cli/actions/InstallAction.ts +++ b/libraries/rush-lib/src/cli/actions/InstallAction.ts @@ -49,7 +49,7 @@ export class InstallAction extends BaseInstallAction { description: `Only check the validity of the shrinkwrap file without performing an install.` }); - if (this.rushConfiguration?.packageManager === 'pnpm') { + if (this.rushConfiguration?.isPnpm) { this._resolutionOnlyParameter = this.defineFlagParameter({ parameterLongName: '--resolution-only', description: `Only perform dependency resolution, useful for ensuring peer dependendencies are up to date. Note that this flag is only supported when using the pnpm package manager.` diff --git a/libraries/rush-lib/src/cli/actions/PublishAction.ts b/libraries/rush-lib/src/cli/actions/PublishAction.ts index 12170823a0f..78f9ecec180 100644 --- a/libraries/rush-lib/src/cli/actions/PublishAction.ts +++ b/libraries/rush-lib/src/cli/actions/PublishAction.ts @@ -455,7 +455,7 @@ export class PublishAction extends BaseRushAction { args.push(`--access`, this._npmAccessLevel.value); } - if (this.rushConfiguration.packageManager === 'pnpm') { + if (this.rushConfiguration.isPnpm) { // PNPM 4.11.0 introduced a feature that may interrupt publishing and prompt the user for input. // See this issue for details: https://github.com/microsoft/rushstack/issues/1940 args.push('--no-git-checks'); diff --git a/libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts b/libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts index 6eed33d9d00..5824689d613 100644 --- a/libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts +++ b/libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts @@ -466,10 +466,7 @@ export class PhasedScriptAction extends BaseScriptAction { } const { configuration: experiments } = this.rushConfiguration.experimentsConfiguration; - if ( - this.rushConfiguration?.packageManager === 'pnpm' && - experiments?.usePnpmSyncForInjectedDependencies - ) { + if (this.rushConfiguration?.isPnpm && experiments?.usePnpmSyncForInjectedDependencies) { const { PnpmSyncCopyOperationPlugin } = await import( '../../logic/operations/PnpmSyncCopyOperationPlugin' ); diff --git a/libraries/rush-lib/src/logic/Autoinstaller.ts b/libraries/rush-lib/src/logic/Autoinstaller.ts index 3b04e6c0cca..db712c3e2a3 100644 --- a/libraries/rush-lib/src/logic/Autoinstaller.ts +++ b/libraries/rush-lib/src/logic/Autoinstaller.ts @@ -193,7 +193,7 @@ export class Autoinstaller { oldFileContents = FileSystem.readFile(this.shrinkwrapFilePath, { convertLineEndings: NewlineKind.Lf }); this._logIfConsoleOutputIsNotRestricted('Deleting ' + this.shrinkwrapFilePath); await FileSystem.deleteFileAsync(this.shrinkwrapFilePath); - if (this._rushConfiguration.packageManager === 'pnpm') { + if (this._rushConfiguration.isPnpm) { // Workaround for https://github.com/pnpm/pnpm/issues/1890 // // When "rush update-autoinstaller" is run, Rush deletes "common/autoinstallers/my-task/pnpm-lock.yaml" diff --git a/libraries/rush-lib/src/logic/InstallManagerFactory.ts b/libraries/rush-lib/src/logic/InstallManagerFactory.ts index 1bb46cb9d9d..f68de2d8c0c 100644 --- a/libraries/rush-lib/src/logic/InstallManagerFactory.ts +++ b/libraries/rush-lib/src/logic/InstallManagerFactory.ts @@ -17,7 +17,7 @@ export class InstallManagerFactory { options: IInstallManagerOptions ): Promise { if ( - rushConfiguration.packageManager === 'pnpm' && + rushConfiguration.isPnpm && rushConfiguration.pnpmOptions && rushConfiguration.pnpmOptions.useWorkspaces ) { diff --git a/libraries/rush-lib/src/logic/ProjectChangeAnalyzer.ts b/libraries/rush-lib/src/logic/ProjectChangeAnalyzer.ts index 6c820c9ca78..dbcd522c123 100644 --- a/libraries/rush-lib/src/logic/ProjectChangeAnalyzer.ts +++ b/libraries/rush-lib/src/logic/ProjectChangeAnalyzer.ts @@ -149,9 +149,7 @@ export class ProjectChangeAnalyzer { return new Set(rushConfiguration.projects); } - const { packageManager } = rushConfiguration; - - if (packageManager === 'pnpm') { + if (rushConfiguration.isPnpm) { const currentShrinkwrap: PnpmShrinkwrapFile | undefined = PnpmShrinkwrapFile.loadFromFile(fullShrinkwrapPath); @@ -256,7 +254,7 @@ export class ProjectChangeAnalyzer { // Include project shrinkwrap files as part of the computation const additionalRelativePathsToHash: string[] = []; const globalAdditionalFiles: string[] = []; - if (rushConfiguration.packageManager === 'pnpm') { + if (rushConfiguration.isPnpm) { await Async.forEachAsync(rushConfiguration.projects, async (project: RushConfigurationProject) => { const projectShrinkwrapFilePath: string = BaseProjectShrinkwrapFile.getFilePathForProject(project); if (!(await FileSystem.existsAsync(projectShrinkwrapFilePath))) { diff --git a/libraries/rush-lib/src/logic/PurgeManager.ts b/libraries/rush-lib/src/logic/PurgeManager.ts index 423d1ca388d..80081d21d1d 100644 --- a/libraries/rush-lib/src/logic/PurgeManager.ts +++ b/libraries/rush-lib/src/logic/PurgeManager.ts @@ -88,7 +88,7 @@ export class PurgeManager { ); if ( - this._rushConfiguration.packageManager === 'pnpm' && + this._rushConfiguration.isPnpm && this._rushConfiguration.pnpmOptions.pnpmStore === 'global' && this._rushConfiguration.pnpmOptions.pnpmStorePath ) { diff --git a/libraries/rush-lib/src/logic/RepoStateFile.ts b/libraries/rush-lib/src/logic/RepoStateFile.ts index 3bdff9898ed..3d875afd66c 100644 --- a/libraries/rush-lib/src/logic/RepoStateFile.ts +++ b/libraries/rush-lib/src/logic/RepoStateFile.ts @@ -162,7 +162,7 @@ export class RepoStateFile { // Only support saving the pnpm shrinkwrap hash if it was enabled const preventShrinkwrapChanges: boolean = - rushConfiguration.packageManager === 'pnpm' && + rushConfiguration.isPnpm && rushConfiguration.pnpmOptions && rushConfiguration.pnpmOptions.preventManualShrinkwrapChanges; if (preventShrinkwrapChanges) { @@ -200,7 +200,7 @@ export class RepoStateFile { this._modified = true; } - if (rushConfiguration.packageManager === 'pnpm' && rushConfiguration.subspacesFeatureEnabled) { + if (rushConfiguration.isPnpm && rushConfiguration.subspacesFeatureEnabled) { const packageJsonInjectedDependenciesHash: string | undefined = subspace.getPackageJsonInjectedDependenciesHash(variant); diff --git a/libraries/rush-lib/src/logic/SetupChecks.ts b/libraries/rush-lib/src/logic/SetupChecks.ts index f85a2386664..9f60b71708e 100644 --- a/libraries/rush-lib/src/logic/SetupChecks.ts +++ b/libraries/rush-lib/src/logic/SetupChecks.ts @@ -40,7 +40,7 @@ export class SetupChecks { private static _validate(rushConfiguration: RushConfiguration): string | undefined { // Check for outdated tools - if (rushConfiguration.packageManager === 'pnpm') { + if (rushConfiguration.isPnpm) { if (semver.lt(rushConfiguration.packageManagerToolVersion, MINIMUM_SUPPORTED_PNPM_VERSION)) { return ( `The ${RushConstants.rushJsonFilename} file requests PNPM version ` + diff --git a/libraries/rush-lib/src/logic/StandardScriptUpdater.ts b/libraries/rush-lib/src/logic/StandardScriptUpdater.ts index 79bc66b6128..5a4fda349c7 100644 --- a/libraries/rush-lib/src/logic/StandardScriptUpdater.ts +++ b/libraries/rush-lib/src/logic/StandardScriptUpdater.ts @@ -87,7 +87,7 @@ const _pnpmOnlyScripts: IScriptSpecifier[] = [ ]; const getScripts = (rushConfiguration: RushConfiguration): IScriptSpecifier[] => { - if (rushConfiguration.packageManager === 'pnpm') { + if (rushConfiguration.isPnpm) { return _scripts.concat(_pnpmOnlyScripts); } diff --git a/libraries/rush-lib/src/logic/base/BaseInstallManager.ts b/libraries/rush-lib/src/logic/base/BaseInstallManager.ts index c0d081cc23b..44b38b4e8d0 100644 --- a/libraries/rush-lib/src/logic/base/BaseInstallManager.ts +++ b/libraries/rush-lib/src/logic/base/BaseInstallManager.ts @@ -283,7 +283,7 @@ export abstract class BaseInstallManager { const { configuration: experiments } = this.rushConfiguration.experimentsConfiguration; // if usePnpmSyncForInjectedDependencies is true // the pnpm-sync will generate the pnpm-sync.json based on lockfile - if (this.rushConfiguration.packageManager === 'pnpm' && experiments?.usePnpmSyncForInjectedDependencies) { + if (this.rushConfiguration.isPnpm && experiments?.usePnpmSyncForInjectedDependencies) { const pnpmLockfilePath: string = subspace.getTempShrinkwrapFilename(); const dotPnpmFolder: string = `${subspace.getSubspaceTempFolderPath()}/node_modules/.pnpm`; @@ -401,7 +401,7 @@ export abstract class BaseInstallManager { // Add pnpm-config.json file to the potentially changed files list. potentiallyChangedFiles.push(subspace.getPnpmConfigFilePath()); - if (this.rushConfiguration.packageManager === 'pnpm') { + if (this.rushConfiguration.isPnpm) { // If the repo is using pnpmfile.js, consider that also const pnpmFileFilePath: string = subspace.getPnpmfilePath(variant); const pnpmFileExists: boolean = await FileSystem.existsAsync(pnpmFileFilePath); @@ -544,7 +544,7 @@ export abstract class BaseInstallManager { ? crypto.createHash('sha1').update(npmrcText).digest('hex') : undefined; - if (this.rushConfiguration.packageManager === 'pnpm') { + if (this.rushConfiguration.isPnpm) { // Copy the committed patches folder if using pnpm const commonTempPnpmPatchesFolder: string = `${subspace.getSubspaceTempFolderPath()}/${ RushConstants.pnpmPatchesFolderName @@ -599,7 +599,7 @@ export abstract class BaseInstallManager { // Shim support for pnpmfile in. // Additionally when in workspaces, the shim implements support for common versions. - if (this.rushConfiguration.packageManager === 'pnpm') { + if (this.rushConfiguration.isPnpm) { await PnpmfileConfiguration.writeCommonTempPnpmfileShimAsync( this.rushConfiguration, subspace.getSubspaceTempFolderPath(), @@ -837,7 +837,7 @@ ${gitLfsHookHandling} if (collectLogFile) { args.push('--verbose'); } - } else if (this.rushConfiguration.packageManager === 'pnpm') { + } else if (this.rushConfiguration.isPnpm) { // Only explicitly define the store path if `pnpmStore` is using the default, or has been set to // 'local'. If `pnpmStore` = 'global', then allow PNPM to use the system's default // path. In all cases, this will be overridden by RUSH_PNPM_STORE_PATH @@ -1116,7 +1116,7 @@ ${gitLfsHookHandling} // Otherwise delete the temporary file FileSystem.deleteFile(subspace.getTempShrinkwrapFilename()); - if (this.rushConfiguration.packageManager === 'pnpm') { + if (this.rushConfiguration.isPnpm) { // Workaround for https://github.com/pnpm/pnpm/issues/1890 // // When "rush update --full" is run, Rush deletes "common/temp/pnpm-lock.yaml" diff --git a/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts b/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts index f18605a61f3..40f3354879c 100644 --- a/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts +++ b/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts @@ -50,7 +50,7 @@ export class InstallHelpers { version: '0.0.0' }; - if (rushConfiguration.packageManager === 'pnpm') { + if (rushConfiguration.isPnpm) { const pnpmOptions: PnpmOptionsConfiguration = subspace.getPnpmOptions() || rushConfiguration.pnpmOptions; if (!commonPackageJson.pnpm) { @@ -132,7 +132,7 @@ export class InstallHelpers { if (rushConfiguration.npmOptions && rushConfiguration.npmOptions.environmentVariables) { configurationEnvironment = rushConfiguration.npmOptions.environmentVariables; } - } else if (rushConfiguration.packageManager === 'pnpm') { + } else if (rushConfiguration.isPnpm) { if (rushConfiguration.pnpmOptions && rushConfiguration.pnpmOptions.environmentVariables) { configurationEnvironment = rushConfiguration.pnpmOptions.environmentVariables; } diff --git a/libraries/rush-lib/src/logic/installManager/RushInstallManager.ts b/libraries/rush-lib/src/logic/installManager/RushInstallManager.ts index 5761b45c529..0c016ca3106 100644 --- a/libraries/rush-lib/src/logic/installManager/RushInstallManager.ts +++ b/libraries/rush-lib/src/logic/installManager/RushInstallManager.ts @@ -332,7 +332,7 @@ export class RushInstallManager extends BaseInstallManager { // with the shrinkwrap file, since these will cause install to fail. if ( shrinkwrapFile && - this.rushConfiguration.packageManager === 'pnpm' && + this.rushConfiguration.isPnpm && this.rushConfiguration.experimentsConfiguration.configuration.usePnpmFrozenLockfileForRushInstall ) { const pnpmShrinkwrapFile: PnpmShrinkwrapFile = shrinkwrapFile as PnpmShrinkwrapFile; @@ -361,7 +361,7 @@ export class RushInstallManager extends BaseInstallManager { } // Remove the workspace file if it exists - if (this.rushConfiguration.packageManager === 'pnpm') { + if (this.rushConfiguration.isPnpm) { const workspaceFilePath: string = path.join( this.rushConfiguration.commonTempFolder, 'pnpm-workspace.yaml' @@ -622,7 +622,7 @@ export class RushInstallManager extends BaseInstallManager { }, this.options.maxInstallAttempts, () => { - if (this.rushConfiguration.packageManager === 'pnpm') { + if (this.rushConfiguration.isPnpm) { // eslint-disable-next-line no-console console.log(Colorize.yellow(`Deleting the "node_modules" folder`)); this.installRecycler.moveFolder(commonNodeModulesFolder); diff --git a/libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts b/libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts index 86bb2e15fd3..768904e0cf3 100644 --- a/libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts +++ b/libraries/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts @@ -431,7 +431,7 @@ export class WorkspaceInstallManager extends BaseInstallManager { const potentiallyChangedFiles: string[] = []; - if (this.rushConfiguration.packageManager === 'pnpm') { + if (this.rushConfiguration.isPnpm) { // Add workspace file. This file is only modified when workspace packages change. const pnpmWorkspaceFilename: string = path.join( subspace.getSubspaceTempFolderPath(), @@ -568,7 +568,7 @@ export class WorkspaceInstallManager extends BaseInstallManager { }, this.options.maxInstallAttempts, () => { - if (this.rushConfiguration.packageManager === 'pnpm') { + if (this.rushConfiguration.isPnpm) { this._terminal.writeWarningLine(`Deleting the "node_modules" folder`); this.installRecycler.moveFolder(commonNodeModulesFolder); @@ -649,10 +649,7 @@ export class WorkspaceInstallManager extends BaseInstallManager { }, { concurrency: 10 } ); - } else if ( - this.rushConfiguration.packageManager === 'pnpm' && - this.rushConfiguration.pnpmOptions?.useWorkspaces - ) { + } else if (this.rushConfiguration.isPnpm && this.rushConfiguration.pnpmOptions?.useWorkspaces) { // If we're in PNPM workspace mode and PNPM didn't create a shrinkwrap file, // there are no dependencies. Generate empty shrinkwrap files for all projects. await Async.forEachAsync( @@ -749,7 +746,7 @@ export class WorkspaceInstallManager extends BaseInstallManager { super.pushConfigurationArgs(args, options, subspace); // Add workspace-specific args - if (this.rushConfiguration.packageManager === 'pnpm') { + if (this.rushConfiguration.isPnpm) { args.push('--recursive'); args.push('--link-workspace-packages', 'false'); From 92b5b4d11bd0d4d76bb8a23dcfeddbdaada9a5d0 Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Tue, 10 Dec 2024 15:50:47 -0500 Subject: [PATCH 7/8] Only support fallback for pnpm. --- .../rush-lib/src/cli/actions/PublishAction.ts | 7 +- libraries/rush-lib/src/logic/Autoinstaller.ts | 6 +- .../src/logic/base/BaseInstallManager.ts | 9 +- .../src/logic/setup/SetupPackageRegistry.ts | 3 +- libraries/rush-lib/src/scripts/install-run.ts | 6 +- libraries/rush-lib/src/utilities/Utilities.ts | 3 +- .../rush-lib/src/utilities/npmrcUtilities.ts | 84 ++++--- .../__snapshots__/npmrcUtilities.test.ts.snap | 225 ++++++++++++++++++ .../src/utilities/test/npmrcUtilities.test.ts | 188 +++++++-------- 9 files changed, 383 insertions(+), 148 deletions(-) create mode 100644 libraries/rush-lib/src/utilities/test/__snapshots__/npmrcUtilities.test.ts.snap diff --git a/libraries/rush-lib/src/cli/actions/PublishAction.ts b/libraries/rush-lib/src/cli/actions/PublishAction.ts index 78f9ecec180..1f3cf062a7b 100644 --- a/libraries/rush-lib/src/cli/actions/PublishAction.ts +++ b/libraries/rush-lib/src/cli/actions/PublishAction.ts @@ -239,7 +239,7 @@ export class PublishAction extends BaseRushAction { this._validate(); - this._addNpmPublishHome(); + this._addNpmPublishHome(this.rushConfiguration.isPnpm); const git: Git = new Git(this.rushConfiguration); const publishGit: PublishGit = new PublishGit(git, this._targetBranch.value); @@ -582,7 +582,7 @@ export class PublishAction extends BaseRushAction { } } - private _addNpmPublishHome(): void { + private _addNpmPublishHome(supportEnvVarFallbackSyntax: boolean): void { // Create "common\temp\publish-home" folder, if it doesn't exist Utilities.createFolderWithRetry(this._targetNpmrcPublishFolder); @@ -590,7 +590,8 @@ export class PublishAction extends BaseRushAction { Utilities.syncNpmrc({ sourceNpmrcFolder: this.rushConfiguration.commonRushConfigFolder, targetNpmrcFolder: this._targetNpmrcPublishFolder, - useNpmrcPublish: true + useNpmrcPublish: true, + supportEnvVarFallbackSyntax }); } diff --git a/libraries/rush-lib/src/logic/Autoinstaller.ts b/libraries/rush-lib/src/logic/Autoinstaller.ts index db712c3e2a3..8dd5b0e4dbf 100644 --- a/libraries/rush-lib/src/logic/Autoinstaller.ts +++ b/libraries/rush-lib/src/logic/Autoinstaller.ts @@ -137,7 +137,8 @@ export class Autoinstaller { // Copy: .../common/autoinstallers/my-task/.npmrc Utilities.syncNpmrc({ sourceNpmrcFolder: this._rushConfiguration.commonRushConfigFolder, - targetNpmrcFolder: autoinstallerFullPath + targetNpmrcFolder: autoinstallerFullPath, + supportEnvVarFallbackSyntax: this._rushConfiguration.isPnpm }); this._logIfConsoleOutputIsNotRestricted( @@ -222,7 +223,8 @@ export class Autoinstaller { Utilities.syncNpmrc({ sourceNpmrcFolder: this._rushConfiguration.commonRushConfigFolder, - targetNpmrcFolder: this.folderFullPath + targetNpmrcFolder: this.folderFullPath, + supportEnvVarFallbackSyntax: this._rushConfiguration.isPnpm }); await Utilities.executeCommandAsync({ diff --git a/libraries/rush-lib/src/logic/base/BaseInstallManager.ts b/libraries/rush-lib/src/logic/base/BaseInstallManager.ts index 44b38b4e8d0..dcf634229e8 100644 --- a/libraries/rush-lib/src/logic/base/BaseInstallManager.ts +++ b/libraries/rush-lib/src/logic/base/BaseInstallManager.ts @@ -536,7 +536,8 @@ export abstract class BaseInstallManager { sourceNpmrcFolder: subspace.getSubspaceConfigFolderPath(), targetNpmrcFolder: subspace.getSubspaceTempFolderPath(), linesToPrepend: extraNpmrcLines, - createIfMissing: this.rushConfiguration.subspacesFeatureEnabled + createIfMissing: this.rushConfiguration.subspacesFeatureEnabled, + supportEnvVarFallbackSyntax: this.rushConfiguration.isPnpm }); this._syncNpmrcAlreadyCalled = true; @@ -912,7 +913,8 @@ ${gitLfsHookHandling} */ const isAutoInstallPeersInNpmrc: boolean = isVariableSetInNpmrcFile( subspace.getSubspaceConfigFolderPath(), - 'auto-install-peers' + 'auto-install-peers', + this.rushConfiguration.isPnpm ); let autoInstallPeers: boolean | undefined = this.rushConfiguration.pnpmOptions.autoInstallPeers; @@ -940,7 +942,8 @@ ${gitLfsHookHandling} */ const isResolutionModeInNpmrc: boolean = isVariableSetInNpmrcFile( subspace.getSubspaceConfigFolderPath(), - 'resolution-mode' + 'resolution-mode', + this.rushConfiguration.isPnpm ); let resolutionMode: PnpmResolutionMode | undefined = this.rushConfiguration.pnpmOptions.resolutionMode; diff --git a/libraries/rush-lib/src/logic/setup/SetupPackageRegistry.ts b/libraries/rush-lib/src/logic/setup/SetupPackageRegistry.ts index ac6acbc2a43..07b06fc0e68 100644 --- a/libraries/rush-lib/src/logic/setup/SetupPackageRegistry.ts +++ b/libraries/rush-lib/src/logic/setup/SetupPackageRegistry.ts @@ -110,7 +110,8 @@ export class SetupPackageRegistry { if (!this._options.syncNpmrcAlreadyCalled) { Utilities.syncNpmrc({ sourceNpmrcFolder: this.rushConfiguration.commonRushConfigFolder, - targetNpmrcFolder: this.rushConfiguration.commonTempFolder + targetNpmrcFolder: this.rushConfiguration.commonTempFolder, + supportEnvVarFallbackSyntax: this.rushConfiguration.isPnpm }); } diff --git a/libraries/rush-lib/src/scripts/install-run.ts b/libraries/rush-lib/src/scripts/install-run.ts index f6332c0ebd7..775e87eb89e 100644 --- a/libraries/rush-lib/src/scripts/install-run.ts +++ b/libraries/rush-lib/src/scripts/install-run.ts @@ -169,7 +169,8 @@ function _resolvePackageVersion( syncNpmrc({ sourceNpmrcFolder, targetNpmrcFolder: rushTempFolder, - logger + logger, + supportEnvVarFallbackSyntax: false }); const npmPath: string = getNpmPath(); @@ -433,7 +434,8 @@ export function installAndRun( syncNpmrc({ sourceNpmrcFolder, targetNpmrcFolder: packageInstallFolder, - logger + logger, + supportEnvVarFallbackSyntax: false }); _createPackageJson(packageInstallFolder, packageName, packageVersion); diff --git a/libraries/rush-lib/src/utilities/Utilities.ts b/libraries/rush-lib/src/utilities/Utilities.ts index f4f8901b00c..d521640b543 100644 --- a/libraries/rush-lib/src/utilities/Utilities.ts +++ b/libraries/rush-lib/src/utilities/Utilities.ts @@ -531,7 +531,8 @@ export class Utilities { if (commonRushConfigFolder) { Utilities.syncNpmrc({ sourceNpmrcFolder: commonRushConfigFolder, - targetNpmrcFolder: directory + targetNpmrcFolder: directory, + supportEnvVarFallbackSyntax: false }); } diff --git a/libraries/rush-lib/src/utilities/npmrcUtilities.ts b/libraries/rush-lib/src/utilities/npmrcUtilities.ts index 5be35c4ebac..db15a0d17d4 100644 --- a/libraries/rush-lib/src/utilities/npmrcUtilities.ts +++ b/libraries/rush-lib/src/utilities/npmrcUtilities.ts @@ -22,12 +22,13 @@ export interface ILogger { // create a global _combinedNpmrc for cache purpose const _combinedNpmrcMap: Map = new Map(); -function _trimNpmrcFile(options: { - sourceNpmrcPath: string; - linesToPrepend?: string[]; - linesToAppend?: string[]; -}): string { - const { sourceNpmrcPath, linesToPrepend, linesToAppend } = options; +function _trimNpmrcFile( + options: Pick< + INpmrcTrimOptions, + 'sourceNpmrcPath' | 'linesToAppend' | 'linesToPrepend' | 'supportEnvVarFallbackSyntax' + > +): string { + const { sourceNpmrcPath, linesToPrepend, linesToAppend, supportEnvVarFallbackSyntax } = options; const combinedNpmrcFromCache: string | undefined = _combinedNpmrcMap.get(sourceNpmrcPath); if (combinedNpmrcFromCache !== undefined) { return combinedNpmrcFromCache; @@ -48,7 +49,7 @@ function _trimNpmrcFile(options: { npmrcFileLines = npmrcFileLines.map((line) => (line || '').trim()); - const resultLines: string[] = trimNpmrcFileLines(npmrcFileLines, process.env); + const resultLines: string[] = trimNpmrcFileLines(npmrcFileLines, process.env, supportEnvVarFallbackSyntax); const combinedNpmrc: string = resultLines.join('\n'); @@ -58,7 +59,18 @@ function _trimNpmrcFile(options: { return combinedNpmrc; } -export function trimNpmrcFileLines(npmrcFileLines: string[], env: NodeJS.ProcessEnv): string[] { +/** + * + * @param npmrcFileLines The npmrc file's lines + * @param env The environment variables object + * @param supportEnvVarFallbackSyntax Whether to support fallback values in the form of `${VAR_NAME:-fallback}` + * @returns + */ +export function trimNpmrcFileLines( + npmrcFileLines: string[], + env: NodeJS.ProcessEnv, + supportEnvVarFallbackSyntax: boolean +): string[] { const resultLines: string[] = []; // This finds environment variable tokens that look like "${VAR_NAME}" @@ -91,18 +103,24 @@ export function trimNpmrcFileLines(npmrcFileLines: string[], env: NodeJS.Process */ const nameWithFallback: string = token.substring(2, token.length - 1); - /** - * Get the environment variable name and fallback value. - * - * name fallback - * nameString -> nameString undefined - * nameString-fallbackString -> nameString fallbackString - * nameString:-fallbackString -> nameString fallbackString - */ - const matched: string[] | null = nameWithFallback.match(/^([^:-]+)(?:\:?-(.+))?$/); - // matched: [originStr, variableName, fallback] - const environmentVariableName: string = matched?.[1] ?? nameWithFallback; - const fallback: string | undefined = matched?.[2]; + let environmentVariableName: string; + let fallback: string | undefined; + if (supportEnvVarFallbackSyntax) { + /** + * Get the environment variable name and fallback value. + * + * name fallback + * nameString -> nameString undefined + * nameString-fallbackString -> nameString fallbackString + * nameString:-fallbackString -> nameString fallbackString + */ + const matched: string[] | null = nameWithFallback.match(/^([^:-]+)(?:\:?-(.+))?$/); + // matched: [originStr, variableName, fallback] + environmentVariableName = matched?.[1] ?? nameWithFallback; + fallback = matched?.[2]; + } else { + environmentVariableName = nameWithFallback; + } // Is the environment variable and fallback value defined. if (!env[environmentVariableName] && !fallback) { @@ -146,18 +164,15 @@ interface INpmrcTrimOptions { logger: ILogger; linesToPrepend?: string[]; linesToAppend?: string[]; + supportEnvVarFallbackSyntax: boolean; } function _copyAndTrimNpmrcFile(options: INpmrcTrimOptions): string { - const { logger, sourceNpmrcPath, targetNpmrcPath, linesToPrepend, linesToAppend } = options; + const { logger, sourceNpmrcPath, targetNpmrcPath } = options; logger.info(`Transforming ${sourceNpmrcPath}`); // Verbose logger.info(` --> "${targetNpmrcPath}"`); - const combinedNpmrc: string = _trimNpmrcFile({ - sourceNpmrcPath, - linesToPrepend, - linesToAppend - }); + const combinedNpmrc: string = _trimNpmrcFile(options); fs.writeFileSync(targetNpmrcPath, combinedNpmrc); @@ -176,12 +191,14 @@ function _copyAndTrimNpmrcFile(options: INpmrcTrimOptions): string { export interface ISyncNpmrcOptions { sourceNpmrcFolder: string; targetNpmrcFolder: string; + supportEnvVarFallbackSyntax: boolean; useNpmrcPublish?: boolean; logger?: ILogger; linesToPrepend?: string[]; linesToAppend?: string[]; createIfMissing?: boolean; } + export function syncNpmrc(options: ISyncNpmrcOptions): string | undefined { const { sourceNpmrcFolder, @@ -193,9 +210,7 @@ export function syncNpmrc(options: ISyncNpmrcOptions): string | undefined { // eslint-disable-next-line no-console error: console.error }, - createIfMissing = false, - linesToAppend, - linesToPrepend + createIfMissing = false } = options; const sourceNpmrcPath: string = path.join( sourceNpmrcFolder, @@ -213,8 +228,7 @@ export function syncNpmrc(options: ISyncNpmrcOptions): string | undefined { sourceNpmrcPath, targetNpmrcPath, logger, - linesToAppend, - linesToPrepend + ...options }); } else if (fs.existsSync(targetNpmrcPath)) { // If the source .npmrc doesn't exist and there is one in the target, delete the one in the target @@ -226,7 +240,11 @@ export function syncNpmrc(options: ISyncNpmrcOptions): string | undefined { } } -export function isVariableSetInNpmrcFile(sourceNpmrcFolder: string, variableKey: string): boolean { +export function isVariableSetInNpmrcFile( + sourceNpmrcFolder: string, + variableKey: string, + supportEnvVarFallbackSyntax: boolean +): boolean { const sourceNpmrcPath: string = `${sourceNpmrcFolder}/.npmrc`; //if .npmrc file does not exist, return false directly @@ -234,7 +252,7 @@ export function isVariableSetInNpmrcFile(sourceNpmrcFolder: string, variableKey: return false; } - const trimmedNpmrcFile: string = _trimNpmrcFile({ sourceNpmrcPath }); + const trimmedNpmrcFile: string = _trimNpmrcFile({ sourceNpmrcPath, supportEnvVarFallbackSyntax }); const variableKeyRegExp: RegExp = new RegExp(`^${variableKey}=`, 'm'); return trimmedNpmrcFile.match(variableKeyRegExp) !== null; diff --git a/libraries/rush-lib/src/utilities/test/__snapshots__/npmrcUtilities.test.ts.snap b/libraries/rush-lib/src/utilities/test/__snapshots__/npmrcUtilities.test.ts.snap new file mode 100644 index 00000000000..691864176a2 --- /dev/null +++ b/libraries/rush-lib/src/utilities/test/__snapshots__/npmrcUtilities.test.ts.snap @@ -0,0 +1,225 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a a variable without a fallback 1`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a a variable without a fallback 2`] = ` +Array [ + "var1=\${foo}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a variable with a fallback 1`] = ` +Array [ + "var1=\${foo-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a variable with a fallback 2`] = ` +Array [ + "var1=\${foo-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a variable with a fallback 3`] = ` +Array [ + "var1=\${foo:-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a variable with a fallback 4`] = ` +Array [ + "var1=\${foo:-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a variable with a fallback 5`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}-\${bar}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a variable with a fallback 6`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}-\${bar}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a variable with a fallback 7`] = ` +Array [ + "var1=\${foo}-\${bar}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a variable with a fallback 8`] = ` +Array [ + "var1=\${foo:-fallback_value}-\${bar-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports malformed lines 1`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo_fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports malformed lines 2`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo:fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports malformed lines 3`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo:_fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports malformed lines 4`] = ` +Array [ + "var1=\${foo", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports multiple lines 1`] = ` +Array [ + "var1=\${foo}", + "; MISSING ENVIRONMENT VARIABLE: var2=\${bar}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports multiple lines 2`] = ` +Array [ + "var1=\${foo}", + "var2=\${bar}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports multiple lines 3`] = ` +Array [ + "var1=\${foo}", + "var2=\${bar-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports multiple lines 4`] = ` +Array [ + "var1=\${foo:-fallback_value}", + "var2=\${bar-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a a variable without a fallback 1`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a a variable without a fallback 2`] = ` +Array [ + "var1=\${foo}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a variable with a fallback 1`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a variable with a fallback 2`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a variable with a fallback 3`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo:-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a variable with a fallback 4`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo:-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a variable with a fallback 5`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}-\${bar}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a variable with a fallback 6`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}-\${bar}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a variable with a fallback 7`] = ` +Array [ + "var1=\${foo}-\${bar}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a variable with a fallback 8`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo:-fallback_value}-\${bar-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports malformed lines 1`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo_fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports malformed lines 2`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo:fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports malformed lines 3`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo:_fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports malformed lines 4`] = ` +Array [ + "var1=\${foo", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports multiple lines 1`] = ` +Array [ + "var1=\${foo}", + "; MISSING ENVIRONMENT VARIABLE: var2=\${bar}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports multiple lines 2`] = ` +Array [ + "var1=\${foo}", + "var2=\${bar}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports multiple lines 3`] = ` +Array [ + "var1=\${foo}", + "; MISSING ENVIRONMENT VARIABLE: var2=\${bar-fallback_value}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports multiple lines 4`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo:-fallback_value}", + "; MISSING ENVIRONMENT VARIABLE: var2=\${bar-fallback_value}", +] +`; diff --git a/libraries/rush-lib/src/utilities/test/npmrcUtilities.test.ts b/libraries/rush-lib/src/utilities/test/npmrcUtilities.test.ts index 9a87a2eac6a..b889435a0f7 100644 --- a/libraries/rush-lib/src/utilities/test/npmrcUtilities.test.ts +++ b/libraries/rush-lib/src/utilities/test/npmrcUtilities.test.ts @@ -4,111 +4,93 @@ import { trimNpmrcFileLines } from '../npmrcUtilities'; describe('npmrcUtilities', () => { - it(trimNpmrcFileLines.name, () => { - // Normal - expect(trimNpmrcFileLines(['var1=${foo}'], {})).toMatchInlineSnapshot(` -Array [ - "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}", -] -`); - expect(trimNpmrcFileLines(['var1=${foo}'], { foo: 'test' })).toMatchInlineSnapshot(` -Array [ - "var1=\${foo}", -] -`); - expect(trimNpmrcFileLines(['var1=${foo-fallback_value}'], {})).toMatchInlineSnapshot(` -Array [ - "var1=\${foo-fallback_value}", -] -`); - expect(trimNpmrcFileLines(['var1=${foo-fallback_value}'], { foo: 'test' })).toMatchInlineSnapshot(` -Array [ - "var1=\${foo-fallback_value}", -] -`); - expect(trimNpmrcFileLines(['var1=${foo:-fallback_value}'], {})).toMatchInlineSnapshot(` -Array [ - "var1=\${foo:-fallback_value}", -] -`); - expect(trimNpmrcFileLines(['var1=${foo:-fallback_value}'], { foo: 'test' })).toMatchInlineSnapshot(` -Array [ - "var1=\${foo:-fallback_value}", -] -`); + function runTests(supportEnvVarFallbackSyntax: boolean): void { + it('handles empty input', () => { + expect(trimNpmrcFileLines([], {}, supportEnvVarFallbackSyntax)).toEqual([]); + }); - // Multiple environment variables - expect(trimNpmrcFileLines(['var1=${foo}-${bar}'], { foo: 'test' })).toMatchInlineSnapshot(` -Array [ - "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}-\${bar}", -] -`); - expect(trimNpmrcFileLines(['var1=${foo}-${bar}'], { bar: 'test' })).toMatchInlineSnapshot(` -Array [ - "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}-\${bar}", -] -`); - expect(trimNpmrcFileLines(['var1=${foo}-${bar}'], { foo: 'test', bar: 'test' })).toMatchInlineSnapshot(` -Array [ - "var1=\${foo}-\${bar}", -] -`); - expect(trimNpmrcFileLines(['var1=${foo:-fallback_value}-${bar-fallback_value}'], {})) - .toMatchInlineSnapshot(` -Array [ - "var1=\${foo:-fallback_value}-\${bar-fallback_value}", -] -`); + it('supports a a variable without a fallback', () => { + expect(trimNpmrcFileLines(['var1=${foo}'], {}, supportEnvVarFallbackSyntax)).toMatchSnapshot(); + expect( + trimNpmrcFileLines(['var1=${foo}'], { foo: 'test' }, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + }); - // Multiline - expect(trimNpmrcFileLines(['var1=${foo}', 'var2=${bar}'], { foo: 'test' })).toMatchInlineSnapshot(` -Array [ - "var1=\${foo}", - "; MISSING ENVIRONMENT VARIABLE: var2=\${bar}", -] -`); - expect(trimNpmrcFileLines(['var1=${foo}', 'var2=${bar}'], { foo: 'test', bar: 'test' })) - .toMatchInlineSnapshot(` -Array [ - "var1=\${foo}", - "var2=\${bar}", -] -`); - expect(trimNpmrcFileLines(['var1=${foo}', 'var2=${bar-fallback_value}'], { foo: 'test' })) - .toMatchInlineSnapshot(` -Array [ - "var1=\${foo}", - "var2=\${bar-fallback_value}", -] -`); - expect(trimNpmrcFileLines(['var1=${foo:-fallback_value}', 'var2=${bar-fallback_value}'], {})) - .toMatchInlineSnapshot(` -Array [ - "var1=\${foo:-fallback_value}", - "var2=\${bar-fallback_value}", -] -`); + it('supports a variable with a fallback', () => { + expect( + trimNpmrcFileLines(['var1=${foo-fallback_value}'], {}, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines(['var1=${foo-fallback_value}'], { foo: 'test' }, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines(['var1=${foo:-fallback_value}'], {}, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines(['var1=${foo:-fallback_value}'], { foo: 'test' }, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines(['var1=${foo}-${bar}'], { foo: 'test' }, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines(['var1=${foo}-${bar}'], { bar: 'test' }, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines(['var1=${foo}-${bar}'], { foo: 'test', bar: 'test' }, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines( + ['var1=${foo:-fallback_value}-${bar-fallback_value}'], + {}, + supportEnvVarFallbackSyntax + ) + ).toMatchSnapshot(); + }); - // Malformed - expect(trimNpmrcFileLines(['var1=${foo_fallback_value}'], {})).toMatchInlineSnapshot(` -Array [ - "; MISSING ENVIRONMENT VARIABLE: var1=\${foo_fallback_value}", -] -`); - expect(trimNpmrcFileLines(['var1=${foo:fallback_value}'], {})).toMatchInlineSnapshot(` -Array [ - "; MISSING ENVIRONMENT VARIABLE: var1=\${foo:fallback_value}", -] -`); - expect(trimNpmrcFileLines(['var1=${foo:_fallback_value}'], {})).toMatchInlineSnapshot(` -Array [ - "; MISSING ENVIRONMENT VARIABLE: var1=\${foo:_fallback_value}", -] -`); - expect(trimNpmrcFileLines(['var1=${foo'], {})).toMatchInlineSnapshot(` -Array [ - "var1=\${foo", -] -`); + it('supports multiple lines', () => { + expect( + trimNpmrcFileLines(['var1=${foo}', 'var2=${bar}'], { foo: 'test' }, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines( + ['var1=${foo}', 'var2=${bar}'], + { foo: 'test', bar: 'test' }, + supportEnvVarFallbackSyntax + ) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines( + ['var1=${foo}', 'var2=${bar-fallback_value}'], + { foo: 'test' }, + supportEnvVarFallbackSyntax + ) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines( + ['var1=${foo:-fallback_value}', 'var2=${bar-fallback_value}'], + {}, + supportEnvVarFallbackSyntax + ) + ).toMatchSnapshot(); + }); + + it('supports malformed lines', () => { + // Malformed + expect( + trimNpmrcFileLines(['var1=${foo_fallback_value}'], {}, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines(['var1=${foo:fallback_value}'], {}, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect( + trimNpmrcFileLines(['var1=${foo:_fallback_value}'], {}, supportEnvVarFallbackSyntax) + ).toMatchSnapshot(); + expect(trimNpmrcFileLines(['var1=${foo'], {}, supportEnvVarFallbackSyntax)).toMatchSnapshot(); + }); + } + + describe(trimNpmrcFileLines.name, () => { + describe('With support for env var fallback syntax', () => runTests(true)); + describe('Without support for env var fallback syntax', () => runTests(false)); }); }); From 29bbba703993085623c546b90b81104574588400 Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Tue, 10 Dec 2024 12:54:18 -0800 Subject: [PATCH 8/8] Rush change. --- ...ature-support_fallback_syntax_in_npmrc_2024-12-04-13-22.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/changes/@microsoft/rush/feature-support_fallback_syntax_in_npmrc_2024-12-04-13-22.json b/common/changes/@microsoft/rush/feature-support_fallback_syntax_in_npmrc_2024-12-04-13-22.json index 312c8fbab59..c1ed42db1e7 100644 --- a/common/changes/@microsoft/rush/feature-support_fallback_syntax_in_npmrc_2024-12-04-13-22.json +++ b/common/changes/@microsoft/rush/feature-support_fallback_syntax_in_npmrc_2024-12-04-13-22.json @@ -2,7 +2,7 @@ "changes": [ { "packageName": "@microsoft/rush", - "comment": "Support fallback syntax in .npmrc file", + "comment": "Support fallback syntax in `.npmrc` files if the package manager is PNPM. See https://pnpm.io/npmrc", "type": "none" } ],