Skip to content

Commit

Permalink
Support fallback syntax in .npmrc file
Browse files Browse the repository at this point in the history
  • Loading branch information
fzxen committed Dec 10, 2024
1 parent ca5a3a9 commit d7aa2ed
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@microsoft/rush",
"comment": "Support fallback syntax in .npmrc file",
"type": "none"
}
],
"packageName": "@microsoft/rush"
}
83 changes: 56 additions & 27 deletions libraries/rush-lib/src/utilities/npmrcUtilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,27 +22,11 @@ export interface ILogger {
// create a global _combinedNpmrc for cache purpose
const _combinedNpmrcMap: Map<string, string> = 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}"
Expand All @@ -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;
Expand All @@ -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');

Expand Down
38 changes: 38 additions & 0 deletions libraries/rush-lib/src/utilities/test/npmrcUtilities.test.ts
Original file line number Diff line number Diff line change
@@ -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']);
});
});

0 comments on commit d7aa2ed

Please sign in to comment.