"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.NpmController = void 0;
const node_path_1 = require("node:path");
const node_url_1 = require("node:url");
const system_settings_1 = require("@ewizardjs/system-settings");
const PackageJson = require("@npmcli/package-json");
const child_process_promise_1 = require("child-process-promise");
const fs_extra_1 = require("fs-extra");
const inquirer_1 = require("inquirer");
const ramda_1 = require("ramda");
const urlJoin = require("url-join");
const npm_controller_interface_1 = require("../../interfaces/npm-controller.interface");
const utils_1 = require("../../utils");
const I18n_1 = require("../I18n");
const Logger_1 = require("../Logger");
const NPM_RC = {
    project: '.npmrc',
    builtIn: 'npmrc',
};
const LOCKFILE_VERSIONS = {
    [system_settings_1.NpmVersion.Legacy]: 1,
    [system_settings_1.NpmVersion.Latest]: 3,
};
class NpmController {
    constructor(_a) {
        var { cwd, registry = '', token = '', npmVersion = system_settings_1.NpmVersion.Latest, distTag } = _a, options = __rest(_a, ["cwd", "registry", "token", "npmVersion", "distTag"]);
        this.useLegacyNpm = false;
        const env = (0, ramda_1.compose)((0, ramda_1.mergeLeft)({ AUTH_TOKEN: token }), removeNpmConfigOptions)(process.env);
        this.npmVersion = npmVersion;
        this.registry = registry;
        this.distTag = distTag;
        this.options = Object.assign(Object.assign({}, options), { cwd,
            env });
    }
    install(args, deps) {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.npmVersion === system_settings_1.NpmVersion.Legacy) {
                yield this.installWithNpmLegacy(args, deps);
            }
            else {
                yield this.installWithNpmLatest(args, deps);
            }
        });
    }
    installWithNpmLegacy(args, deps) {
        return __awaiter(this, void 0, void 0, function* () {
            const packageLock = yield (0, utils_1.getPackageLockFile)(this.options.cwd);
            const isLegacyLockfile = (packageLock === null || packageLock === void 0 ? void 0 : packageLock.lockfileVersion) === LOCKFILE_VERSIONS[system_settings_1.NpmVersion.Legacy];
            if (!isLegacyLockfile) {
                this.npmVersion = system_settings_1.NpmVersion.Latest;
                this.useLegacyNpm = false;
                yield this.setNpmPaths();
                yield this.installWithNpmLatest(args, deps);
                return;
            }
            const shouldUseLegacy = yield this.askToInstallLegacyNpm();
            if (!shouldUseLegacy) {
                this.npmVersion = system_settings_1.NpmVersion.Latest;
                this.useLegacyNpm = false;
                yield this.setNpmPaths();
                yield this.installWithNpmLatest(args, deps);
                return;
            }
            this.useLegacyNpm = true;
            yield this.setNpmPaths();
            const { options } = this;
            const updateLockFileCommand = yield this.getUpdateLockFileCommand();
            yield this.runNpm([
                ...updateLockFileCommand,
                { cmd: npm_controller_interface_1.NpmCommand.Install, args: ['--package-lock-only', ...args, ...deps], options },
                { cmd: npm_controller_interface_1.NpmCommand.CleanInstall, args, options },
            ]);
        });
    }
    installWithNpmLatest(args, deps) {
        return __awaiter(this, void 0, void 0, function* () {
            this.useLegacyNpm = false;
            const { options } = this;
            const updateLockFileCommand = yield this.getUpdateLockFileCommand();
            const vueOverridesChanged = yield this.handleVueOverrides(deps, npm_controller_interface_1.NpmCommand.Install);
            const cmd = !vueOverridesChanged && !deps.length && (yield (0, utils_1.isPackageLockFileAvailable)(options.cwd))
                ? npm_controller_interface_1.NpmCommand.CleanInstall
                : npm_controller_interface_1.NpmCommand.Install;
            yield this.runNpm([
                ...updateLockFileCommand,
                { cmd, args: [...args, ...deps], options },
            ]);
        });
    }
    publish(args) {
        return __awaiter(this, void 0, void 0, function* () {
            const { distTag, options } = this;
            const finalArgs = distTag ? [...args, `--tag=${distTag}`] : args;
            yield this.runNpm([{ cmd: npm_controller_interface_1.NpmCommand.Publish, args: finalArgs, options }]);
        });
    }
    uninstall(args, deps) {
        return __awaiter(this, void 0, void 0, function* () {
            const { options } = this;
            yield this.runNpm([{ cmd: npm_controller_interface_1.NpmCommand.Uninstall, args: [...args, ...deps], options }]);
            const vueOverridesChanged = yield this.handleVueOverrides(deps, npm_controller_interface_1.NpmCommand.Uninstall);
            if (vueOverridesChanged) {
                yield this.runNpm([{ cmd: npm_controller_interface_1.NpmCommand.Install, args, options }]);
            }
        });
    }
    setNpmPaths() {
        return __awaiter(this, void 0, void 0, function* () {
            this.npmPaths = yield this.getNpmPaths();
            this.npmRcPaths = yield this.getNpmRcPaths();
        });
    }
    getNpmPaths() {
        return __awaiter(this, void 0, void 0, function* () {
            const versions = [system_settings_1.NpmVersion.Latest];
            if (this.useLegacyNpm) {
                versions.push(system_settings_1.NpmVersion.Legacy);
            }
            const pathEntries = yield Promise.all(versions
                .map((npmVersion) => __awaiter(this, void 0, void 0, function* () { return [npmVersion, yield (0, utils_1.getNpm)({ npmVersion })]; })));
            const availableEntries = pathEntries.filter(([, path]) => Boolean(path));
            return Object.fromEntries(availableEntries);
        });
    }
    getNpmRcPaths() {
        return __awaiter(this, void 0, void 0, function* () {
            const versions = Object.keys(this.npmPaths);
            const builtInConfigs = (yield Promise.all(versions
                .map((npmVersion) => __awaiter(this, void 0, void 0, function* () {
                const rootPath = yield (0, utils_1.getNpm)({ npmVersion, root: true });
                return rootPath ? (0, node_path_1.join)(rootPath, NPM_RC.builtIn) : null;
            }))))
                .filter(Boolean);
            const projectConfig = (0, node_path_1.join)(this.options.cwd, NPM_RC.project);
            return [...builtInConfigs, projectConfig];
        });
    }
    runNpm(npmCommands) {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.registry) {
                yield this.runNpmWithRc(npmCommands);
            }
            else {
                yield this.runNpmCommands(npmCommands);
            }
        });
    }
    runNpmWithRc(npmCommands) {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                yield this.createNpmRc();
                yield this.runNpmCommands(npmCommands);
            }
            finally {
                yield this.removeNpmRc();
            }
        });
    }
    runNpmCommands(npmCommands) {
        return __awaiter(this, void 0, void 0, function* () {
            for (const command of npmCommands) {
                yield this.runNpmProcess(command);
            }
        });
    }
    createNpmRc() {
        const content = this.generateNpmConfig();
        return Promise.all(this.npmRcPaths.map((path) => (0, fs_extra_1.outputFile)(path, content)));
    }
    removeNpmRc() {
        return Promise.all(this.npmRcPaths.map((path) => (0, fs_extra_1.remove)(path)));
    }
    generateNpmConfig() {
        const { host, pathname } = new node_url_1.URL(this.registry);
        const registry = `registry=${this.registry}`;
        const registryWithoutProtocol = urlJoin(host, removeTrailingSlash(pathname));
        const scopedRegistries = getScopedRegistries(registry);
        const authRegistries = getAuthRegistries(registryWithoutProtocol, this.npmVersion === system_settings_1.NpmVersion.Legacy);
        return [...scopedRegistries, registry, ...authRegistries].join('\n');
    }
    runNpmProcess({ cmd, args, npmVersion = this.npmVersion, options }) {
        return __awaiter(this, void 0, void 0, function* () {
            let resolvedVersion = npmVersion;
            let npm = this.npmPaths[npmVersion];
            if (!npm && npmVersion === system_settings_1.NpmVersion.Legacy) {
                resolvedVersion = system_settings_1.NpmVersion.Latest;
                npm = this.npmPaths[resolvedVersion];
            }
            if (!npm) {
                throw new Error(`Npm executable for version "${resolvedVersion}" is not available.`);
            }
            Logger_1.default.info(I18n_1.default.info('NPM_VERSION_IS', resolvedVersion));
            yield (0, child_process_promise_1.fork)(npm, [cmd, ...args], options);
        });
    }
    getUpdateLockFileCommand() {
        return __awaiter(this, void 0, void 0, function* () {
            const { options } = this;
            const npmVersion = system_settings_1.NpmVersion.Latest;
            const packageLock = yield (0, utils_1.getPackageLockFile)(options.cwd);
            const targetLockfileVersion = LOCKFILE_VERSIONS[this.npmVersion];
            return packageLock && packageLock.lockfileVersion !== targetLockfileVersion
                ? [{
                        cmd: npm_controller_interface_1.NpmCommand.Install,
                        args: ['--lockfile-version', targetLockfileVersion.toString()],
                        npmVersion,
                        options,
                    }]
                : [];
        });
    }
    handleVueOverrides(deps, npmCommand) {
        return __awaiter(this, void 0, void 0, function* () {
            if (this.npmVersion === system_settings_1.NpmVersion.Legacy) {
                return false;
            }
            const pkgJson = yield PackageJson.load(this.options.cwd);
            const newOverrides = (0, utils_1.getVueOverride)({ content: pkgJson.content, deps, npmCommand });
            if (newOverrides !== null) {
                pkgJson.update({ overrides: newOverrides });
                yield pkgJson.save();
                return true;
            }
            return false;
        });
    }
    askToInstallLegacyNpm() {
        return __awaiter(this, void 0, void 0, function* () {
            const { confirmLegacy } = yield (0, inquirer_1.prompt)([{
                    type: 'confirm',
                    name: 'confirmLegacy',
                    message: 'Legacy npm v6 is expected to proceed. It\'s no longer supported due to known security vulnerabilities. Install anyway?',
                    default: false,
                }]);
            return confirmLegacy;
        });
    }
}
exports.NpmController = NpmController;
function removeTrailingSlash(pathname) {
    return pathname.replace(/\/$/, '');
}
function removeNpmConfigOptions(obj) {
    return (0, ramda_1.omit)(['npm_config_registry', 'npm_config_always_auth'], obj);
}
function getScopedRegistries(registry) {
    const namespaces = ['@ewizardjs', '@component'];
    return namespaces.map((namespace) => `${namespace}:${registry}`);
}
function getAuthRegistries(registry, isLegacy) {
    const authConfigs = ['/:_authToken=\${AUTH_TOKEN}'];
    if (isLegacy) {
        authConfigs.push('/:always-auth=true');
    }
    return authConfigs.map((authConfig) => `//${registry}${authConfig}`);
}
//# sourceMappingURL=index.js.map