脚手架开发(3) - 执行阶段

点击查看脚手架系列文章总览【正在更新】
个人网站:www.dengzhanyong.com
关注公众号【前端筱园】,不错过每一篇文章

脚手架的第三个阶段:执行阶段,本篇文章以脚手架初始化项目命令为例进行讲解。

前言

对于脚手架的每个命令的执行,都拆分成了独立的 npm 包来执行,执行相应的命令时,会将对应的 npm 命令包下载到本地缓存中,若缓存中已存在但非最新版本,也命令的注册阶段会帮我们完成自动更新。然后执行本地缓存中的文件即可。

为了方便调试,支持传入本地路径,如果符合条件,则会执行本地代码,对本地的命令包进行调试。

对于执行不同的命令,在相应的包中去实现自己的逻辑即可,但我们任然可以做一些公共的事情,比如:参数的初始化、node版本的校验、实现统一规范等。

command 类

命令执行的那些公共部分,全部可以抽离到 command 类中,在实现每个命令时,需要继承 command 类。

包名:@steamed/command

位置:models/command

作用:命令执行统一处理,规范化命令的实现

检查node版本

准备阶段中,已经校检查 node 的版本,校验的目的是为了满足脚手架正常运行的环境。这个步骤可以下沉到执行阶段,主要原因是因为脚手架执行的命令包都是独立的,并且是不断更新扩展的,可能会用到一些更新的语法,这就需要我们提高我们所规定的 node 最低版本要求。

检查和初始化参数

参数不能为空且格式必须为数组。

init 和 exec

为了统一命令包的开发规范,可以通过 Command 类来要求必须实现初始化(init)和执行(exec)两个方法。

'use strict';

const colors = require('colors');
const semver = require('semver');
const log = require('@steamed/log');
const { LOWEST_NODE_VERSION } = require('./constant');

class Command {
    constructor(argv) {
        try {
            if (!argv) {
                throw new Error('请输入参数');
            }
            if (!Array.isArray(argv)) {
                throw new Error('参数必须是一个数组');
            }
            if (argv.length < 1) {
                throw new Error('参数内容为空');
            }
            this._argv = argv;
            let runner = new Promise((resolve, reject) => {
                let chain = Promise.resolve();
                chain.then(() => this.checkNodeVersion());
                chain.then(() => this.initArgs());
                chain.then(() => this.init());
                chain.then(() => this.exec());
                chain.catch((error) => {
                    log.error(error);
                })
            })
        } catch (error) {
            log.error(error.message);
            if (log.level === 'verbose') {
                log.verbose(error);
            }
        }
        
    }

    // 初始化参数
    initArgs() {
        this._cmd = this._argv[this._argv.length - 1];
    }

    // 初始化
    init() {
        throw new Error('请实现init方法');
    };

    // 执行
    exec() {
        throw new Error('请实现exec方法');
    }

    // 检查node版本
    checkNodeVersion() {
        const currentVersion = process.version;
        const lowestVersion = LOWEST_NODE_VERSION;
        if (!semver.gte(currentVersion, lowestVersion)) {
            throw new Error(colors.red(`steamed-cli 需要node的最低版本为${lowestVersion},当前node.js版本为${currentVersion}`));
        }
    }
}

module.exports = Command;

初始化命令开发

脚手架一般都有一个 init 或者 create 命令用来初始化项目模板,例如 vue-cli 使用 vue create hello-world 来创建一个项目。

首先来分析一下有哪些要点:

  • 应该支持手动传入项目名
  • 支持传入 --force 参数强制创建项目
  • 需要用户输入或选择一些其他必要信息,应该支持命令行的交互功能
  • 需要实现项目模板下载到当前目录下
  • 下载后的模板需要根据用户填写的信息,对模板内的关键信息进行替换
要点分析
  1. 命令行交互

    使用 inquirer 包实现,使用方法请参考:https://www.npmjs.com/package/inquirer

  2. 判断当前文件夹是否为空,如果不为空,需要让用户确认是否强制覆盖

    // 判断文件
    isDirEmpty(localPath) {
        const fileList = fes.readdirSync(localPath);
        return fileList.filter(f => !f.startsWith('.')).length === 0; 
    }
    
    if (!this.force) {  // 没有--fore参数
        isForce = (await inquirer.prompt({
            name: "isForce",
            type: "confirm",
            default: false,
            message: "当前文件夹不为空,是否强制创建?"
        })).isForce;
    }
    
  3. 模板下载

    模板存储在哪里?

    有两种方案:

    • 存到 github 中,使用时通过 git clone 命令去动态拉取,好处在于可以很方便的修改模板代码。

    • npm 包的形式存在,与动态命令思路相同,首次执行时,将相应的模板下载到本地缓存目录下,初始化项目时,将相应的模板从本地拷贝到当前目录。

    我选择的是第二种,主要优势在于可以很方便控制模板的版本,后面会讲到如何控制。

    模板选择列表的管理

    初始化项目模板不仅是内容可能存在更新,也可能随时新增删除模板,模板选择项是不固定的,不能写死在代码中。可以通过接口请求的方式动态获取模板信息,包含模板的名称、版本号、npm包名称、下载源、模板类型等信息。

  4. 替换关键信息

    用户通过命令行交互输入项目名称、版本号、项目描述等信息,这些信息应该替换到初始化模板中的相应位置,可以通过 ejs 模板渲染实现。 使用方法请参考:https://www.npmjs.com/package/ejs

    替换文件中的信息需要遍历模板中的文件,可以使用 glob 这个包。

完整代码请查看:https://github.com/DengZhanyong/steamed

点击查看脚手架系列文章总览【正在更新】
个人网站:www.dengzhanyong.com
关注公众号【前端筱园】,不错过每一篇文章
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端筱园

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值