UNPKG

@tanstack/cli

Version:
171 lines (170 loc) 7.09 kB
import { intro } from '@clack/prompts'; import { finalizeAddOns, getFrameworkById, getPackageManager, loadStarter, populateAddOnOptionsDefaults, readConfigFile, } from '@tanstack/create'; import { getProjectName, promptForAddOnOptions, promptForEnvVars, selectAddOns, selectDeployment, selectExamples, selectGit, selectPackageManager, selectTemplate, selectToolchain, } from './ui-prompts.js'; import { listTemplateChoices, resolveStarterSpecifier, } from './command-line.js'; import { getCurrentDirectoryName, sanitizePackageName, validateProjectName, } from './utils.js'; export async function promptForCreateOptions(cliOptions, { forcedAddOns = [], showDeploymentOptions = false, }) { const options = {}; options.framework = getFrameworkById(cliOptions.framework || 'react'); // Validate project name if (cliOptions.projectName) { // Handle "." as project name - use sanitized current directory name if (cliOptions.projectName === '.') { options.projectName = sanitizePackageName(getCurrentDirectoryName()); } else { options.projectName = cliOptions.projectName; } const { valid, error } = validateProjectName(options.projectName); if (!valid) { console.error(error); process.exit(1); } } else { options.projectName = await getProjectName(); } // Mode is always file-router (TanStack Start) options.mode = 'file-router'; const template = cliOptions.template?.toLowerCase().trim(); const isLegacyTemplate = template && ['file-router', 'typescript', 'tsx', 'javascript', 'js', 'jsx'].includes(template); const routerOnly = !!cliOptions.routerOnly || (isLegacyTemplate ? template !== 'file-router' : false); if (!cliOptions.starter) { if (cliOptions.template && !isLegacyTemplate) { cliOptions.starter = cliOptions.template; } else if (cliOptions.templateId) { cliOptions.starter = cliOptions.templateId; } } if (!routerOnly && !cliOptions.starter) { const starterChoices = await listTemplateChoices(options.framework.id); const selectedTemplateId = await selectTemplate(starterChoices.map((choice) => ({ id: choice.id, name: choice.name, description: choice.description, }))); if (selectedTemplateId) { cliOptions.starter = selectedTemplateId; } } const starter = !routerOnly && cliOptions.starter ? await loadStarter(await resolveStarterSpecifier(cliOptions.starter, options.framework.id)) : undefined; if (starter) { options.framework = getFrameworkById(starter.framework) || options.framework; options.mode = starter.mode; } // TypeScript is always enabled with file-router options.typescript = true; // Package manager selection if (cliOptions.packageManager) { options.packageManager = cliOptions.packageManager; } else { const detectedPackageManager = await getPackageManager(); options.packageManager = detectedPackageManager || (await selectPackageManager()); } // Toolchain selection const toolchain = await selectToolchain(options.framework, cliOptions.toolchain); // Deployment selection const deployment = showDeploymentOptions ? routerOnly ? undefined : await selectDeployment(options.framework, cliOptions.deployment) : undefined; // Add-ons selection const addOns = new Set(); // Examples/demo pages are enabled by default const includeExamples = cliOptions.examples ?? (routerOnly ? false : await selectExamples()); options.includeExamples = includeExamples; if (toolchain) { addOns.add(toolchain); } if (deployment) { addOns.add(deployment); } if (!routerOnly) { for (const addOn of starter?.dependsOn || []) { addOns.add(addOn); } for (const addOn of forcedAddOns) { addOns.add(addOn); } } if (!routerOnly && Array.isArray(cliOptions.addOns)) { for (const addOn of cliOptions.addOns) { if (addOn.toLowerCase() === 'start') { continue; } addOns.add(addOn); } } else if (!routerOnly) { for (const addOn of await selectAddOns(options.framework, options.mode, 'add-on', 'What add-ons would you like for your project?', forcedAddOns)) { addOns.add(addOn); } if (includeExamples) { for (const addOn of await selectAddOns(options.framework, options.mode, 'example', 'Would you like an example?', forcedAddOns, false)) { addOns.add(addOn); } } } const chosenAddOns = Array.from(await finalizeAddOns(options.framework, options.mode, Array.from(addOns))); options.chosenAddOns = includeExamples ? chosenAddOns : chosenAddOns.filter((addOn) => addOn.type !== 'example'); // Tailwind is always enabled options.tailwind = true; // Prompt for add-on options in interactive mode if (Array.isArray(cliOptions.addOns)) { // Non-interactive mode: use defaults options.addOnOptions = populateAddOnOptionsDefaults(options.chosenAddOns); } else { // Interactive mode: prompt for options const userOptions = await promptForAddOnOptions(options.chosenAddOns.map((a) => a.id), options.framework); const defaultOptions = populateAddOnOptionsDefaults(options.chosenAddOns); // Merge user options with defaults options.addOnOptions = { ...defaultOptions, ...userOptions }; } // Prompt for env vars exposed by selected add-ons in interactive mode const envVarValues = Array.isArray(cliOptions.addOns) ? {} : await promptForEnvVars(options.chosenAddOns); options.envVarValues = envVarValues; options.git = cliOptions.git ?? (await selectGit()); if (cliOptions.install === false) { options.install = false; } if (starter) { options.starter = starter; } return options; } export async function promptForAddOns() { const config = await readConfigFile(process.cwd()); if (!config) { console.error('No config file found'); process.exit(1); } const framework = getFrameworkById(config.framework); if (!framework) { console.error(`Unknown framework: ${config.framework}`); process.exit(1); } intro(`Adding new add-ons to '${config.projectName}'`); const addOns = new Set(); for (const addOn of await selectAddOns(framework, config.mode, 'add-on', 'What add-ons would you like for your project?', config.chosenAddOns)) { addOns.add(addOn); } for (const addOn of await selectAddOns(framework, config.mode, 'example', 'Would you like any examples?', config.chosenAddOns)) { addOns.add(addOn); } return Array.from(addOns); }