"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.installPlugins = installPlugins;
exports.initHandler = initHandler;
exports.detectPlugins = detectPlugins;
const fs_1 = require("fs");
const semver_1 = require("semver");
const output_1 = require("../../utils/output");
const package_manager_1 = require("../../utils/package-manager");
const add_nx_scripts_1 = require("./implementation/dot-nx/add-nx-scripts");
const child_process_1 = require("../../utils/child-process");
const fileutils_1 = require("../../utils/fileutils");
const versions_1 = require("../../utils/versions");
const utils_1 = require("./implementation/utils");
const enquirer_1 = require("enquirer");
const child_process_2 = require("child_process");
const angular_1 = require("./implementation/angular");
const workspace_context_1 = require("../../utils/workspace-context");
const connect_to_nx_cloud_1 = require("../connect/connect-to-nx-cloud");
const add_nx_to_npm_repo_1 = require("./implementation/add-nx-to-npm-repo");
const add_nx_to_monorepo_1 = require("./implementation/add-nx-to-monorepo");
const nx_json_1 = require("../../config/nx-json");
const get_package_name_from_import_path_1 = require("../../utils/get-package-name-from-import-path");
function installPlugins(repoRoot, plugins, pmc, updatePackageScripts) {
    if (plugins.length === 0) {
        return;
    }
    (0, utils_1.addDepsToPackageJson)(repoRoot, plugins);
    (0, utils_1.runInstall)(repoRoot, pmc);
    output_1.output.log({ title: '🔨 Configuring plugins' });
    for (const plugin of plugins) {
        (0, child_process_2.execSync)(`${pmc.exec} nx g ${plugin}:init --keepExistingVersions ${updatePackageScripts ? '--updatePackageScripts' : ''}`, {
            stdio: [0, 1, 2],
            cwd: repoRoot,
            windowsHide: false,
        });
    }
}
async function initHandler(options) {
    process.env.NX_RUNNING_NX_INIT = 'true';
    const version = process.env.NX_VERSION ?? ((0, semver_1.prerelease)(versions_1.nxVersion) ? 'next' : 'latest');
    if (process.env.NX_VERSION) {
        output_1.output.log({ title: `Using version ${process.env.NX_VERSION}` });
    }
    if (!(0, fs_1.existsSync)('package.json') || options.useDotNxInstallation) {
        if (process.platform !== 'win32') {
            console.log('Setting Nx up installation in `.nx`. You can run Nx commands like: `./nx --help`');
        }
        else {
            console.log('Setting Nx up installation in `.nx`. You can run Nx commands like: `./nx.bat --help`');
        }
        (0, add_nx_scripts_1.generateDotNxSetup)(version);
        const nxJson = (0, nx_json_1.readNxJson)(process.cwd());
        const { plugins } = await detectPlugins(nxJson, options.interactive);
        plugins.forEach((plugin) => {
            (0, child_process_1.runNxSync)(`add ${plugin}`, {
                stdio: 'inherit',
            });
        });
        // invokes the wrapper, thus invoking the initial installation process
        (0, child_process_1.runNxSync)('--version', { stdio: 'ignore' });
        return;
    }
    // TODO(jack): Remove this Angular logic once `@nx/angular` is compatible with inferred targets.
    if ((0, fs_1.existsSync)('angular.json')) {
        await (0, angular_1.addNxToAngularCliRepo)({
            ...options,
            integrated: !!options.integrated,
        });
        (0, utils_1.printFinalMessage)({
            learnMoreLink: 'https://nx.dev/recipes/angular/migration/angular',
        });
        return;
    }
    const packageJson = (0, fileutils_1.readJsonFile)('package.json');
    if ((0, utils_1.isMonorepo)(packageJson)) {
        await (0, add_nx_to_monorepo_1.addNxToMonorepo)({
            interactive: options.interactive,
            nxCloud: false,
        });
    }
    else {
        await (0, add_nx_to_npm_repo_1.addNxToNpmRepo)({
            interactive: options.interactive,
            nxCloud: false,
        });
    }
    const learnMoreLink = (0, utils_1.isMonorepo)(packageJson)
        ? 'https://nx.dev/getting-started/tutorials/npm-workspaces-tutorial'
        : 'https://nx.dev/recipes/adopting-nx/adding-to-existing-project';
    const useNxCloud = options.nxCloud ??
        (options.interactive ? await (0, connect_to_nx_cloud_1.connectExistingRepoToNxCloudPrompt)() : false);
    const repoRoot = process.cwd();
    const pmc = (0, package_manager_1.getPackageManagerCommand)();
    (0, utils_1.createNxJsonFile)(repoRoot, [], [], {});
    (0, utils_1.updateGitIgnore)(repoRoot);
    const nxJson = (0, nx_json_1.readNxJson)(repoRoot);
    output_1.output.log({ title: '🧐 Checking dependencies' });
    const { plugins, updatePackageScripts } = await detectPlugins(nxJson, options.interactive);
    output_1.output.log({ title: '📦 Installing Nx' });
    installPlugins(repoRoot, plugins, pmc, updatePackageScripts);
    if (useNxCloud) {
        output_1.output.log({ title: '🛠️ Setting up Nx Cloud' });
        await (0, utils_1.initCloud)('nx-init');
    }
    (0, utils_1.printFinalMessage)({
        learnMoreLink,
    });
}
const npmPackageToPluginMap = {
    // Generic JS tools
    eslint: '@nx/eslint',
    storybook: '@nx/storybook',
    // Bundlers
    vite: '@nx/vite',
    vitest: '@nx/vite',
    webpack: '@nx/webpack',
    '@rspack/core': '@nx/rspack',
    rollup: '@nx/rollup',
    // Testing tools
    jest: '@nx/jest',
    cypress: '@nx/cypress',
    '@playwright/test': '@nx/playwright',
    // Frameworks
    detox: '@nx/detox',
    expo: '@nx/expo',
    next: '@nx/next',
    nuxt: '@nx/nuxt',
    'react-native': '@nx/react-native',
    '@remix-run/dev': '@nx/remix',
    '@rsbuild/core': '@nx/rsbuild',
};
async function detectPlugins(nxJson, interactive, includeAngularCli) {
    let files = ['package.json'].concat((0, workspace_context_1.globWithWorkspaceContextSync)(process.cwd(), ['**/*/package.json']));
    const currentPlugins = new Set((nxJson.plugins ?? []).map((p) => {
        const plugin = typeof p === 'string' ? p : p.plugin;
        return (0, get_package_name_from_import_path_1.getPackageNameFromImportPath)(plugin);
    }));
    const detectedPlugins = new Set();
    for (const file of files) {
        if (!(0, fs_1.existsSync)(file))
            continue;
        let packageJson;
        try {
            packageJson = (0, fileutils_1.readJsonFile)(file);
        }
        catch {
            // Could have malformed JSON for unit tests, etc.
            continue;
        }
        const deps = {
            ...packageJson.dependencies,
            ...packageJson.devDependencies,
        };
        const _npmPackageToPluginMap = {
            ...npmPackageToPluginMap,
        };
        if (includeAngularCli) {
            _npmPackageToPluginMap['@angular/cli'] = '@nx/angular';
        }
        for (const [dep, plugin] of Object.entries(_npmPackageToPluginMap)) {
            if (deps[dep]) {
                detectedPlugins.add(plugin);
            }
        }
    }
    let gradlewFiles = ['gradlew', 'gradlew.bat'].concat((0, workspace_context_1.globWithWorkspaceContextSync)(process.cwd(), [
        '**/gradlew',
        '**/gradlew.bat',
    ]));
    if (gradlewFiles.some((f) => (0, fs_1.existsSync)(f))) {
        detectedPlugins.add('@nx/gradle');
    }
    // Remove existing plugins
    for (const plugin of detectedPlugins) {
        if (currentPlugins.has(plugin)) {
            detectedPlugins.delete(plugin);
        }
    }
    const plugins = Array.from(detectedPlugins);
    if (plugins.length === 0) {
        return {
            plugins: [],
            updatePackageScripts: false,
        };
    }
    if (!interactive) {
        output_1.output.log({
            title: `Recommended Plugins:`,
            bodyLines: [
                `Adding these Nx plugins to integrate with the tools used in your workspace:`,
                ...plugins.map((p) => `- ${p}`),
            ],
        });
        return {
            plugins,
            updatePackageScripts: true,
        };
    }
    output_1.output.log({
        title: `Recommended Plugins:`,
        bodyLines: [
            `Add these Nx plugins to integrate with the tools used in your workspace.`,
        ],
    });
    const pluginsToInstall = await (0, enquirer_1.prompt)([
        {
            name: 'plugins',
            type: 'multiselect',
            message: `Which plugins would you like to add? Press <Space> to select and <Enter> to submit.`,
            choices: plugins.map((p) => ({ name: p, value: p })),
            /**
             * limit is missing from the interface but it limits the amount of options shown
             */
            limit: process.stdout.rows - 4, // 4 leaves room for the header above, the prompt and some whitespace
        },
    ]).then((r) => r.plugins);
    if (pluginsToInstall?.length === 0)
        return {
            plugins: [],
            updatePackageScripts: false,
        };
    const updatePackageScripts = (0, fs_1.existsSync)('package.json') &&
        (await (0, enquirer_1.prompt)([
            {
                name: 'updatePackageScripts',
                type: 'autocomplete',
                message: `Do you want to start using Nx in your package.json scripts?`,
                choices: [
                    {
                        name: 'Yes',
                    },
                    {
                        name: 'No',
                    },
                ],
                initial: 0,
            },
        ]).then((r) => r.updatePackageScripts === 'Yes'));
    return { plugins: pluginsToInstall, updatePackageScripts };
}
