import { promises as fs } from "fs";
import chalk from "next/dist/compiled/chalk";
import * as CommentJson from "next/dist/compiled/comment-json";
import semver from "next/dist/compiled/semver";
import os from "os";
import { getTypeScriptConfiguration } from "./getTypeScriptConfiguration";
function getDesiredCompilerOptions(ts) {
    const o = {
        // These are suggested values and will be set when not present in the
        // tsconfig.json
        lib: {
            suggested: [
                "dom",
                "dom.iterable",
                "esnext"
            ]
        },
        allowJs: {
            suggested: true
        },
        skipLibCheck: {
            suggested: true
        },
        strict: {
            suggested: false
        },
        forceConsistentCasingInFileNames: {
            suggested: true
        },
        noEmit: {
            suggested: true
        },
        ...semver.gte(ts.version, "4.4.2") ? {
            incremental: {
                suggested: true
            }
        } : undefined,
        // These values are required and cannot be changed by the user
        // Keep this in sync with the webpack config
        // 'parsedValue' matches the output value from ts.parseJsonConfigFileContent()
        esModuleInterop: {
            value: true,
            reason: "requirement for SWC / babel"
        },
        module: {
            parsedValue: ts.ModuleKind.ESNext,
            // All of these values work:
            parsedValues: [
                ts.ModuleKind.ES2020,
                ts.ModuleKind.ESNext,
                ts.ModuleKind.CommonJS,
                ts.ModuleKind.AMD,
                ts.ModuleKind.NodeNext,
                ts.ModuleKind.Node16, 
            ],
            value: "esnext",
            reason: "for dynamic import() support"
        },
        moduleResolution: {
            // In TypeScript 5.0, `NodeJs` has renamed to `Node10`
            parsedValue: ts.ModuleResolutionKind.Node10 ?? ts.ModuleResolutionKind.NodeJs,
            // All of these values work:
            parsedValues: [
                ts.ModuleResolutionKind.Node10 ?? ts.ModuleResolutionKind.NodeJs,
                // only newer TypeScript versions have this field, it
                // will be filtered for new versions of TypeScript
                (ts.ModuleResolutionKind).Node12,
                ts.ModuleResolutionKind.Node16,
                ts.ModuleResolutionKind.NodeNext, 
            ].filter((val)=>typeof val !== "undefined"),
            value: "node",
            reason: "to match webpack resolution"
        },
        resolveJsonModule: {
            value: true,
            reason: "to match webpack resolution"
        },
        isolatedModules: {
            value: true,
            reason: "requirement for SWC / Babel"
        },
        jsx: {
            parsedValue: ts.JsxEmit.Preserve,
            value: "preserve",
            reason: "next.js implements its own optimized jsx transform"
        }
    };
    return o;
}
export function getRequiredConfiguration(ts) {
    const res = {};
    const desiredCompilerOptions = getDesiredCompilerOptions(ts);
    for (const optionKey of Object.keys(desiredCompilerOptions)){
        const ev = desiredCompilerOptions[optionKey];
        if (!("value" in ev)) {
            continue;
        }
        res[optionKey] = ev.parsedValue ?? ev.value;
    }
    return res;
}
export async function writeConfigurationDefaults(ts, tsConfigPath, isFirstTimeSetup, isAppDirEnabled, distDir) {
    if (isFirstTimeSetup) {
        await fs.writeFile(tsConfigPath, "{}" + os.EOL);
    }
    const desiredCompilerOptions = getDesiredCompilerOptions(ts);
    const { options: tsOptions , raw: rawConfig  } = await getTypeScriptConfiguration(ts, tsConfigPath, true);
    const userTsConfigContent = await fs.readFile(tsConfigPath, {
        encoding: "utf8"
    });
    const userTsConfig = CommentJson.parse(userTsConfigContent);
    if (userTsConfig.compilerOptions == null && !("extends" in rawConfig)) {
        userTsConfig.compilerOptions = {};
        isFirstTimeSetup = true;
    }
    const suggestedActions = [];
    const requiredActions = [];
    for (const optionKey of Object.keys(desiredCompilerOptions)){
        const check = desiredCompilerOptions[optionKey];
        if ("suggested" in check) {
            if (!(optionKey in tsOptions)) {
                if (!userTsConfig.compilerOptions) {
                    userTsConfig.compilerOptions = {};
                }
                userTsConfig.compilerOptions[optionKey] = check.suggested;
                suggestedActions.push(chalk.cyan(optionKey) + " was set to " + chalk.bold(check.suggested));
            }
        } else if ("value" in check) {
            var ref;
            const ev = tsOptions[optionKey];
            if (!("parsedValues" in check ? (ref = check.parsedValues) == null ? void 0 : ref.includes(ev) : "parsedValue" in check ? check.parsedValue === ev : check.value === ev)) {
                if (!userTsConfig.compilerOptions) {
                    userTsConfig.compilerOptions = {};
                }
                userTsConfig.compilerOptions[optionKey] = check.value;
                requiredActions.push(chalk.cyan(optionKey) + " was set to " + chalk.bold(check.value) + ` (${check.reason})`);
            }
        } else {
            // eslint-disable-next-line @typescript-eslint/no-unused-vars
            const _ = check;
        }
    }
    const nextAppTypes = `${distDir}/types/**/*.ts`;
    if (!("include" in rawConfig)) {
        userTsConfig.include = isAppDirEnabled ? [
            "next-env.d.ts",
            nextAppTypes,
            "**/*.ts",
            "**/*.tsx"
        ] : [
            "next-env.d.ts",
            "**/*.ts",
            "**/*.tsx"
        ];
        suggestedActions.push(chalk.cyan("include") + " was set to " + chalk.bold(isAppDirEnabled ? `['next-env.d.ts', '${nextAppTypes}', '**/*.ts', '**/*.tsx']` : `['next-env.d.ts', '**/*.ts', '**/*.tsx']`));
    } else if (isAppDirEnabled && !rawConfig.include.includes(nextAppTypes)) {
        userTsConfig.include.push(nextAppTypes);
        suggestedActions.push(chalk.cyan("include") + " was updated to add " + chalk.bold(`'${nextAppTypes}'`));
    }
    // Enable the Next.js typescript plugin.
    if (isAppDirEnabled) {
        if (userTsConfig.compilerOptions) {
            // If the TS config extends on another config, we can't add the `plugin` field
            // because that will override the parent config's plugins.
            // Instead we have to show a message to the user to add the plugin manually.
            if ("extends" in userTsConfig && !("plugins" in userTsConfig)) {
                console.log(`\nYour ${chalk.cyan("tsconfig.json")} extends another configuration, which means we cannot add the Next.js TypeScript plugin automatically. To improve your development experience, we recommend adding the Next.js plugin (\`${chalk.cyan('"plugins": [{ "name": "next" }]')}\`) manually to your TypeScript configuration. Learn more: https://beta.nextjs.org/docs/configuring/typescript#using-the-typescript-plugin\n`);
            } else {
                if (!("plugins" in userTsConfig.compilerOptions)) {
                    userTsConfig.compilerOptions.plugins = [];
                }
                if (!userTsConfig.compilerOptions.plugins.some((plugin)=>plugin.name === "next")) {
                    userTsConfig.compilerOptions.plugins.push({
                        name: "next"
                    });
                    suggestedActions.push(chalk.cyan("plugins") + " was updated to add " + chalk.bold(`{ name: 'next' }`));
                }
            }
        }
    }
    if (!("exclude" in rawConfig)) {
        userTsConfig.exclude = [
            "node_modules"
        ];
        suggestedActions.push(chalk.cyan("exclude") + " was set to " + chalk.bold(`['node_modules']`));
    }
    if (suggestedActions.length < 1 && requiredActions.length < 1) {
        return;
    }
    await fs.writeFile(tsConfigPath, CommentJson.stringify(userTsConfig, null, 2) + os.EOL);
    if (isFirstTimeSetup) {
        console.log(chalk.green(`We detected TypeScript in your project and created a ${chalk.bold("tsconfig.json")} file for you.`) + "\n");
        return;
    }
    console.log(chalk.green(`We detected TypeScript in your project and reconfigured your ${chalk.bold("tsconfig.json")} file for you. Strict-mode is set to ${chalk.bold("false")} by default.`) + "\n");
    if (suggestedActions.length) {
        console.log(`The following suggested values were added to your ${chalk.cyan("tsconfig.json")}. These values ${chalk.bold("can be changed")} to fit your project's needs:\n`);
        suggestedActions.forEach((action)=>console.log(`\t- ${action}`));
        console.log("");
    }
    if (requiredActions.length) {
        console.log(`The following ${chalk.bold("mandatory changes")} were made to your ${chalk.cyan("tsconfig.json")}:\n`);
        requiredActions.forEach((action)=>console.log(`\t- ${action}`));
        console.log("");
    }
}

//# sourceMappingURL=writeConfigurationDefaults.js.map