企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
### 目录结构 |-- ProjectFolder |-- .gitignore |-- .npmrc |-- package.json |-- qz-init.js |-- README.md |-- lib | |-- helper.js | |-- update-check.js |-- templates |-- xxxx ### 插件 * [`ejs`](https://www.npmjs.com/package/ejs) 模板插入 * [`prompts`](https://www.npmjs.com/package/prompts) 命令行问答式窗口 * [`update-check`](https://www.npmjs.com/package/update-check) 检查插件是否有更新 ### 整体思路 将不同的模板文件放到`templates`目录下,通过命令行问答的方式,选择不同的类型,进而下载对应的模板; ### `package.json` 配置`bin:{"cli":"./qz-init.js"}` ### `init.js` ```js #! /usr/bin/env node const path = require('path') const fs = require('fs') const prompts = require('prompts') const ejs = require('ejs') const chalk = require('chalk') const updateCheck = require('./lib/update-check') const pkg = require('./package') const helpers = require('./lib/helper') // Prepare container for response data const responses = { update: false, mode: '', npmName: '', language: '', savePath: '', } // Create function to display update notification at script completion function displayUpdateMessage() { /* eslint-disable no-console */ if (responses.update) { const {latest} = responses.update if (latest) console.log( `\r\n${chalk.black.bgGreen(' UPDATE AVAILABLE ')}${chalk.red( '-->' )} The latest version of ${pkg.name} is ${chalk.yellow(latest)}\r\n` ) } /* eslint-enable no-console */ } // Create cancel function to exit the process function onCancel() { // eslint-disable-next-line no-console console.log('User canceled. Goodbye!') displayUpdateMessage() process.exit() } // Create async prompt functions async function checkForUpdates() { let update = null try { update = await updateCheck(pkg) } catch (err) { const errIntro = ` ${pkg.name} failed to check for updates ` // eslint-disable-next-line no-console console.error( `\r\n${chalk.black.bgRed(errIntro)}${chalk.red('-->')} ${err}\r\n` ) update = null } if (update) { responses.update = update } } async function getMode() { const question = { type: 'select', name: 'mode', message: 'Is this a canoe application?', choices: [{title: 'Canoe Application', value: 'canoe'}], initial: 0, } const response = await prompts(question, {onCancel}) responses.mode = response.mode } async function getName() { const {mode} = responses let tmpKebabName = '' const questions = [ { type: 'text', name: 'npmName', message: `What is the npm name of your ${mode}?`, validate(val) { const kebabName = helpers.kebabcase(val).trim() return kebabName !== '' }, }, ] const response = await prompts(questions, { onSubmit(prompt, answer) { if (prompt.name === 'npmName') tmpKebabName = helpers.kebabcase(answer).trim() }, onCancel, }) responses.npmName = response.npmName responses.savePath = `./${tmpKebabName}` } async function getLanguage() { const {mode} = responses const question = { type: 'select', name: 'language', message: `Will this ${mode} be written in JavaScript or TypeScript?`, choices: [ // {title: 'JavaScript', value: 'js'}, {title: 'TypeScript', value: 'ts'}, ], initial: 0, } const response = await prompts(question, {onCancel}) responses.language = response.language } async function getSavePath() { const {mode, savePath} = responses const questions = [ { type: 'text', name: 'savePath', message: `Enter a location to save the ${mode} files:`, initial() { return savePath }, validate: (val) => { // Validate path does not exist, then create directory const pathExists = fs.existsSync(val) return !pathExists }, }, ] const response = await prompts(questions, { onCancel, }) responses.savePath = path.resolve(response.savePath) return response } let template_files = [] function findAllFiles(srcPath) { let paths = fs.readdirSync(srcPath) paths.forEach((pathItem) => { if (pathItem === 'node_modules') return if (pathItem === 'package.json') return let fullPath = path.join(srcPath, pathItem) let stat = fs.statSync(fullPath) if (stat.isFile()) { template_files.push(fullPath) } else if (stat.isDirectory()) { findAllFiles(fullPath) } }) } // Create function to scaffold based on response data function scaffold(data) { // Stop prompting for input, start processing const vars = { npmName: data.npmName, ts: data.language === 'ts', } let srcPath = path.join.apply(null, [ __dirname, 'templates', data.mode === 'canoe' ? 'canoe' : '', ]) let destPath = path.join.apply(null, [data.savePath]) findAllFiles(srcPath) template_files.forEach((filePath) => { let relative_path = filePath.replace(srcPath, '') let target_absolute_path = path.join(destPath, relative_path) helpers.ensureDirectoryExists(target_absolute_path) if (filePath.indexOf('demo-package.json') > -1) { fs.writeFileSync( target_absolute_path.replace('demo-package.json', 'package.json'), ejs.render(fs.readFileSync(filePath).toString(), vars) ) } else { fs.writeFileSync(target_absolute_path) } }) // Display completion messages let completeMessage if (data.mode === 'canoe') { completeMessage = ` Init is complete, your files have been generated and saved into the directory you specified above. When you're ready, run \`yarn install && yarn run serve\` to start your application.` } // eslint-disable-next-line no-console console.log(completeMessage) } let args = process.argv.slice(2) if (args[0] === '-v' || args[0] === '--version') { console.log(`v${pkg.version}`) } else if (args[0] === '-h' || args[0] === '--help') { console.log(' usage:\n') console.log(' -v --version [show version]') } else { // Begin asking for input, then scaffold checkForUpdates() .then(displayUpdateMessage) .then(getMode) .then(getName) .then(getLanguage) .then(getSavePath) .then(() => { scaffold(responses) displayUpdateMessage() }) } ```