【源码解析】taro-cli(1)- 建立、诊断、info、更新

taro-cli解析

前言

taro 脚手架分别有:javascript

  • init // 初始化项目
  • create // 建立页面
  • doctor // 诊断项目
  • info // 查看系统信息和版本信息等
  • update // 更新依赖
  • convert // 小程序转taro
  • build // 编译taro项目

本章主要讲解前面五个的功能和实现,而buildconvert则会放在taro-cli原理系列的二和三,敬请期待~ 因为 官方也在维护,代码可能会更新,所以文中的代码不免会与最新源码有出入, 本文所解析taro-cliversion1.3.21,将来若是某些功能的分析文章与源码若是出入过大,则会更新对应的文章。css

cli目录

首先咱们看一下taro-cli的目录,该工程位于taro的packages目录下。当咱们安装taro-cli后,会生成taro 命令。 html

用到的核心库
  • 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时,会有如下的输出:vue

�👽 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-clipackage.json中的bin字段:java

"bin": {
    "taro": "bin/taro"
  }
复制代码

上面代码指定,taro 命令对应的可执行文件为 bin 子目录下的 taro.js。Npm会寻找这个文件,在node_modules/.bin/目录下创建符号连接。在上面的例子中,taro.js会创建符号连接npm_modules/.bin/taro。因为node_modules/.bin/目录会在运行时加入系统的PATH变量,所以在运行npm时,就能够不带路径,直接经过命令来调用这些脚本。若是只有一个命令,也能够直接简写成string,前提是命令名称和模块名称同样node

{ 
    "name": "taro",
    "version": "1.2.5",
    "bin": "bin/taro"
}
复制代码

Taro Init

使用命令建立模板项目git

taro init myApp
// npx @tarojs/cli init myApp
复制代码

其中用到了commander这个库,能够自动的解析命令和参数,合并多选项,处理短参等等。让咱们来看一下它在init命令中的用法:github

// 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 Projectproject.create()所作的事情。typescript

// 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作了什么事情。shell

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)))
  }
复制代码
  1. 获取模板。先从taroconfig中查看是否有模板的地址(templateSource),若是没有的话则使用默认的模板组,并经过git clone的方式下载(这里是使用了download-git-repo这个库)

1.3.9 开始 Taro 会在用户根目录下建立 .taro 文件夹,其中 .taro/index.json 用于存放 CLI 相关配置。 开发者可使用 taro config 命令对配置项进行一系列操做。说明

  1. ask(询问),便是经过inquirer模块,创造命令行交互,肯定(项目名,描述,是否用ts, css预处理器,模板)
  2. 肯定所需的选项后,使用mem-fs-editorcopyTpl拷贝模板,模板使用ejs,并将所须要的选项传递进去。好比修改package.json中的name和description。

4. 在建立完项目后,是使用child_process来执行 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,经过用户输入的命令和命令行与用户的交互,来建立文件建立页面项目诊断编译更新等等。其中,由commanderinquire模块来处理输入参数。
taro-cli的亮点,是多了一个doctor的东西,按照以往的经验,根据一个推荐化的配置来检查项目,减小咱们的犯错机会,这是十分值得称赞的。这其中不少技术细节值得咱们去深刻挖掘~

下一篇预告: 【源码解析】taro-cli(2) - build

相关文章
相关标签/搜索