taro-cli解析
前言
taro 脚手架分别有:
- init // 初始化项目
- create // 创建页面
- doctor // 诊断项目
- info // 查看系统信息和版本信息等
- update // 更新依赖
- convert // 小程序转taro
- build // 编译taro项目
本章主要讲解前面五个的功能和实现,而build
和convert
则会放在taro-cli
原理系列的二和三,敬请期待~ 由于 官方也在维护,代码可能会更新,因此文中的代码难免会与最新源码有出入,
本文所解析taro-cli
的version
为1.3.21
,未来如果某些功能的分析文章与源码如果出入过大,则会更新对应的文章。
cli目录
首先我们看一下taro-cli的目录,该工程位于taro的packages目录下。当我们安装taro-cli后,会生成taro 命令。
用到的核心库
- tj/commander.js Node.js - 命令行接口全面的解决方案,灵感来自于 Ruby's commander。可以自动的解析命令和参数,合并多选项,处理短参等等,功能强大,上手简单。
- jprichardson/node-fs-extra - 在 Node.js 的 fs 基础上增加了一些新的方法,更好用,还可以拷贝模板。
- chalk/chalk - 可以用于控制终端输出字符串的样式。
- SBoudrias/Inquirer.js - Node.js 命令行交互工具,通用的命令行用户界面集合,可以和用户进行交互。
- sindresorhus/ora - 实现加载中的状态是一个 Loading 加前面转起来的小圈圈,成功了是一个 Success 加前面一个小钩钩。
- SBoudrias/mem-fs-editor - 提供一系列 API,方便操作模板文件。
- shelljs/shelljs - ShellJS 是 Node.js 扩展,用于实现 Unix shell 命令执行。
- Node.js child_process - 模块用于新建子进程。子进程的运行结果储存在系统缓存之中(最大 200KB),等到子进程运行结束以后,主进程再用回调函数读取子进程的运行结果。
如何在命令行中使用Taro
当我们在命令行中输入Taro
时,会有以下的输出:
�👽 Taro v1.2.13
Usage: taro <command> [options] ource\\todoList-Redux\\
Options: de
-V, --version output the version number
-h, --help output usage information
Commands: postinstall || echo \"
init [projectName] Init a project with default templete
build Build a project with options
update Update packages of taro
convert Convert weapp to taro
info Diagnostics Taro env info
help [cmd] display help for [cmd]
那么,像@vue/cli
,@wepy/cli
等,是如何让我们可以直接在命令行中使用呢?
原因在于, @taro/taro-cli
的package.json
中的bin
字段:
"bin": {
"taro": "bin/taro"
}
上面代码指定,taro 命令对应的可执行文件为 bin 子目录下的 taro.js。Npm会寻找这个文件,在node_modules/.bin/目录下建立符号链接。在上面的例子中,taro.js会建立符号链接npm_modules/.bin/taro。由于node_modules/.bin/目录会在运行时加入系统的PATH变量,因此在运行npm时,就可以不带路径,直接通过命令来调用这些脚本。如果只有一个命令,也可以直接简写成string,前提是命令名称和模块名称一样
。
{
"name": "taro",
"version": "1.2.5",
"bin": "bin/taro"
}
Taro Init
使用命令创建模板项目
taro init myApp
// npx @tarojs/cli init myApp
其中用到了commander
这个库,可以自动的解析命令和参数,合并多选项,处理短参等等。让我们来看一下它在init
命令中的用法:
// taro-cli/bin/taro-init
const program = require('commander')
program
.option('--name [name]', '项目名称')
.option('--description [description]', '项目介绍')
.option('--typescript', '使用TypeScript')
.option('--no-typescript', '不使用TypeScript')
.option('--template-source [templateSource]', '项目模板源')
.option('--clone [clone]', '拉取远程模板时使用git clone')
.option('--template [template]', '项目模板')
.option('--css [css]', 'CSS预处理器(sass/less/stylus/none)')
.parse(process.argv)
const { template, templateSource, clone, description, name, css } = program;
const project = new Project({
projectName,
projectDir: process.cwd(),
templateSource,
clone,
template,
description,
typescript,
css
})
代码较短,相信大家看了也是一目了然。这里就不详细说明。
重点讲一下,new Project
和 project.create()
所做的事情。
// taro-cli/src/create/project.ts
class Project extends Creator {
constructor (options: IProjectConf) {
super(options.sourceRoot)
this.conf = Object.assign({
projectName: '',
projectDir: '',
template: '',
description: ''
}, options)
}
}
new Project
时,在构造函数中传进我们通过命令行输入的参数,比如 -- description
。紧接着我们看一下create做了什么事情。
create () {
this.fetchTemplates()
.then((templateChoices: string[]) => this.ask(templateChoices))
.then(answers => {
const date = new Date()
this.conf = Object.assign(this.conf, answers)
this.conf.date = `${date.getFullYear()}-${(date.getMonth() + 1)}-${date.getDate()}`
this.write()
})
.catch(err => console.log(chalk.red('创建项目失败: ', err)))
}
- 获取模板。先从taroconfig中查看是否有模板的地址(templateSource),如果没有的话则使用默认的模板组,并通过
git clone
的方式下载(这里是使用了download-git-repo
这个库)
1.3.9 开始 Taro 会在用户根目录下创建 .taro 文件夹,其中 .taro/index.json 用于存放 CLI 相关配置。 开发者可以使用 taro config 命令对配置项进行一系列操作。说明
ask
(询问),即是通过inquirer
模块,创造命令行交互,确定(项目名,描述,是否用ts, css预处理器,模板)- 确定所需的选项后,使用
mem-fs-editor
的copyTpl
拷贝模板,模板使用ejs,并将所需要的选项传递进去。比如修改package.json中的name和description。
git init
初始化仓库和npm install
来安装依赖(当然,也可能是使用yarn源或者cnpm来安装),以下是判断使用哪个registry的代码
if (shouldUseYarn) {
command = 'yarn install'
} else if (helper.shouldUseCnpm()) {
command = 'cnpm install'
} else {
command = 'npm install'
}
export function shouldUseYarn (): boolean {
try {
execSync('yarn --version', { stdio: 'ignore' })
return true
} catch (e) {
return false
}
}
Taro create
快速创建新页面
Taro create --name [页面名称]
能够在当前项目的pages目录下快速生成新的页面文件,并填充基础代码,是一个提高开发效率的利器。
create的过程与init类似,这里就不做过多的阐述.
Taro doctor
在taro中,提供了5个Validator,分别是:- configValidator,
- packageValidator,
- recommandValidator,
- eslintValidator,
- abilityXMLValidator (这个是用来
检查原子化服务规范
) 下面,我们分别讲讲各个validator的功能与实现:
1. configValidator
使用joi
(The most powerful data validation library for JS),对config目录下的配置做校验。
/**
*projectConfig: config文件夹下的配置
*configSchema: joi配置
*/
export default async function ({ configPath, projectConfig }) {
const { error } = Joi.validate(projectConfig, configSchema, { abortEarly: false })
return buildReport(configPath, error)
}
这里我们也看一下JOI的简单用法。以下两个字段示例,定义了类型,以及是否必填
const schema = Joi.object().keys({
'projectName': Joi.string().required(),
'date': Joi.date()
})
2. packageValidator
依赖检查主要有三个方面:
- 包是否已经安装
- 检测是否有新版本
- 检测cli版本是否一致
errorLines = _.concat(errorLines, pkgsNotInstalled(pkgs))
errorLines = _.concat(errorLines, taroShouldUpdate(taroPkgs))
errorLines = _.concat(errorLines, taroCliVersionNotMatch(taroPkgs))
3. recommandValidator
recommandValidator 检查的是一些常见的推荐内容。主要有:
- .editconfig配置
- .gitignore 配置
- Readme
- ESLint
- 测试依赖
前三项文件配置的检查,是通过fs.readdirSync('./')
获取项目文件,并匹配。而eslint和测试的检查,则是通过检查是否有安装了相关的依赖包。
4. eslintValidator
检查 ESLint,该Validator是对代码做了一遍eslint的检查。使用eslint的 CLIEngine
模块
import { CLIEngine } from 'eslint'
const eslintCli = new CLIEngine({
cwd: process.cwd(),
useEslintrc: false,
configFile
})
...
const report = eslintCli.executeOnFiles([sourceFiles])
const formatter = eslintCli.getFormatter()
5. abilityXMLValidator
这个validator是用于对华为原子服务定义文件的校验。快应用这一块小编并不了解,想详细学习的可以去developer.huawei.com看一下。
检查是否有project.quickapp.json
文件,如果有的话则对ability.xml
做检验。以下是abilityXMLValidator.ts中的注释,小编就直接CV过来啦~~~
Taro Info
Taro 提供了命令来一键检测 Taro 环境及依赖的版本等信息,方便大家查看项目的环境及依赖,排查环境问题。在提 issue 的时候,请附上 taro info 打印的信息,帮助开发人员快速定位问题。
以下是taro info
的核心代码,主要是使用到了envinfo
这个库
async function info (options) {
const info = await envinfo.run(Object.assign({}, {
System: ['OS', 'Shell'],
Binaries: ['Node', 'Yarn', 'npm'],
npmPackages,
npmGlobalPackages: ['typescript']
}, options), {
title: `Taro CLI ${getPkgVersion()} environment info`
})
console.log(info)
}
envinfo.run
这个方法接受需要的env Info的参数,返回具体的参数值。
Taro update
更新项目中 Taro 相关的依赖
taro update project
1. 获取 Taro 的最新版本
const getLatestVersion = require('latest-version')
2.更新项目 package.json 里面的 Taro 依赖信息到最新版本
// 写入package.json
try {
await fs.writeJson(pkgPath, packageMap, {spaces: '\t'})
console.log(chalk.green('更新项目 package.json 成功!'))
console.log()
} catch (err) {
console.error(err)
}
3. 运行 npm install
let command
if (shouldUseYarn()) {
command = 'yarn'
} else if (shouldUseCnpm()) {
command = 'cnpm install'
} else {
command = 'npm install'
}
const child = exec(command)
const spinner = ora('即将将项目所有 Taro 相关依赖更新到最新版本...').start()
child.stdout.on('data', function (data) {
spinner.stop()
console.log(data)
})
child.stderr.on('data', function (data) {
spinner.stop()
console.log(data)
})
这里的exec使用了child_process
模块,exce
也提供了回调,因此,也可以这样子写:
exec(command, (error, stdout, stderr) => {
if (error) {
console.log(error)
} else {
console.log(`${stderr}${stdout}`)
}
})
总结
关于taro-cli的实现:
我们借助nodejs,通过用户输入的命令和命令行与用户的交互,来创建文件
,创建页面
,项目诊断
,编译
,更新
等等。其中,由commander
,inquire
模块来处理输入参数。
taro-cli的亮点,是多了一个doctor的东西,按照以往的经验,根据一个推荐化的配置来检查项目,减少我们的犯错机会,这是十分值得称赞的。这其中很多技术细节值得我们去深入挖掘~
下一篇预告: 【源码解析】taro-cli(2) - build