UNPKG

create-zephyr-apps

Version:

A CLI tool to create web applications using Zephyr.

187 lines (183 loc) 7.72 kB
import { parseArgs } from "node:util"; import { DEFAULT_WEB_TEMPLATE, ProjectTypes, TemplateRepositories, Templates, getTemplate } from "./templates.js"; const PackageManagers = [ 'pnpm', 'npm', 'yarn', 'bun' ]; class OperationCancelled extends Error { constructor(){ super('Operation cancelled.'); this.name = 'OperationCancelled'; } } const NON_INTERACTIVE_EXAMPLE_ARGS = [ './apps/example', '--template', 'react-rsbuild', '--package-manager', 'pnpm', '--no-git', '--install', '--build', '--json' ]; const NON_INTERACTIVE_EXAMPLE = `create-zephyr-apps ${NON_INTERACTIVE_EXAMPLE_ARGS.join(' ')}`; const HELP_TEXT = ` Usage: create-zephyr-apps [directory] [options] Create a Zephyr application from a version-pinned template. Options: --directory, -d <path> Project directory (alternative to positional) --template, -t <id> Web template ID (default: ${DEFAULT_WEB_TEMPLATE}) --project-type <type> web or react-native (default: web) --package-manager <manager> pnpm, npm, yarn, or bun --template-revision <commit> Override the pinned template with a full commit SHA --git Initialize Git and create an initial commit --no-git Do not initialize Git --install Install dependencies --build Install dependencies and run the build script --json Emit one machine-readable JSON result --yes, -y Use deterministic defaults without prompting --list-templates List available web template IDs --version, -v Print the CLI version --help, -h Show this help Example: ${NON_INTERACTIVE_EXAMPLE} `.trim(); function parseCliArgs(args) { const parsed = parseArgs({ args, allowPositionals: true, strict: true, options: { directory: { type: 'string', short: 'd' }, template: { type: 'string', short: 't' }, 'project-type': { type: 'string' }, 'package-manager': { type: 'string' }, 'template-revision': { type: 'string' }, git: { type: 'boolean' }, 'no-git': { type: 'boolean' }, install: { type: 'boolean' }, build: { type: 'boolean' }, json: { type: 'boolean' }, yes: { type: 'boolean', short: 'y' }, 'list-templates': { type: 'boolean' }, version: { type: 'boolean', short: 'v' }, help: { type: 'boolean', short: 'h' } } }); if (parsed.positionals.length > 1) throw new Error('Only one positional project directory is supported.'); const positionalDirectory = parsed.positionals[0]; const flagDirectory = parsed.values.directory; if (positionalDirectory && flagDirectory) throw new Error('Provide the project directory either positionally or with --directory, not both.'); if (parsed.values.git && parsed.values['no-git']) throw new Error('--git and --no-git cannot be used together.'); const projectType = parsed.values['project-type']; if (void 0 !== projectType && !ProjectTypes.some((candidate)=>candidate.value === projectType)) throw new Error(`Unsupported project type "${projectType}". Expected web or react-native.`); const packageManager = parsed.values['package-manager']; if (void 0 !== packageManager && !PackageManagers.includes(packageManager)) throw new Error(`Unsupported package manager "${packageManager}". Expected ${PackageManagers.join(', ')}.`); const template = parsed.values.template; if (void 0 !== template && !getTemplate(template)) throw new Error(`Unknown template "${template}". Available templates: ${Templates.map(({ name })=>name).join(', ')}.`); if ('react-native' === projectType && void 0 !== template) throw new Error('--template is only supported for web projects.'); const templateRevision = parsed.values['template-revision']; if (void 0 !== templateRevision && !/^[a-f\d]{40}$/iu.test(templateRevision)) throw new Error('--template-revision must be a full 40-character commit SHA.'); return { directory: flagDirectory ?? positionalDirectory, template, projectType: projectType, packageManager: packageManager, initializeGit: parsed.values.git ? true : parsed.values['no-git'] ? false : void 0, install: parsed.values.install ?? false, build: parsed.values.build ?? false, json: parsed.values.json ?? false, yes: parsed.values.yes ?? false, templateRevision, listTemplates: parsed.values['list-templates'] ?? false, help: parsed.values.help ?? false, version: parsed.values.version ?? false }; } function shouldUseInteractiveMode(options, terminal) { return void 0 === options.directory && !options.yes && !options.json && Boolean(terminal.inputIsTTY && terminal.outputIsTTY); } async function resolveCliOptions(options, context) { const prompts = context.prompts; let directory = options.directory; if (void 0 === directory && context.interactive && prompts) { directory = await prompts.directory(); if (void 0 === directory) throw new OperationCancelled(); } if (!directory?.trim()) throw new Error('A project directory is required in non-interactive mode. Pass it positionally or with --directory.'); let projectType = options.projectType ?? (options.template ? 'web' : void 0); if (void 0 === projectType && context.interactive && prompts) { projectType = await prompts.projectType(); if (void 0 === projectType) throw new OperationCancelled(); } projectType ??= 'web'; let template = 'web' === projectType ? options.template : void 0; if ('web' === projectType && void 0 === template && context.interactive && prompts) { template = await prompts.template(); if (void 0 === template) throw new OperationCancelled(); } template ??= 'web' === projectType ? DEFAULT_WEB_TEMPLATE : void 0; let initializeGit = options.initializeGit; if (void 0 === initializeGit && context.interactive && prompts) { initializeGit = await prompts.initializeGit(); if (void 0 === initializeGit) throw new OperationCancelled(); } initializeGit ??= false; const repository = TemplateRepositories[projectType]; return { directory, template, projectType, packageManager: options.packageManager, initializeGit, install: options.install || options.build, build: options.build, json: options.json, templateRevision: options.templateRevision ?? repository.revision }; } function listTemplatesJson() { return JSON.stringify({ default: DEFAULT_WEB_TEMPLATE, revision: TemplateRepositories.web.revision, templates: Templates }, null, 2); } export { HELP_TEXT, NON_INTERACTIVE_EXAMPLE, NON_INTERACTIVE_EXAMPLE_ARGS, OperationCancelled, PackageManagers, listTemplatesJson, parseCliArgs, resolveCliOptions, shouldUseInteractiveMode }; //# sourceMappingURL=cli.js.map