The target

  1. Minimum code implementation
  2. Select package manager, NPM source function
  3. Plug-in, free to expand

Enter the command to prepare to create the project

  1. User commands are parsed and interactive statements are displayed for users to choose
  2. The scaffold creates the package.json file
  3. Perform NPM I to install dependencies

Technical solutions:

1. Handle user commands: commander. Js (parse user commands and extract user input to scaffolding)

#! /usr/bin/env node const program = require('commander') const create = require('.. /lib/create') program.version ('0.1.0').command('gz <name>').description('create a new project').action(name => { create(name) }) program.parse()Copy the code

Register the gz command, put it in the project bin directory,cli.js, add “bin” : {” gz “:”./bin/cli.js “} to package.json, NPM link registers it as a global command.

2. Interact with users

Ask the user what functionality is required to create the project. With Inquirer. Js, a question and some options pop up for the user to choose from.

For example:

Const questions = [{type: "input", name: "compZhName", prefix: "Welcome to dvP-CLI quick build new components,", message: Validate (value) {if (value) return true;}}, {type: "rawList ", name: "CompClassify ", prefix:" Welcome to use DVP-CLI to quickly build new components,", message: "Please select component category ", Choices: [{name:" graph "}, {name: "control"}, {name: "Media"}, {name: "3 d"}, {name: "GIS"}, {name: "container"},]}] inquirer. Prompt (questions)Copy the code

3. Render the template

Use EJS and prettier

fs.writeFileSync(`${getCompPath()}/index.vue`, createVueTemplate(answer));
Copy the code
import fs from 'fs'; import ejs from 'ejs'; import path from 'path'; import { fileURLToPath } from "url"; import prettier from "prettier"; export default (config) => { const __dirname = fileURLToPath(import.meta.url); const template = fs.readFileSync(path.resolve(__dirname, ".. /template/view.ejs")); const code = ejs.render(template.toString(), { compEnName: config.compEnName, }) return prettier.format(code, { "trailingComma": "none", parser: "vue" }); }Copy the code
{" name ":" < % = packageName % > ", "version" : "0.1.0 from", "private" : true, "scripts" : {" dev ": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint" <% if(middleware.husky) { %> ,"prepare": "husky install" <% } %> }, "dependencies": { "vue": "3.2.8", "lint - staged" : "^ 12.3.2", "axios" : "^ 0.25.0", "vue - the router" : "4.0.11", "vuex" : "4.0.2" < % if (middleware. Dayjs) {% >, "dayjs" : "^" 1.10.7} < % % > < % if (middleware. Driver) {% >, "driver. Js" : "^" 0.9.8} < % % > < % if (middleware. Element) {% >, "element - plus" : "^ 1.3.0 - beta. 9"} < % % > < % if (middleware. Screenfull) {% >, "screenfull" : "5.1.0" < %} % > < % if (middleware. I18n) {% >, "vue - i18n" : "^ 9.2.0 - beta. 30" % > < %}}, "devDependencies" : {" @ vue/cli - plugin - Babel ":" ~ 4.5.0 ", "@ vue/cli - service" : "~ 4.5.0", "@ vue/compiler - SFC" : "^ 3.0.0", "core - js" : "^ 3.20.3 cz - customizable", "" :" ^ 6.3.0 ", "SVG - Sprite - loader" : "^ 6.0.9", "unplugin - auto - import" : "^ 0.5.11 unplugin - vue -", "components" : "^ 0.17.15", "@ vue/cli - plugin - the router" : "~ 4.5.0", "@ vue/cli - plugin - vuex" : "~ 4.5.0 sass", ""," ^ 1.26.5 ", "sass - loader" : "^ 8.0.2" < % if (middleware. Husky) {% >, "husky" : "7.0.1"} < % % > < % if (middleware. Eslint) {% >, "eslint" : "^ 6.7.2", "@ vue/cli - plugin - eslint" : "~ 4.5.0 @", "vue/eslint - config - standard" : "^ 5.1.2", "Babel - eslint" : "^ 10.1.0", "eslint - plugin - import" : "^ 2.20.2 eslint - plugin -", "node" : "^ 11.1.0", "eslint - plugin - promise" : "^ 2", "eslint - plugin - standard" : "^ 4.0.0 eslint - plugin -", "vue" : "^" 7.0.0} < % % > < % if mitLink (middleware.com) {% >, "@ commitlint/cli" : "12.1.4" < %} % > < % if mitLink (middleware.com) {% >, "@ commitlint/config - but" : "^ 12.1.4" % > < %}}, "config" : { "commitizen": { "path": "node_modules/cz-customizable" } }, "lint-staged": { "src/**/*.{js,vue}": [ "eslint --fix", "git add" ] } }Copy the code

4. Download dependencies

Execa, which can call child processes to execute commands.

Execa (" NPM I ", {CWD: getRootPath(), stdio: [2, 2, 2]}) console.log('\n dependencies download complete! Run the following command to start development: \n') console.log(' CD ${name} ') console.log(' NPM run dev ')Copy the code

Call executeCommand() to start downloading dependencies with NPM install and the project path created by the user. In order for the user to see the process of downloading dependencies, we need to pass the output of the child process to the main process, i.e. to the console, using the following code:

child.stdout.on('data', buffer => {
    process.stdout.write(buffer)
})
Copy the code

5. Improve your future

  1. Determine whether an item already exists when creating it, supporting overwrite and merge creation. (fs. ExistsSync (targetDir))
  2. There are two modes for selecting functions: default configuration and manual selection.
  3. If the user’s environment has both YARN and NPM, the user is prompted which package manager to use.
  4. If NPM’s default source speed is slow, it prompts the user whether to switch to taobao source.
  5. If the user manually selects the feature, the user is asked if they want to save the selection as the default.

Reference data: https://github.com/vuejs/vue-cli

Finished!