After three months, I finally had time to write the second article in the scaffolding series. Working in Beijing is indeed much busier than in Tianjin, and I don't have time to fish. If you haven't read the first article in this series, you to write a scaffolding , it is recommended to read this article first, and the effect will be better.
mini-cli project github address: https://github.com/woai3c/mini-cli
The v3 version of the code is in the v3 branch, and the v4 version of the code is in the v4 branch.
The third version v3
The third version mainly adds two functions:
- How to split the project into monorepo.
- Added the add command, you can add plug-ins
mvc add xxx
monorepo
First, let's take a brief look at monorepo and multirepo. They are all a way of project management. Multirepo means to maintain different projects in different git repositories, while monorepo maintains multiple projects in the same git repository. In the v3 version, I will transform mini-cli into a monorepo method, and maintain different plug-ins as independent projects.
After transforming the project into monorepo, the directory is as follows:
├─packages
│ ├─@mvc
│ │ ├─cli # 核心插件
│ │ ├─cli-plugin-babel # babel 插件
│ │ ├─cli-plugin-linter # linter 插件
│ │ ├─cli-plugin-router # router 插件
│ │ ├─cli-plugin-vue # vue 插件
│ │ ├─cli-plugin-vuex # vuex 插件
│ │ └─cli-plugin-webpack # webpack 插件
└─scripts # commit message 验证脚本 和项目无关 不需关注
│─lerna.json
|─package.json
Monorepo transformation process
Install lerna globally
npm install -g lerna
Create project
git init mini-cli
initialization
cd mini-cli
lerna init
Create package
lerna create xxx
Since cli
is the core scaffolding code, other plugins need to be called here, because other plugins need to be added to the dependencies of @mvc/cli
# 如果是添加到 devDependencies,则需要在后面加上 --dev
# 下载第三方依赖也是同样的命令
lerna add @mvc/cli-plugin-babel --scope=@mvc/cli
The scaffolding function after transformation into monorepo-repo is no different from the second version, except that the code related to the plug-in is separated into a separate repo, and the plug-in can be published separately to npm in the future.
Advantages of using monorepo
- If you use multirepo to develop, if you need to call other plug-ins during local debugging, you need to
npm i
before you can use it. Using monorepo, there is no such trouble, you can directly call other plug-ins in the packages directory to facilitate development and debugging. - If multiple plug-ins have been modified, the modified plug-ins can be released at the same time when
lerna publish
add command
The purpose of transforming the project into monorepo-repo is to facilitate subsequent expansion. For example, the generated project does not support router, and suddenly want to add router function in the middle, you can execute command mvc add router
add vue-router
dependency and related template code.
Let's take a look at the code of the add command first:
const path = require('path')
const inquirer = require('inquirer')
const Generator = require('./Generator')
const clearConsole = require('./utils/clearConsole')
const PackageManager = require('./PackageManager')
const getPackage = require('./utils/getPackage')
const readFiles = require('./utils/readFiles')
async function add(name) {
const targetDir = process.cwd()
const pkg = getPackage(targetDir)
// 清空控制台
clearConsole()
let answers = {}
try {
const pluginPrompts = require(`@mvc/cli-plugin-${name}/prompts`)
answers = await inquirer.prompt(pluginPrompts)
} catch (error) {
console.log(error)
}
const generator = new Generator(pkg, targetDir, await readFiles(targetDir))
const pm = new PackageManager(targetDir, answers.packageManager)
require(`@mvc/cli-plugin-${name}/generator`)(generator, answers)
await generator.generate()
// 下载依赖
await pm.install()
}
module.exports = add
Since the v3 version is still developed locally, the relevant plug-ins are not published on npm, because the plug-ins can be directly referenced without the need to perform npm i
installation. When you execute the create
command to create a project in the v2 version, all interactive prompts are placed under the cli
plugin, but the add command adds a single plugin, so you also need to add a prompts.js
file under each plugin (if not needed, You don’t need to add), there are some statements that interact with the user. For example, when you use the add command to add a router plug-in, you will be asked whether to select the history mode.
const chalk = require('chalk')
module.exports = [
{
name: 'historyMode',
type: 'confirm',
message: `Use history mode for router? ${chalk.yellow(`(Requires proper server setup for index fallback in production)`)}`,
description: `By using the HTML5 History API, the URLs don't need the '#' character anymore.`,
},
]
It can be seen from the code logic of the add command that if the newly added plug-in has a prompts.js file, it will read the code and pop up the interactive statement. Otherwise, skip and download directly.
Fourth version v4
The v4 version mainly makes webpack's dev and build functions dynamic. The original scaffolding project has a build directory, which contains some configuration codes of webpack. The project generated by the v4 version of the scaffold does not have a build directory.
This function is implemented by the newly added mvc-cli-service
plug-in. The generated project will have the following two script commands:
scripts: {
serve: 'mvc-cli-service serve',
build: 'mvc-cli-service build',
},
When running npm run serve
, the command mvc-cli-service serve
will be executed. The code for this piece is as follows:
#!/usr/bin/env node
const webpack = require('webpack')
const WebpackDevServer = require('webpack-dev-server')
const devConfig = require('../lib/dev.config')
const buildConfig = require('../lib/pro.config')
const args = process.argv.slice(2)
if (args[0] === 'serve') {
const compiler = webpack(devConfig)
const server = new WebpackDevServer(compiler)
server.listen(8080, '0.0.0.0', err => {
console.log(err)
})
} else if (args[0] === 'build') {
webpack(buildConfig, (err, stats) => {
if (err) console.log(err)
if (stats.hasErrors()) {
console.log(new Error('Build failed with errors.'))
}
})
} else {
console.log('error command')
}
The principle is as follows ( npm scripts user guide ):
The principle of npm scripts is very simple. Whenever npm run is executed, a new Shell will be created automatically, and the specified script commands will be executed in this Shell. Therefore, as long as it is a command that can be run by Shell (usually Bash), it can be written in an npm script.What's more special is that the shell created by npm run will add the node_modules/.bin subdirectory of the current directory to the PATH variable. After the execution is over, the PATH variable will be restored to its original shape.
The above code judges the executed command, if it is serve
, a new WebpackDevServer
start the development environment. If it is build
, use webpack for packaging.
The webpack configuration of vue-cli is dynamic, and chainwebpack is used to dynamically add different configurations. My demo was written to death, mainly because I didn't have time, so I didn't further study it.
After publishing to npm
Download mini-cli
scaffolding, in fact, only the core plug-in mvc-cli
is downloaded. If this plug-in needs to reference other plug-ins, you need to install it before calling it. Therefore, some modifications need to be made create
add
Let's take a look at the changes to the create
answers.features.forEach(feature => {
if (feature !== 'service') {
pkg.devDependencies[`mvc-cli-plugin-${feature}`] = '~1.0.0'
} else {
pkg.devDependencies['mvc-cli-service'] = '~1.0.0'
}
})
await writeFileTree(targetDir, {
'package.json': JSON.stringify(pkg, null, 2),
})
await pm.install()
// 根据用户选择的选项加载相应的模块,在 package.json 写入对应的依赖项
// 并且将对应的 template 模块渲染
answers.features.forEach(feature => {
if (feature !== 'service') {
require(`mvc-cli-plugin-${feature}/generator`)(generator, answers)
} else {
require(`mvc-cli-service/generator`)(generator, answers)
}
})
await generator.generate()
// 下载依赖
await pm.install()
The above code is the new logic. After the user selects the required plug-ins, write these plug-ins to the pkg
object, then generate the package.json
file, and then execute npm install
install the dependencies. After installing the plug-in, read the generator
directory/file code of each plug-in to generate a template or add different dependencies again. Then perform the installation again.
Publish the pits encountered
The v3 version of the plug-in has a prefix of @mvc
. Since the npm package with the @ prefix will be a private package by default, it has encountered some pitfalls. It took a long time, and then I was too lazy to make it. I simply changed the prefix of all the plug-ins to the prefix mvc
.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。