#!/usr/bin/env node
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const cross_spawn_1 = __importDefault(require("cross-spawn"));
const minimist_1 = __importDefault(require("minimist"));
const prompts_1 = __importDefault(require("prompts"));
const kolorist_1 = require("kolorist");
// Avoids autoconversion to number of the project name by defining that the args
// non associated with an option ( _ ) needs to be parsed as a string. See #4606
const argv = (0, minimist_1.default)(process.argv.slice(2), { string: ['_'] });
const cwd = process.cwd();
const FRAMEWORKS = [
    // {
    //     name: 'vanilla',
    //     display: 'Vanilla',
    //     color: yellow,
    //     variants: [
    //         {
    //             name: 'vanilla',
    //             display: 'JavaScript',
    //             color: yellow,
    //         },
    //         {
    //             name: 'vanilla-ts',
    //             display: 'TypeScript',
    //             color: blue,
    //         },
    //     ],
    // },
    {
        name: 'vue',
        display: 'Vue',
        color: kolorist_1.green,
        variants: [
            {
                name: 'vue-giime-ts',
                display: 'giime-ts',
                color: kolorist_1.yellow,
            },
            {
                name: 'vue-tailwind-ts',
                display: 'tailwind-ts',
                color: kolorist_1.green,
            },
            {
                name: 'vue-admin-ts',
                display: 'admin-ts',
                color: kolorist_1.lightGreen,
            },
            {
                name: 'vue-crx-template',
                display: 'vue-crx-template',
                color: kolorist_1.yellow,
            },
            {
                name: 'vue-giime-electron-ts',
                display: 'giime-electron-ts',
                color: kolorist_1.green,
            },
            // {
            //     name: 'custom-create-vue',
            //     display: 'Customize with create-vue ↗',
            //     color: green,
            //     customCommand: 'npm create vue@latest TARGET_DIR',
            // },
            // {
            //     name: 'custom-nuxt',
            //     display: 'Nuxt ↗',
            //     color: lightGreen,
            //     customCommand: 'npm exec nuxi init TARGET_DIR',
            // },
        ],
    },
    {
        name: 'html',
        display: 'Html',
        color: kolorist_1.yellow,
        variants: [
            {
                name: 'html-vite-ts',
                display: 'vite-ts',
                color: kolorist_1.lightGreen,
            },
        ],
    },
    {
        name: 'nestjs',
        display: 'Nestjs',
        color: kolorist_1.cyan,
        variants: [
            // {
            //     name: 'react',
            //     display: 'JavaScript',
            //     color: yellow,
            // },
            {
                name: 'nestjs',
                display: 'TypeScript',
                color: kolorist_1.blue,
            },
        ],
    },
    // {
    //     name: 'preact',
    //     display: 'Preact',
    //     color: magenta,
    //     variants: [
    //         {
    //             name: 'preact',
    //             display: 'JavaScript',
    //             color: yellow,
    //         },
    //         {
    //             name: 'preact-ts',
    //             display: 'TypeScript',
    //             color: blue,
    //         },
    //     ],
    // },
    // {
    //     name: 'lit',
    //     display: 'Lit',
    //     color: lightRed,
    //     variants: [
    //         {
    //             name: 'lit',
    //             display: 'JavaScript',
    //             color: yellow,
    //         },
    //         {
    //             name: 'lit-ts',
    //             display: 'TypeScript',
    //             color: blue,
    //         },
    //     ],
    // },
    // {
    //     name: 'svelte',
    //     display: 'Svelte',
    //     color: red,
    //     variants: [
    //         {
    //             name: 'svelte',
    //             display: 'JavaScript',
    //             color: yellow,
    //         },
    //         {
    //             name: 'svelte-ts',
    //             display: 'TypeScript',
    //             color: blue,
    //         },
    //         {
    //             name: 'custom-svelte-kit',
    //             display: 'SvelteKit ↗',
    //             color: red,
    //             customCommand: 'npm create svelte@latest TARGET_DIR',
    //         },
    //     ],
    // },
    // {
    //     name: 'others',
    //     display: 'Others',
    //     color: reset,
    //     variants: [
    //         {
    //             name: 'create-vite-extra',
    //             display: 'create-vite-extra ↗',
    //             color: reset,
    //             customCommand: 'npm create vite-extra@latest TARGET_DIR',
    //         },
    //     ],
    // },
];
const TEMPLATES = FRAMEWORKS.map(f => (f.variants && f.variants.map(v => v.name)) || [f.name]).reduce((a, b) => a.concat(b), []);
const renameFiles = {
    _gitignore: '.gitignore',
    _npmrc: '.npmrc',
    _env: '.env',
};
const defaultTargetDir = 'zerone-project';
async function init() {
    const argTargetDir = formatTargetDir(argv._[0]);
    const argTemplate = argv.template || argv.t;
    let targetDir = argTargetDir || defaultTargetDir;
    const getProjectName = () => (targetDir === '.' ? node_path_1.default.basename(node_path_1.default.resolve()) : targetDir);
    let result;
    try {
        result = await (0, prompts_1.default)([
            {
                type: argTargetDir ? null : 'text',
                name: 'projectName',
                message: (0, kolorist_1.reset)('Project name:'),
                initial: defaultTargetDir,
                onState: state => {
                    targetDir = formatTargetDir(state.value) || defaultTargetDir;
                },
            },
            {
                type: () => (!node_fs_1.default.existsSync(targetDir) || isEmpty(targetDir) ? null : 'confirm'),
                name: 'overwrite',
                message: () => (targetDir === '.' ? 'Current directory' : `Target directory "${targetDir}"`) +
                    ` is not empty. Remove existing files and continue?`,
            },
            {
                type: (_, { overwrite }) => {
                    if (overwrite === false) {
                        throw new Error((0, kolorist_1.red)('✖') + ' Operation cancelled');
                    }
                    return null;
                },
                name: 'overwriteChecker',
            },
            {
                type: () => (isValidPackageName(getProjectName()) ? null : 'text'),
                name: 'packageName',
                message: (0, kolorist_1.reset)('Package name:'),
                initial: () => toValidPackageName(getProjectName()),
                validate: dir => isValidPackageName(dir) || 'Invalid package.json name',
            },
            {
                type: argTemplate && TEMPLATES.includes(argTemplate) ? null : 'select',
                name: 'framework',
                message: typeof argTemplate === 'string' && !TEMPLATES.includes(argTemplate)
                    ? (0, kolorist_1.reset)(`"${argTemplate}" isn't a valid template. Please choose from below: `)
                    : (0, kolorist_1.reset)('Select a framework:'),
                initial: 0,
                choices: FRAMEWORKS.map(framework => {
                    const frameworkColor = framework.color;
                    return {
                        title: frameworkColor(framework.display || framework.name),
                        value: framework,
                    };
                }),
            },
            {
                type: (framework) => (framework && framework.variants ? 'select' : null),
                name: 'variant',
                message: (0, kolorist_1.reset)('Select a variant:'),
                choices: (framework) => framework.variants.map(variant => {
                    const variantColor = variant.color;
                    return {
                        title: variantColor(variant.display || variant.name),
                        value: variant.name,
                    };
                }),
            },
        ], {
            onCancel: () => {
                throw new Error((0, kolorist_1.red)('✖') + ' Operation cancelled');
            },
        });
    }
    catch (cancelled) {
        console.log(cancelled.message);
        return;
    }
    // user choice associated with prompts
    const { framework, overwrite, packageName, variant } = result;
    const root = node_path_1.default.join(cwd, targetDir);
    if (overwrite) {
        emptyDir(root);
    }
    else if (!node_fs_1.default.existsSync(root)) {
        node_fs_1.default.mkdirSync(root, { recursive: true });
    }
    // determine template
    const template = variant || framework?.name || argTemplate;
    const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent);
    const pkgManager = pkgInfo ? pkgInfo.name : 'npm';
    const isYarn1 = pkgManager === 'yarn' && pkgInfo?.version.startsWith('1.');
    const { customCommand } = FRAMEWORKS.flatMap(f => f.variants).find(v => v.name === template) ?? {};
    if (customCommand) {
        const fullCustomCommand = customCommand
            .replace('TARGET_DIR', targetDir)
            .replace(/^npm create/, `${pkgManager} create`)
            // Only Yarn 1.x doesn't support `@version` in the `create` command
            .replace('@latest', () => (isYarn1 ? '' : '@latest'))
            .replace(/^npm exec/, () => {
            // Prefer `pnpm dlx` or `yarn dlx`
            if (pkgManager === 'pnpm') {
                return 'pnpm dlx';
            }
            if (pkgManager === 'yarn' && !isYarn1) {
                return 'yarn dlx';
            }
            // Use `npm exec` in all other cases,
            // including Yarn 1.x and other custom npm clients.
            return 'npm exec';
        });
        const [command, ...args] = fullCustomCommand.split(' ');
        const { status } = cross_spawn_1.default.sync(command, args, {
            stdio: 'inherit',
        });
        process.exit(status ?? 0);
    }
    console.log(`\nScaffolding project in ${root}...`);
    // const templateDir = path.resolve(fileURLToPath(import.meta.url), '../..', `template-${template}`);
    const templateDir = node_path_1.default.join(__dirname, '..', `templates/${template}`);
    const write = (file, content) => {
        const targetPath = node_path_1.default.join(root, renameFiles[file] ?? file);
        if (content) {
            node_fs_1.default.writeFileSync(targetPath, content);
        }
        else {
            copy(node_path_1.default.join(templateDir, file), targetPath);
        }
    };
    const files = node_fs_1.default.readdirSync(templateDir);
    for (const file of files.filter(f => f !== 'package.json')) {
        write(file);
    }
    const pkg = JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.join(templateDir, `package.json`), 'utf-8'));
    pkg.name = packageName || getProjectName();
    write('package.json', JSON.stringify(pkg, null, 2));
    console.log(`\nDone. Now run:\n`);
    if (root !== cwd) {
        console.log(`  cd ${node_path_1.default.relative(cwd, root)}`);
    }
    switch (pkgManager) {
        case 'yarn':
            console.log('  yarn');
            console.log('  yarn dev');
            break;
        default:
            console.log(`  ${pkgManager} install`);
            console.log(`  ${pkgManager} run dev`);
            break;
    }
    console.log();
}
function formatTargetDir(targetDir) {
    return targetDir?.trim().replace(/\/+$/g, '');
}
function copy(src, dest) {
    const stat = node_fs_1.default.statSync(src);
    if (stat.isDirectory()) {
        copyDir(src, dest);
    }
    else {
        node_fs_1.default.copyFileSync(src, dest);
    }
}
function isValidPackageName(projectName) {
    return /^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(projectName);
}
function toValidPackageName(projectName) {
    return projectName
        .trim()
        .toLowerCase()
        .replace(/\s+/g, '-')
        .replace(/^[._]/, '')
        .replace(/[^a-z\d\-~]+/g, '-');
}
function copyDir(srcDir, destDir) {
    node_fs_1.default.mkdirSync(destDir, { recursive: true });
    for (const file of node_fs_1.default.readdirSync(srcDir)) {
        const srcFile = node_path_1.default.resolve(srcDir, file);
        const destFile = node_path_1.default.resolve(destDir, file);
        copy(srcFile, destFile);
    }
}
function isEmpty(path) {
    const files = node_fs_1.default.readdirSync(path);
    return files.length === 0 || (files.length === 1 && files[0] === '.git');
}
function emptyDir(dir) {
    if (!node_fs_1.default.existsSync(dir)) {
        return;
    }
    for (const file of node_fs_1.default.readdirSync(dir)) {
        if (file === '.git') {
            continue;
        }
        node_fs_1.default.rmSync(node_path_1.default.resolve(dir, file), { recursive: true, force: true });
    }
}
function pkgFromUserAgent(userAgent) {
    if (!userAgent)
        return undefined;
    const pkgSpec = userAgent.split(' ')[0];
    const pkgSpecArr = pkgSpec.split('/');
    return {
        name: pkgSpecArr[0],
        version: pkgSpecArr[1],
    };
}
init().catch(e => {
    console.error(e);
});
