diff --git a/common/changes/@microsoft/rush-lib/filter-npmrc-properties_2026-01-26-23-12.json b/common/changes/@microsoft/rush-lib/filter-npmrc-properties_2026-01-26-23-12.json new file mode 100644 index 0000000000..54bb3a29b1 --- /dev/null +++ b/common/changes/@microsoft/rush-lib/filter-npmrc-properties_2026-01-26-23-12.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "Filter npm-incompatible properties from .npmrc when npm is used with a configuration intended for pnpm or yarn, to eliminate spurious warnings during package manager installation.", + "type": "none", + "packageName": "@microsoft/rush" + } + ], + "packageName": "@microsoft/rush", + "email": "copilot@github.com" +} diff --git a/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts b/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts index 35a1998c6a..515f9e5bec 100644 --- a/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts +++ b/libraries/rush-lib/src/logic/installManager/InstallHelpers.ts @@ -257,7 +257,10 @@ export class InstallHelpers { // In particular, we'll assume that two different NPM registries cannot have two // different implementations of the same version of the same package. // This was needed for: https://github.com/microsoft/rushstack/issues/691 - commonRushConfigFolder: rushConfiguration.commonRushConfigFolder + commonRushConfigFolder: rushConfiguration.commonRushConfigFolder, + // Only filter npm-incompatible properties when the repo uses pnpm or yarn. + // If the repo uses npm, the .npmrc is already configured for npm, so don't filter. + filterNpmIncompatibleProperties: rushConfiguration.packageManager !== 'npm' }); logIfConsoleOutputIsNotRestricted( diff --git a/libraries/rush-lib/src/scripts/install-run.ts b/libraries/rush-lib/src/scripts/install-run.ts index 2aec0b67d1..7f56856648 100644 --- a/libraries/rush-lib/src/scripts/install-run.ts +++ b/libraries/rush-lib/src/scripts/install-run.ts @@ -173,7 +173,10 @@ function _resolvePackageVersion( sourceNpmrcFolder, targetNpmrcFolder: rushTempFolder, logger, - supportEnvVarFallbackSyntax: false + supportEnvVarFallbackSyntax: false, + // Always filter npm-incompatible properties in install-run scripts. + // Any warnings will be shown when running Rush commands directly. + filterNpmIncompatibleProperties: true }); // This returns something that looks like: @@ -459,7 +462,10 @@ export function installAndRun( sourceNpmrcFolder, targetNpmrcFolder: packageInstallFolder, logger, - supportEnvVarFallbackSyntax: false + supportEnvVarFallbackSyntax: false, + // Always filter npm-incompatible properties in install-run scripts. + // Any warnings will be shown when running Rush commands directly. + filterNpmIncompatibleProperties: true }); _createPackageJson(packageInstallFolder, packageName, packageVersion); diff --git a/libraries/rush-lib/src/utilities/Utilities.ts b/libraries/rush-lib/src/utilities/Utilities.ts index c5fe8df92d..6c502224ef 100644 --- a/libraries/rush-lib/src/utilities/Utilities.ts +++ b/libraries/rush-lib/src/utilities/Utilities.ts @@ -65,6 +65,12 @@ export interface IInstallPackageInDirectoryOptions { maxInstallAttempts: number; commonRushConfigFolder: string | undefined; suppressOutput?: boolean; + /** + * Whether to filter npm-incompatible properties from .npmrc. + * This should be true when the .npmrc is configured for a different package manager (pnpm/yarn) + * but npm is being used to install packages. + */ + filterNpmIncompatibleProperties?: boolean; } export interface ILifecycleCommandOptions { @@ -505,7 +511,8 @@ export class Utilities { commonRushConfigFolder, maxInstallAttempts, suppressOutput, - directory + directory, + filterNpmIncompatibleProperties = false }: IInstallPackageInDirectoryOptions): Promise { directory = path.resolve(directory); const directoryExists: boolean = await FileSystem.existsAsync(directory); @@ -531,7 +538,10 @@ export class Utilities { Utilities.syncNpmrc({ sourceNpmrcFolder: commonRushConfigFolder, targetNpmrcFolder: directory, - supportEnvVarFallbackSyntax: false + supportEnvVarFallbackSyntax: false, + // Filter out npm-incompatible properties only when the .npmrc is configured for + // a different package manager (pnpm/yarn) but npm is being used to install. + filterNpmIncompatibleProperties }); } diff --git a/libraries/rush-lib/src/utilities/npmrcUtilities.ts b/libraries/rush-lib/src/utilities/npmrcUtilities.ts index b5a61e45af..6e5b1da615 100644 --- a/libraries/rush-lib/src/utilities/npmrcUtilities.ts +++ b/libraries/rush-lib/src/utilities/npmrcUtilities.ts @@ -25,10 +25,22 @@ const _combinedNpmrcMap: Map = new Map(); function _trimNpmrcFile( options: Pick< INpmrcTrimOptions, - 'sourceNpmrcPath' | 'linesToAppend' | 'linesToPrepend' | 'supportEnvVarFallbackSyntax' + | 'sourceNpmrcPath' + | 'linesToAppend' + | 'linesToPrepend' + | 'supportEnvVarFallbackSyntax' + | 'filterNpmIncompatibleProperties' + | 'env' > ): string { - const { sourceNpmrcPath, linesToPrepend, linesToAppend, supportEnvVarFallbackSyntax } = options; + const { + sourceNpmrcPath, + linesToPrepend, + linesToAppend, + supportEnvVarFallbackSyntax, + filterNpmIncompatibleProperties, + env = process.env + } = options; const combinedNpmrcFromCache: string | undefined = _combinedNpmrcMap.get(sourceNpmrcPath); if (combinedNpmrcFromCache !== undefined) { return combinedNpmrcFromCache; @@ -49,7 +61,12 @@ function _trimNpmrcFile( npmrcFileLines = npmrcFileLines.map((line) => (line || '').trim()); - const resultLines: string[] = trimNpmrcFileLines(npmrcFileLines, process.env, supportEnvVarFallbackSyntax); + const resultLines: string[] = trimNpmrcFileLines( + npmrcFileLines, + env, + supportEnvVarFallbackSyntax, + filterNpmIncompatibleProperties + ); const combinedNpmrc: string = resultLines.join('\n'); @@ -59,17 +76,64 @@ function _trimNpmrcFile( return combinedNpmrc; } +/** + * List of npmrc properties that are not supported by npm but may be present in the config. + * These include pnpm-specific properties and deprecated npm properties. + */ +const NPM_INCOMPATIBLE_PROPERTIES: Set = new Set([ + // pnpm-specific hoisting configuration + 'hoist', + 'hoist-pattern', + 'public-hoist-pattern', + 'shamefully-hoist', + // Deprecated or unknown npm properties that cause warnings + 'email', + 'publish-branch' +]); + +/** + * List of registry-scoped npmrc property suffixes that are pnpm-specific. + * These are properties like "//registry.example.com/:tokenHelper" where "tokenHelper" + * is the suffix after the last colon. + */ +const NPM_INCOMPATIBLE_REGISTRY_SCOPED_PROPERTIES: Set = new Set([ + // pnpm-specific token helper properties + 'tokenHelper', + 'urlTokenHelper' +]); + +/** + * Regular expression to extract property names from .npmrc lines. + * Matches everything before '=', '[', or whitespace to capture the property name. + * Note: The 'g' flag is intentionally omitted since we only need the first match. + * Examples: + * "registry=https://..." -> matches "registry" + * "hoist-pattern[]=..." -> matches "hoist-pattern" + */ +const PROPERTY_NAME_REGEX: RegExp = /^([^=\[\s]+)/; + +/** + * Regular expression to extract environment variable names and optional fallback values. + * Matches patterns like: + * nameString -> group 1: nameString, group 2: undefined + * nameString-fallbackString -> group 1: nameString, group 2: fallbackString + * nameString:-fallbackString -> group 1: nameString, group 2: fallbackString + */ +const ENV_VAR_WITH_FALLBACK_REGEX: RegExp = /^(?[^:-]+)(?::?-(?.+))?$/; + /** * * @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 + * @param filterNpmIncompatibleProperties Whether to filter out properties that npm doesn't understand + * @returns An array of processed npmrc file lines with undefined environment variables and npm-incompatible properties commented out */ export function trimNpmrcFileLines( npmrcFileLines: string[], env: NodeJS.ProcessEnv, - supportEnvVarFallbackSyntax: boolean + supportEnvVarFallbackSyntax: boolean, + filterNpmIncompatibleProperties: boolean = false ): string[] { const resultLines: string[] = []; @@ -82,6 +146,7 @@ export function trimNpmrcFileLines( // Trim out lines that reference environment variables that aren't defined for (let line of npmrcFileLines) { let lineShouldBeTrimmed: boolean = false; + let trimReason: string = ''; //remove spaces before or after key and value line = line @@ -91,51 +156,92 @@ export function trimNpmrcFileLines( // Ignore comment lines if (!commentRegExp.test(line)) { - const environmentVariables: string[] | null = line.match(expansionRegExp); - if (environmentVariables) { - for (const token of environmentVariables) { - /** - * 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); - - 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]; + // Check if this is a property that npm doesn't understand + if (filterNpmIncompatibleProperties) { + // Extract the property name (everything before the '=' or '[') + const match: RegExpMatchArray | null = line.match(PROPERTY_NAME_REGEX); + if (match) { + const propertyName: string = match[1]; + + // Check if this is a registry-scoped property (starts with "//" like "//registry.npmjs.org/:_authToken") + const isRegistryScoped: boolean = propertyName.startsWith('//'); + + if (isRegistryScoped) { + // For registry-scoped properties, check if the suffix (after the last colon) is npm-incompatible + // Example: "//registry.example.com/:tokenHelper" -> suffix is "tokenHelper" + const lastColonIndex: number = propertyName.lastIndexOf(':'); + if (lastColonIndex !== -1) { + const registryPropertySuffix: string = propertyName.substring(lastColonIndex + 1); + if (NPM_INCOMPATIBLE_REGISTRY_SCOPED_PROPERTIES.has(registryPropertySuffix)) { + lineShouldBeTrimmed = true; + trimReason = 'NPM_INCOMPATIBLE_PROPERTY'; + } + } } else { - environmentVariableName = nameWithFallback; + // For non-registry-scoped properties, check the full property name + if (NPM_INCOMPATIBLE_PROPERTIES.has(propertyName)) { + lineShouldBeTrimmed = true; + trimReason = 'NPM_INCOMPATIBLE_PROPERTY'; + } } + } + } - // Is the environment variable and fallback value defined. - if (!env[environmentVariableName] && !fallback) { - // No, so trim this line - lineShouldBeTrimmed = true; - break; + // Check for undefined environment variables + if (!lineShouldBeTrimmed) { + const environmentVariables: string[] | null = line.match(expansionRegExp); + if (environmentVariables) { + for (const token of environmentVariables) { + /** + * Remove the leading "${" and the trailing "}" from the token + * + * ${nameString} -> nameString + * ${nameString-fallbackString} -> name-fallbackString + * ${nameString:-fallbackString} -> name:-fallbackString + */ + const nameWithFallback: string = token.slice(2, -1); + + 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: RegExpMatchArray | null = nameWithFallback.match(ENV_VAR_WITH_FALLBACK_REGEX); + environmentVariableName = matched?.groups?.name ?? nameWithFallback; + fallback = matched?.groups?.fallback; + } else { + environmentVariableName = nameWithFallback; + } + + // Is the environment variable and fallback value defined. + if (!env[environmentVariableName] && !fallback) { + // No, so trim this line + lineShouldBeTrimmed = true; + trimReason = 'MISSING_ENVIRONMENT_VARIABLE'; + break; + } } } } } if (lineShouldBeTrimmed) { - // Example output: - // "; MISSING ENVIRONMENT VARIABLE: //my-registry.com/npm/:_authToken=${MY_AUTH_TOKEN}" - resultLines.push('; MISSING ENVIRONMENT VARIABLE: ' + line); + // Comment out the line with appropriate reason + if (trimReason === 'NPM_INCOMPATIBLE_PROPERTY') { + // Example output: + // "; UNSUPPORTED BY NPM: email=test@example.com" + resultLines.push('; UNSUPPORTED BY NPM: ' + line); + } else { + // Example output: + // "; MISSING ENVIRONMENT VARIABLE: //my-registry.com/npm/:_authToken=${MY_AUTH_TOKEN}" + resultLines.push('; MISSING ENVIRONMENT VARIABLE: ' + line); + } } else { resultLines.push(line); } @@ -165,6 +271,8 @@ interface INpmrcTrimOptions { linesToPrepend?: string[]; linesToAppend?: string[]; supportEnvVarFallbackSyntax: boolean; + filterNpmIncompatibleProperties?: boolean; + env?: NodeJS.ProcessEnv; } function _copyAndTrimNpmrcFile(options: INpmrcTrimOptions): string { @@ -197,6 +305,8 @@ export interface ISyncNpmrcOptions { linesToPrepend?: string[]; linesToAppend?: string[]; createIfMissing?: boolean; + filterNpmIncompatibleProperties?: boolean; + env?: NodeJS.ProcessEnv; } export function syncNpmrc(options: ISyncNpmrcOptions): string | undefined { @@ -252,7 +362,11 @@ export function isVariableSetInNpmrcFile( return false; } - const trimmedNpmrcFile: string = _trimNpmrcFile({ sourceNpmrcPath, supportEnvVarFallbackSyntax }); + const trimmedNpmrcFile: string = _trimNpmrcFile({ + sourceNpmrcPath, + supportEnvVarFallbackSyntax, + filterNpmIncompatibleProperties: false + }); 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 index 691864176a..0484645555 100644 --- a/libraries/rush-lib/src/utilities/test/__snapshots__/npmrcUtilities.test.ts.snap +++ b/libraries/rush-lib/src/utilities/test/__snapshots__/npmrcUtilities.test.ts.snap @@ -1,14 +1,69 @@ // 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`] = ` +exports[`npmrcUtilities trimNpmrcFileLines With npm-incompatible properties filtering does not filter when filterNpmIncompatibleProperties is false 1`] = ` Array [ - "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}", + "registry=https://registry.npmjs.org/", + "email=test@example.com", + "hoist=false", ] `; -exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a a variable without a fallback 2`] = ` +exports[`npmrcUtilities trimNpmrcFileLines With npm-incompatible properties filtering filters out deprecated npm properties 1`] = ` Array [ - "var1=\${foo}", + "registry=https://registry.npmjs.org/", + "; UNSUPPORTED BY NPM: email=test@example.com", + "; UNSUPPORTED BY NPM: publish-branch=main", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With npm-incompatible properties filtering filters out pnpm-specific hoisting properties 1`] = ` +Array [ + "registry=https://registry.npmjs.org/", + "; UNSUPPORTED BY NPM: hoist=false", + "; UNSUPPORTED BY NPM: hoist-pattern[]=*eslint*", + "; UNSUPPORTED BY NPM: public-hoist-pattern[]=", + "; UNSUPPORTED BY NPM: shamefully-hoist=true", + "always-auth=false", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With npm-incompatible properties filtering filters out pnpm-specific registry-scoped properties 1`] = ` +Array [ + "registry=https://registry.npmjs.org/", + "//registry.npmjs.org/:_authToken=\${NPM_TOKEN}", + "; UNSUPPORTED BY NPM: //my-registry.com/:tokenHelper=/path/to/helper", + "; UNSUPPORTED BY NPM: //other-registry.com/:urlTokenHelper=/path/to/url-helper", + "//registry.npmjs.org/:always-auth=true", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With npm-incompatible properties filtering preserves registry-scoped auth tokens 1`] = ` +Array [ + "registry=https://registry.npmjs.org/", + "//registry.npmjs.org/:_authToken=\${NPM_TOKEN}", + "//my-registry.com/:_authToken=\${MY_TOKEN}", + "; UNSUPPORTED BY NPM: email=test@example.com", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With npm-incompatible properties filtering preserves registry-scoped configurations 1`] = ` +Array [ + "registry=https://registry.npmjs.org/", + "//registry.npmjs.org/:always-auth=true", + "//my-registry.com/:_authToken=\${MY_TOKEN}", + "; UNSUPPORTED BY NPM: hoist=false", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With npm-incompatible properties filtering preserves standard npm properties 1`] = ` +Array [ + "registry=https://registry.npmjs.org/", + "always-auth=false", + "strict-ssl=true", + "save-exact=true", + "package-lock=true", + "; UNSUPPORTED BY NPM: hoist=false", + "; UNSUPPORTED BY NPM: email=test@example.com", ] `; @@ -60,6 +115,18 @@ Array [ ] `; +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a variable without a fallback 1`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports a variable without a fallback 2`] = ` +Array [ + "var1=\${foo}", +] +`; + exports[`npmrcUtilities trimNpmrcFileLines With support for env var fallback syntax supports malformed lines 1`] = ` Array [ "; MISSING ENVIRONMENT VARIABLE: var1=\${foo_fallback_value}", @@ -112,18 +179,6 @@ Array [ ] `; -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}", @@ -172,6 +227,18 @@ Array [ ] `; +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a variable without a fallback 1`] = ` +Array [ + "; MISSING ENVIRONMENT VARIABLE: var1=\${foo}", +] +`; + +exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports a variable without a fallback 2`] = ` +Array [ + "var1=\${foo}", +] +`; + exports[`npmrcUtilities trimNpmrcFileLines Without support for env var fallback syntax supports malformed lines 1`] = ` Array [ "; MISSING ENVIRONMENT VARIABLE: var1=\${foo_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 b889435a0f..3c84a54cfc 100644 --- a/libraries/rush-lib/src/utilities/test/npmrcUtilities.test.ts +++ b/libraries/rush-lib/src/utilities/test/npmrcUtilities.test.ts @@ -9,7 +9,7 @@ describe('npmrcUtilities', () => { expect(trimNpmrcFileLines([], {}, supportEnvVarFallbackSyntax)).toEqual([]); }); - it('supports a a variable without a fallback', () => { + it('supports a variable without a fallback', () => { expect(trimNpmrcFileLines(['var1=${foo}'], {}, supportEnvVarFallbackSyntax)).toMatchSnapshot(); expect( trimNpmrcFileLines(['var1=${foo}'], { foo: 'test' }, supportEnvVarFallbackSyntax) @@ -92,5 +92,118 @@ describe('npmrcUtilities', () => { describe(trimNpmrcFileLines.name, () => { describe('With support for env var fallback syntax', () => runTests(true)); describe('Without support for env var fallback syntax', () => runTests(false)); + + describe('With npm-incompatible properties filtering', () => { + const supportEnvVarFallbackSyntax = false; + const filterNpmIncompatibleProperties = true; + + it('filters out pnpm-specific hoisting properties', () => { + expect( + trimNpmrcFileLines( + [ + 'registry=https://registry.npmjs.org/', + 'hoist=false', + 'hoist-pattern[]=*eslint*', + 'public-hoist-pattern[]=', + 'shamefully-hoist=true', + 'always-auth=false' + ], + {}, + supportEnvVarFallbackSyntax, + filterNpmIncompatibleProperties + ) + ).toMatchSnapshot(); + }); + + it('filters out deprecated npm properties', () => { + expect( + trimNpmrcFileLines( + ['registry=https://registry.npmjs.org/', 'email=test@example.com', 'publish-branch=main'], + {}, + supportEnvVarFallbackSyntax, + filterNpmIncompatibleProperties + ) + ).toMatchSnapshot(); + }); + + it('preserves registry-scoped auth tokens', () => { + expect( + trimNpmrcFileLines( + [ + 'registry=https://registry.npmjs.org/', + '//registry.npmjs.org/:_authToken=${NPM_TOKEN}', + '//my-registry.com/:_authToken=${MY_TOKEN}', + 'email=test@example.com' + ], + { NPM_TOKEN: 'abc123', MY_TOKEN: 'xyz789' }, + supportEnvVarFallbackSyntax, + filterNpmIncompatibleProperties + ) + ).toMatchSnapshot(); + }); + + it('preserves registry-scoped configurations', () => { + expect( + trimNpmrcFileLines( + [ + 'registry=https://registry.npmjs.org/', + '//registry.npmjs.org/:always-auth=true', + '//my-registry.com/:_authToken=${MY_TOKEN}', + 'hoist=false' + ], + { MY_TOKEN: 'xyz789' }, + supportEnvVarFallbackSyntax, + filterNpmIncompatibleProperties + ) + ).toMatchSnapshot(); + }); + + it('does not filter when filterNpmIncompatibleProperties is false', () => { + expect( + trimNpmrcFileLines( + ['registry=https://registry.npmjs.org/', 'email=test@example.com', 'hoist=false'], + {}, + supportEnvVarFallbackSyntax, + false + ) + ).toMatchSnapshot(); + }); + + it('preserves standard npm properties', () => { + expect( + trimNpmrcFileLines( + [ + 'registry=https://registry.npmjs.org/', + 'always-auth=false', + 'strict-ssl=true', + 'save-exact=true', + 'package-lock=true', + 'hoist=false', + 'email=test@example.com' + ], + {}, + supportEnvVarFallbackSyntax, + filterNpmIncompatibleProperties + ) + ).toMatchSnapshot(); + }); + + it('filters out pnpm-specific registry-scoped properties', () => { + expect( + trimNpmrcFileLines( + [ + 'registry=https://registry.npmjs.org/', + '//registry.npmjs.org/:_authToken=${NPM_TOKEN}', + '//my-registry.com/:tokenHelper=/path/to/helper', + '//other-registry.com/:urlTokenHelper=/path/to/url-helper', + '//registry.npmjs.org/:always-auth=true' + ], + { NPM_TOKEN: 'abc123' }, + supportEnvVarFallbackSyntax, + filterNpmIncompatibleProperties + ) + ).toMatchSnapshot(); + }); + }); }); });