脚手架开发(1)-准备阶段

脚手架的基本原理

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

初始化项目

1. 创建项目文件

mkdir steamed-cli
cd steamed-cli

2. 使用lerna初始化项目

lerna init

项目默认结构如下:

C:.
|  .git
│  lerna.json
│  package.json
│
└─packages

在此基础上需要进行一些改动:

  • 删除 packages 目录, packages 是用来放置所有包的目录,包较多的情况下,最好多建几个文件夹将他们进行分类

  • 新建文件夹 commands (命令) 、core (核心包)、models (类)、utils (工具方法)

  • 修改 lerna.json 配置中的 packages 属性,此属性指定包的位置

    {
      "packages": [
        "core/*",
        "commands/*",
        "models/*",
        "utils/*"
      ],
      "version": "1.0.0"
    }
    
  • 新建 .gitignore 文件,添加 git 忽略文件配置 **/node_modules

  • 新建 README.md 文件,对脚手架进行介绍及使用说明

整体流程

整个脚手架的开发分成三个阶段进行:准备阶段、注册阶段、执行阶段

  • 准备阶段:在执行命令前需要做的一些事,如:检查node版本,检查脚手架更新,检查用户目录、权限等
  • 注册阶段:解析用户输入的命令(command)和参数(options),这里的参数主要针对于全局参数。
  • 执行阶段:安装/更新执行相应的package,也可直接执行本地文件。

准备阶段

创建入口 package

现在就正式开始脚手架的开发,首先需要一个入口。创建一个名叫 @steamed/cli 的包

lerna create @steamed/cli ./core
# 在最后可以加上一个路径,会将此包创建到指定的目录下

一个包的目录结构如下:

cli:.
│  package.json
│  README.md
│
├─lib
│      cli.js
│
└─__tests__
        cli.test.js

为了保持统一的规范,最好将 lib 下的 cli.js 改为 index.js

@steamed/cli 这个包是整个脚手架的入口,需要创建一个 /bin/index.js 文件作为入口,并在 packages.json 中配置 bin 属性。

{
    "name": "@steamed/cli",
    "version": "1.0.0",
	"bin": {
       "st": "./bin/index.js"
    }
}
测试流程

core/cli 下执行 npm link ,创建其软链接,使得我们可以在本地使用设置的命令进行调试。

core/cli/bin/index.js 下可以先写一句打印 console.log('hello steamed-cli'),然后在命令行中执行 st 命令,如果正常输入,则说明到目前一切正常。

如何获取输入的命令参数?

需要根据用户输入不同的命令来做不同的事,使用 process.argv 可以获取到输入的所有内容,其值为一个数组。

st init procejectName

[
  'C:\\Program Files\\nodejs\\node.exe',
  'C:\\Users\\DZY26\\AppData\\Roaming\\npm\\node_modules\\@steamed\\cli\\bin\\index.js',
  'init',
  'myname'
]
  • 第一项是 node 的安装目录
  • 第二项是所执行的文件路径
  • 后面的内容就是所传入的参数
bin/index.js 只做一件事

对于输入命令后需要做的事情我们都放到 lib/index.js 中去做,在 bin/index.js 只做一件事。

  • import-local:允许全局安装的包使用其自身的本地安装版本(如果可用)

如果存在本地版本,则执行本地脚手架。如果不存在,则执行 lib/index.js 中的内容

#! /usr/bin/env nodeconst importLocal = require('import-local');if (importLocal(__filename)) {    console.log('执行本地脚手架');} else {    require('../lib/index')(process.argv.slice(2))}
功能开发

本阶段的开发需要用到以下的 npm 包,

  • semver:用户版本号规范校验,版本号比较等
  • colors:对打印的日志设置颜色
  • userHome:获取用户主目录
  • npmlog:用户输入日志信息
  • root-check:尝试降级具有root权限的进程的权限,如果失败,则阻止访问
  • dotenv:可以将本地 .env 文件中的环境变量配置到全局环境变量中。

工具包开发

@steamed/log

功能:对 npmlog 进行封装,自定义打印级别和样式

位置:/utils/log

** npmlog 打印级别**

在不同的场景下, 可以使用 npmlog 打印出不同类型的信息,包括:sillyverboseinfotiminghttpnoticewarnerrorsilent

默认打印级别为 info ,即大于等于 info 级别的信息才会被真正的打印。

主要作用

debug 信息一般使用 verbose 级别打印,因此默认情况下是不会展示debug信息的。为了更好的调试,定位问题,可以通过传入 --debug 参数使得可以看到debug信息,实现原理就是将 npmlog 的打印级别设置为 verbose

完整代码:

'use strict';const log = require('npmlog');log.level = process.env.STEAMED_CLI_LOG_LEVEL || 'info';   // 设置log 的打印级别,默认为info,可以在环境变量中拿到自定义级别设置log.heading = 'Steamed';   // 自定义log头部信息,一般为脚手架的名字log.headingStyle = { bg: 'white', fg: 'black' }  // 自定义头部样式module.exports = log;
@steamed/get-npm-info

功能:获取 npm 信息、版本等功能

位置:/utils/get-npm-info

完整代码:

'use strict';const axios = require('axios');const semver = require('semver');// 获取默认的npm源function getDefaultRegistry(origin = true) {    return origin ? 'https://registry.npmjs.org/' : 'https://registry.npm.taobao.org/'}// 获取包的所有历史版本号async function getNpmVersions(npmName, registry) {    const npmRegistry = registry || getDefaultRegistry();    return axios.get(`${npmRegistry}${npmName}`)        .then((res) => {            if (res.status === 200) {                return Object.keys(res.data.versions);            }            return [];        })        .catch(() => {            return [];        })}// 获取最新版本async function getLatestVersion(npmName, registry) {    const versions = (await getNpmVersions(npmName, registry))        .sort((a, b) => semver.gte(a, b) ? -1 : 1);    if (versions[0]) {        return versions[0]    } else {        throw new Error('检查更新失败');    }}module.exports = {    getDefaultRegistry,    getNpmVersions,    getLatestVersion};

准备阶段功能实现

在准备阶段需要按照下面的顺序依次完成每个功能。

检查当前版本

这里指的是脚手架当前版本,此版本号就是 package.json 中的 version 的值。

const pkg = require('../package.json'); // 检查当前版本function checkPkgVersion() {    log.info('cli-version', pkg.version);}
检查node版本

需要检查用户当前使用的 node 版本是否满足脚手架的最低版本要求,以保证脚手架的所有功能可以正常使用。

新建一个 constant.js 文件,用户配置一些常量配置信息。

const LOWEST_NODE_VERSION = '12.0.0';   // 最低node版本const STEAMED_CLI_HOME_PATH = '.steamed';  // 脚手架主目录名称const STEAMED_CLI_LOG_LEVEL = 'info';   // log 的打印等级module.exports = {    LOWEST_NODE_VERSION,    STEAMED_CLI_HOME_PATH,    STEAMED_CLI_LOG_LEVEL}
const semver = require('semver');const { LOWEST_NODE_VERSION } = require('./constant');// 检查node版本function checkNodeVersion() {    const currentVersion = process.version;  // 获取当前使用的版本    const lowestVersion = LOWEST_NODE_VERSION;    if (!semver.gte(currentVersion, lowestVersion)) {        throw new Error(`steamed-cli 需要node的最低版本为${lowestVersion},当前node.js版本为${currentVersion}`);    }}
检查root权限

需要检查用户是否有root权限,如果没有,则会进行降级处理,此功能在 root-check 中已实现。

import rootCheck from 'root-check';// 检查root权限function checkRoot() {	rootCheck();}
检查用户主目录

检查用户主目录是否存在

const userHome = require('user-home');// 检查用户主目录function checkUserHome() {    if (!userHome || !fs.existsSync(userHome)) {        throw new Error(colors.red('当前用户主目录不存在!'));    } else {        process.env.STEAMED_CLI_USER_HOME = userHome;    }}
检查入参
  • 用户需要执行命令,则至少需要输入一个命令或参数。
  • 检查用户输入的参数是否包含 -d--debug ,如果存在,则表示开启 debug 模式,需要将 log 的打印等级设置为 verbose
const log = require('@steamed/log');const { STEAMED_CLI_LOG_LEVEL } = require('./constant');// 检查用户主目录function checkArgv(argv) {    if (argv.length < 0) {        throw new Error('请输入命令')    }    if (argv.includes('-d') || argv.includes('--debug')) {        log.level = 'verbose';    } else {        log.level = STEAMED_CLI_LOG_LEVEL;    }    process.env.LOG_LEVEL = log.level; // 将log等级存到环境变量中,以便其他地方使用}
检查环境变量

将主目录下的 .env 文件中环境变量会被注入到 process.env 中

const { STEAMED_CLI_HOME_PATH } = require('./constant');const userHome = require('user-home');// 检查环境变量function checkEnv() {    const dotnev = require('dotenv');    const envPath = path.resolve(userHome, '.env');  // 获取主目录下的 .env 文件    dotnev.config({        path: envPath    });  // .env 中的环境变量会被注入到 process.env 中    process.env.STEAMED_CLI_HOME_PATH = path.resolve(userHome, STEAMED_CLI_HOME_PATH);}
检查版本更新

检查本地脚手架版本是否是最新版本,如果不是,则提示用户及时更新脚手架

const { getLatestVersion } = require('@steamed/get-npm-info');// 检查更新async function checkCliUpdate() {    const lastVersion = await getLatestVersion(pkg.name);    if (lastVersion && !semver.gte(pkg.version, lastVersion)) {        log.warn('steamed更新', `发现新版本${lastVersion},当前版本${pkg.version},请及时更新!`);    }}
打印报错信息

在上面的流程中,每一步都有可能会出错,可能是第三方组件内部报错,也可能是我们自己写的 throw new Error(),这对这些错误信息,我们可以在最外层通过 try catch 处理。

async function index() {    try {        checkPkgVersion();        checkNodeVersion();        checkRoot();        checkUserHome();        checkEnv();        await checkCliUpdate();    } catch (e) {        log.error(e.message);  // 只打印关键信息        if (log.level === 'verbose') {  // 如果是debug模式,则打印出完整的错误信息栈,以便于定位问题            console.log(e);        }    }}

完整代码地址 Githubhttps://github.com/DengZhanyong/steamed

点击查看脚手架系列文章总览

个人网站:www.dengzhanyong.com

关注公众号【前端筱园】,不错过每一篇文章

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

前端筱园

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

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

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

打赏作者

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

抵扣说明:

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

余额充值