UNPKG

oclif

Version:

oclif: create your own CLI

166 lines (165 loc) 7.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const core_1 = require("@oclif/core"); const ansis_1 = require("ansis"); const promises_1 = require("node:fs/promises"); const node_path_1 = require("node:path"); const generator_1 = require("../generator"); const util_1 = require("../util"); const VALID_MODULE_TYPES = ['ESM', 'CommonJS']; const VALID_PACKAGE_MANAGERS = ['npm', 'yarn', 'pnpm']; function isPackageManager(d) { return VALID_PACKAGE_MANAGERS.includes(d); } function isModuleType(d) { return VALID_MODULE_TYPES.includes(d); } const FLAGGABLE_PROMPTS = { bin: { message: 'Command bin name the CLI will export', validate: (d) => (0, util_1.validateBin)(d) || 'Invalid bin name', }, 'module-type': { message: 'Select a module type', options: VALID_MODULE_TYPES, validate: (d) => isModuleType(d) || 'Invalid module type', }, 'package-manager': { message: 'Select a package manager', options: VALID_PACKAGE_MANAGERS, validate: (d) => isPackageManager(d) || 'Invalid package manager', }, 'topic-separator': { message: 'Select a topic separator', options: ['colons', 'spaces'], validate: (d) => d === 'colons' || d === 'spaces' || 'Invalid topic separator', }, }; class Generate extends generator_1.GeneratorCommand { static description = 'This will add the necessary oclif bin files, add oclif config to package.json, and install @oclif/core and ts-node.'; static examples = [ { command: '<%= config.bin %> <%= command.id %>', description: 'Initialize a new CLI in the current directory', }, { command: '<%= config.bin %> <%= command.id %> --output-dir "/path/to/existing/project"', description: 'Initialize a new CLI in a different directory', }, { command: '<%= config.bin %> <%= command.id %> --topic-separator colons --bin mycli', description: 'Supply answers for specific prompts', }, ]; static flaggablePrompts = FLAGGABLE_PROMPTS; static flags = { ...(0, generator_1.makeFlags)(FLAGGABLE_PROMPTS), 'output-dir': core_1.Flags.directory({ char: 'd', description: 'Directory to initialize the CLI in.', exists: true, }), yes: core_1.Flags.boolean({ aliases: ['defaults'], char: 'y', description: 'Use defaults for all prompts. Individual flags will override defaults.', }), }; static summary = 'Initialize a new oclif CLI'; async run() { const outputDir = this.flags['output-dir'] ?? process.cwd(); const location = (0, node_path_1.resolve)(outputDir); this.log(`Initializing oclif in ${(0, ansis_1.green)(location)}`); const packageJSON = (await (0, generator_1.readPJSON)(location)); if (!packageJSON) { throw new core_1.Errors.CLIError(`Could not find a package.json file in ${location}`); } const bin = await this.getFlagOrPrompt({ defaultValue: location.split(node_path_1.sep).at(-1) || '', name: 'bin', type: 'input', }); const topicSeparator = await this.getFlagOrPrompt({ defaultValue: 'spaces', name: 'topic-separator', type: 'select', }); const moduleType = await this.getFlagOrPrompt({ defaultValue: packageJSON.type === 'module' ? 'ESM' : 'CommonJS', async maybeOtherValue() { return packageJSON.type === 'module' ? 'ESM' : packageJSON.type === 'commonjs' ? 'CommonJS' : undefined; }, name: 'module-type', type: 'select', }); const packageManager = await this.getFlagOrPrompt({ defaultValue: 'npm', async maybeOtherValue() { const rootFiles = await (0, promises_1.readdir)(location); if (rootFiles.includes('package-lock.json')) { return 'npm'; } if (rootFiles.includes('yarn.lock')) { return 'yarn'; } if (rootFiles.includes('pnpm-lock.yaml')) { return 'pnpm'; } }, name: 'package-manager', type: 'select', }); this.log(`Using module type ${(0, ansis_1.green)(moduleType)}`); this.log(`Using package manager ${(0, ansis_1.green)(packageManager)}`); const projectBinPath = (0, node_path_1.join)(location, 'bin'); const templateBinPath = (0, node_path_1.join)(this.templatesDir, 'cli', moduleType.toLowerCase(), 'bin'); await this.template((0, node_path_1.join)(templateBinPath, 'dev.cmd.ejs'), (0, node_path_1.join)(projectBinPath, 'dev.cmd')); await this.template((0, node_path_1.join)(templateBinPath, 'dev.js.ejs'), (0, node_path_1.join)(projectBinPath, 'dev.js')); await this.template((0, node_path_1.join)(templateBinPath, 'run.cmd.ejs'), (0, node_path_1.join)(projectBinPath, 'run.cmd')); await this.template((0, node_path_1.join)(templateBinPath, 'run.js.ejs'), (0, node_path_1.join)(projectBinPath, 'run.js')); if (process.platform !== 'win32') { await (0, generator_1.exec)(`chmod +x "${(0, node_path_1.join)(projectBinPath, 'run.js')}"`); await (0, generator_1.exec)(`chmod +x "${(0, node_path_1.join)(projectBinPath, 'dev.js')}"`); } const updatedPackageJSON = { ...packageJSON, bin: { ...packageJSON.bin, [bin]: './bin/run.js', }, oclif: { bin, commands: './dist/commands', dirname: bin, topicSeparator: topicSeparator === 'colons' ? ':' : ' ', ...packageJSON.oclif, }, }; await (0, promises_1.writeFile)((0, node_path_1.join)(location, 'package.json'), JSON.stringify(updatedPackageJSON, null, 2)); const installedDeps = Object.keys(packageJSON.dependencies ?? {}); if (!installedDeps.includes('@oclif/core')) { this.log('Installing @oclif/core'); await (0, generator_1.exec)(`${packageManager} ${packageManager === 'yarn' ? 'add' : 'install'} @oclif/core`, { cwd: location, silent: false, }); } const allInstalledDeps = new Set([...installedDeps, ...Object.keys(packageJSON.devDependencies ?? {})]); if (!allInstalledDeps.has('ts-node')) { this.log('Installing ts-node'); await (0, generator_1.exec)(`${packageManager} ${packageManager === 'yarn' ? 'add --dev' : 'install --save-dev'} ts-node`, { cwd: location, silent: false, }); } if (!allInstalledDeps.has('@types/node')) { this.log('@types/node'); await (0, generator_1.exec)(`${packageManager} ${packageManager === 'yarn' ? 'add --dev' : 'install --save-dev'} @types/node@^18`, { cwd: location, silent: false, }); } this.log(`\nCreated CLI ${(0, ansis_1.green)(bin)}`); } } exports.default = Generate;