"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.trimNpmrcFileLines = trimNpmrcFileLines;
exports.syncNpmrc = syncNpmrc;
exports.isVariableSetInNpmrcFile = isVariableSetInNpmrcFile;
// IMPORTANT - do not use any non-built-in libraries in this file
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
/**
 * This function reads the content for given .npmrc file path, and also trims
 * unusable lines from the .npmrc file.
 *
 * @returns
 * The text of the the .npmrc.
 */
// create a global _combinedNpmrc for cache purpose
const _combinedNpmrcMap = new Map();
function _trimNpmrcFile(options) {
    const { sourceNpmrcPath, linesToPrepend, linesToAppend, supportEnvVarFallbackSyntax, filterNpmIncompatibleProperties, env = process.env } = options;
    const combinedNpmrcFromCache = _combinedNpmrcMap.get(sourceNpmrcPath);
    if (combinedNpmrcFromCache !== undefined) {
        return combinedNpmrcFromCache;
    }
    let npmrcFileLines = [];
    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 = trimNpmrcFileLines(npmrcFileLines, env, supportEnvVarFallbackSyntax, filterNpmIncompatibleProperties);
    const combinedNpmrc = resultLines.join('\n');
    //save the cache
    _combinedNpmrcMap.set(sourceNpmrcPath, combinedNpmrc);
    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 = 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 = 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 = /^([^=\[\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 = /^(?<name>[^:-]+)(?::?-(?<fallback>.+))?$/;
/**
 *
 * @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}`
 * @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
 */
function trimNpmrcFileLines(npmrcFileLines, env, supportEnvVarFallbackSyntax, filterNpmIncompatibleProperties = false) {
    var _a, _b, _c;
    const resultLines = [];
    // This finds environment variable tokens that look like "${VAR_NAME}"
    const expansionRegExp = /\$\{([^\}]+)\}/g;
    // Comment lines start with "#" or ";"
    const commentRegExp = /^\s*[#;]/;
    // Trim out lines that reference environment variables that aren't defined
    for (let line of npmrcFileLines) {
        let lineShouldBeTrimmed = false;
        let trimReason = '';
        //remove spaces before or after key and value
        line = line
            .split('=')
            .map((lineToTrim) => lineToTrim.trim())
            .join('=');
        // Ignore comment lines
        if (!commentRegExp.test(line)) {
            // Check if this is a property that npm doesn't understand
            if (filterNpmIncompatibleProperties) {
                // Extract the property name (everything before the '=' or '[')
                const match = line.match(PROPERTY_NAME_REGEX);
                if (match) {
                    const propertyName = match[1];
                    // Check if this is a registry-scoped property (starts with "//" like "//registry.npmjs.org/:_authToken")
                    const isRegistryScoped = 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 = propertyName.lastIndexOf(':');
                        if (lastColonIndex !== -1) {
                            const registryPropertySuffix = propertyName.substring(lastColonIndex + 1);
                            if (NPM_INCOMPATIBLE_REGISTRY_SCOPED_PROPERTIES.has(registryPropertySuffix)) {
                                lineShouldBeTrimmed = true;
                                trimReason = 'NPM_INCOMPATIBLE_PROPERTY';
                            }
                        }
                    }
                    else {
                        // For non-registry-scoped properties, check the full property name
                        if (NPM_INCOMPATIBLE_PROPERTIES.has(propertyName)) {
                            lineShouldBeTrimmed = true;
                            trimReason = 'NPM_INCOMPATIBLE_PROPERTY';
                        }
                    }
                }
            }
            // Check for undefined environment variables
            if (!lineShouldBeTrimmed) {
                const environmentVariables = 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 = token.slice(2, -1);
                        let environmentVariableName;
                        let fallback;
                        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 = nameWithFallback.match(ENV_VAR_WITH_FALLBACK_REGEX);
                            environmentVariableName = (_b = (_a = matched === null || matched === void 0 ? void 0 : matched.groups) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : nameWithFallback;
                            fallback = (_c = matched === null || matched === void 0 ? void 0 : matched.groups) === null || _c === void 0 ? void 0 : _c.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) {
            // 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);
        }
    }
    return resultLines;
}
function _copyAndTrimNpmrcFile(options) {
    const { logger, sourceNpmrcPath, targetNpmrcPath } = options;
    logger.info(`Transforming ${sourceNpmrcPath}`); // Verbose
    logger.info(`  --> "${targetNpmrcPath}"`);
    const combinedNpmrc = _trimNpmrcFile(options);
    fs.writeFileSync(targetNpmrcPath, combinedNpmrc);
    return combinedNpmrc;
}
function syncNpmrc(options) {
    const { sourceNpmrcFolder, targetNpmrcFolder, useNpmrcPublish, logger = {
        // eslint-disable-next-line no-console
        info: console.log,
        // eslint-disable-next-line no-console
        error: console.error
    }, createIfMissing = false } = options;
    const sourceNpmrcPath = path.join(sourceNpmrcFolder, !useNpmrcPublish ? '.npmrc' : '.npmrc-publish');
    const targetNpmrcPath = path.join(targetNpmrcFolder, '.npmrc');
    try {
        if (fs.existsSync(sourceNpmrcPath) || createIfMissing) {
            // Ensure the target folder exists
            if (!fs.existsSync(targetNpmrcFolder)) {
                fs.mkdirSync(targetNpmrcFolder, { recursive: true });
            }
            return _copyAndTrimNpmrcFile({
                sourceNpmrcPath,
                targetNpmrcPath,
                logger,
                ...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
            logger.info(`Deleting ${targetNpmrcPath}`); // Verbose
            fs.unlinkSync(targetNpmrcPath);
        }
    }
    catch (e) {
        throw new Error(`Error syncing .npmrc file: ${e}`);
    }
}
function isVariableSetInNpmrcFile(sourceNpmrcFolder, variableKey, supportEnvVarFallbackSyntax) {
    const sourceNpmrcPath = `${sourceNpmrcFolder}/.npmrc`;
    //if .npmrc file does not exist, return false directly
    if (!fs.existsSync(sourceNpmrcPath)) {
        return false;
    }
    const trimmedNpmrcFile = _trimNpmrcFile({
        sourceNpmrcPath,
        supportEnvVarFallbackSyntax,
        filterNpmIncompatibleProperties: false
    });
    const variableKeyRegExp = new RegExp(`^${variableKey}=`, 'm');
    return trimmedNpmrcFile.match(variableKeyRegExp) !== null;
}
//# sourceMappingURL=npmrcUtilities.js.map