9

工程化:依托模板搭建企业级脚手架(cli)

 2 years ago
source link: https://lianpf.github.io/posts/%E5%BC%80%E5%8F%91%E6%97%A5%E8%AE%B0/25.%E6%90%AD%E5%BB%BA%E4%BC%81%E4%B8%9A%E7%BA%A7%E8%84%9A%E6%89%8B%E6%9E%B6/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

本篇是前端工程化之依托模板搭建企业级脚手架(cli)。通过脚手架,我们可以快速初始化一个项目,无需自己从零开始一步步…


1.为什么

通过脚手架,我们可以快速初始化一个项目,无需自己从零开始一步步配置,有效提升开发体验。

当然,社区已经贡献了vue-clicreate-react-appreact-native-cli等非常优秀的脚手架,

但是,这些脚手架未必完全符合我们的实际应用,特别是作为企业通用脚手架而言,所以我们需要定制自己的脚手架,来提升开发效率。

2.脚手架的作用

  • 减少重复性工作:不需要复制其他项目再删除无关代码,或者从零创建一个项目和文件。
  • 多套模板:可以根据交互动态生成项目结构和配置文件。
  • 协作:多人协作更为方便,不需要把文件传来传去。

本项目完整代码地址,在文章末尾。


二、提供的功能

明确脚手架(@lianpf/create-app-cli)需要提供的功能:

  • ca init <template-name> <project-name> 根据远程模板,初始化一个项目(远程模板可配置)
  • ca config set <template-name> <repository> 设置模板信息(暂未实现)
  • ca config get templates 查看模板配置信息
  • ca cfg get templates 同上
  • ca -v 查看当前版本号
  • ca -h 帮助

如果有其他需求,可根据需求,自行拓展commander


三、配置搭建cli

1.依赖的第三方库

  • @babel/cli@babel/core@babel/preset-env: 语法转换
  • commander: 命令行工具
  • download-git-repo: 用来下载远程模板
  • ini: 配置项格式转换
    • 『暂时没用』一般用于物理机本地配置项
  • inquirer: 交互式命令行工具
  • ora: 显示node命令环境loading动画
  • chalk: 修改控制台输出内容样式
  • log-symbols: 显示出×等的图标
  • ascii-table: 以表格形式展示数据,比如:模板列表

关于以上第三方package的介绍,可直接npm.org上查看相应的说明

2.搭建cli

2.1 初始化项目

创建一个空项目(create-app-cli)

create-app-cli $ npm init // 项目初始化
create-app-cli $ npm install @babel/cli @babel/core @babel/preset-env chalk commander download-git-repo ini inquirer log-symbols ora -D
.
├── bin
│   └── www               // 可执行文件
├── lib
    ├── ...               // 生成文件
├── src
│   ├── config.js         // 管理 @lianpf/create-app-cli 配置文件
│   ├── index.js          // 主流程入口文件
│   ├── init.js           // init command
│   ├── main.js           // 入口文件
│   └── utils
│       ├── constants.js  // 定义常量
│       ├── download.js   // 模板远程仓库下载
│       └── rc.js         // 配置文件
├── templates.json        // 模板配置文件
├── .babelrc              // babel配置文件
├── package.json
└── README.md

babel配置:开发使用了ES6语法,使用 babel 进行转义

.bablerc

{
    "presets": [
        [
            "@babel/preset-env",
            {
                "targets": {
                    "node": "current"
                }
            },
            "@babel/preset-react" // react 语法
        ]
    ],
    "plugins": [
        "transform-class-properties",
        "transform-decorators",
        "transform-react-constant-elements",
        "transform-react-inline-elements"
    ]
}

2.2 @lianpf/create-app-cli开发环境搭建

2.2.1 脚手架开发:启动命令配置

node.js内置了对命令行操作的支持,package.json中的bin 字段可以定义命令名和关联的执行文件。在package.json中添加 bin字段

package.json

{
    "name": "@lianpf/create-app-cli",
    "version": "0.2.1",
    "description": "A cli to help create a project",
    "main": "index.js",
    "bin": {
        "ca": "./bin/www"
    },
    "scripts": {
        "compile": "babel src -d lib",
        "watch": "npm run compile -- --watch"
    }
}

www 文件

行首加入一行#!/usr/bin/env node指定当前脚本由node.js进行解析

#! /usr/bin/env node
require('../lib/main.js');
2.2.2 脚手架测试:链接到全局环境

开发过程中为了方便调试,在当前的create-app-cli目录下执行 npm link,将ca命令链接到全局环境

2.2.3 脚手架开发:启动项目
create-app-cli $ npm run watch

2.3 @lianpf/create-app-cli命令行处理

利用commander来处理命令行

/create-app-cli/src/main.js

import program from 'commander';
import { VERSION, make_success, make_fail } from './utils/constants';
import apply from './index';

/**
 * ca commands
 * - config
 * - init 
 * - v
 * - h
 */

let actionMap = {
    // init 命令配置
    init: {
        ...
    },
    // config 命令配置
    config: {
        ...
    }
}

// 遍历预设命令,进行处理配置
Object.keys(actionMap).forEach((action) => {
    program.command(action)
    .description(actionMap[action].description)
    .alias(actionMap[action].alias)
    .action(() => {
        switch (action) {
            case 'config':
                apply(action, ...process.argv.slice(3));
                break;
            case 'init':
                apply(action, ...process.argv.slice(3));
                break;
            default:
                break;
        }
    });
});

function help() {
    // -h 参数是,遍历列出所有命令
    ...
}

program.usage('<command> [options]');
program.on('-h', help);
program.on('--help', help);
program.version(VERSION, '-v, --version').parse(process.argv);

// ca 不带参数时
if (!process.argv.slice(2).length) {
    program.outputHelp(make_success);
}

2.4 @lianpf/create-app-cli下载模板

download-git-repo支持从Github、Gitlab下载远程仓库到本地

/create-app-cli/src/util/download.js

import downloadGit from 'download-git-repo';
import { templateConfig, hasTemplate } from './constants';

export const downloadLocal = async (templateName, projectName) => {
    const _hasTemplate = hasTemplate(templateName)
    let api = ''
    // 判断是否存在模板
    if (_hasTemplate) {
        // 获取 模板下载地址
        api = `${templateConfig[templateName].type}:${templateConfig[templateName].user}/${templateConfig[templateName].repository}#${templateConfig[templateName].branch}`;
    }
    return new Promise((resolve, reject) => {
        // projectName 为下载到的本地目录
        downloadGit(api, projectName, { clone: true }, (err) => {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    });
}

constants.js获取template配置

/create-app-cli/src/util/constants.js

...
import templates from '../../templates.json'

...

// template config
export const templateConfig = templates

// 判断是否存在当前模板
export const hasTemplate = (templateName) => {
    return Object.keys(templateConfig).indexOf(templateName) > -1
}

...

静态template配置

/create-app-cli/template.json

{
  "react-template": {
    "type": "https://github.com",
    "user": "xxx",
    "repository": "xxx",
    "branch": "master",
    "templateName": "react-template",
    "keyWords": "react、webpack"
  },
  ...
}

注意,这里没有实现ca config set <template-name> <repository>设置模板命令,且企业级脚手架对应模板相对固定,故我们将模板预先内置到template.json文件中

2.5 @lianpf/create-app-cli交互式命令和美化

2.5.1 命令行交互

用户执行init命令后,向用户提出问题,接收用户的输入并作出相应的处理。命令行交互利用inquirer实现:

/create-app-cli/src/init.js

inquirer.prompt([
    {
        name: 'description',
        message: 'Please enter the project description: '
    },
    {
        name: 'author',
        message: 'Please enter the author name: '
    }
]).then((answer) => {
    //...
});

类似于:npm init命令执行后,项目初始化,会问询你作者、版本信息、描述等添加到package.json配置文件的交互操作。

2.5.2 视觉美化

在上一步,用户输入后,开始下载模板,此时使用ora提示用户正在下载模板,以及下载结束后给出相应提示。

/create-app-cli/src/init.js

...
import ora from 'ora';

...
let loading = ora('downloading template ...');
loading.start();
//download
loading.succeed(); //或 loading.fail();

2.6 @lianpf/create-app-cli获取模板配置

config配置支持使用其它仓库作为模板。这样使用者可以自由选择下载目标

/create-app-cli/src/config.js

import { get } from './utils/rc';
import { make_success, make_fail, make_warn } from './utils/constants';

let config = async (action, key) => {
    switch (action) {
        case 'get':
            if (key) {
                let result = await get(key);
                if (result.code === 0) {
                    console.log(make_success(result.message));
                } else {
                    console.log(make_fail(result.message));
                }
                
            } else {
                console.log(make_warn('Command does not exist!'));
            }
            break;
        // set 模板命令暂未实现,需通过 template.json 配置
        // case 'set':
            // set(key, value);
            // break;
        // case 'remove':
            // remove(key);
            // break;
        default:
            console.log(make_warn('Command does not exist!'));
            break;
    }
}

module.exports = config;

rc.js负责对本地物理机『模板配置文件』的增删改查,这里改造为对脚手架模板配置文件template.json的查询

/create-app-cli/src/util/rc.js

import { configCommand, make_success, make_warn, templateConfig } from './constants';
import AsciiTable from 'ascii-table'

// constants 配置文件
export const get = async (key) => {
    ...
    if (opts.indexOf(key) !== -1) {
        switch (key) {
            case 'templates':
                // code、message、data处理
                ...
                break;
            default:
                ...
                break;
        }
    }
    ...
    console.log(make_success(templateTable.toString()))
    return {
        code,
        data: {},
        message
    };
}

四、发布cli

  • npm publish脚手架发布。
  • 其它用户可通过npm install @lianpf/create-app-cli -g全局安装,即可使用ca命令。

谢谢各位花费宝贵的时间阅读本文,以下是源码仓库地址,如果本文给了您一点帮助或者是启发,请动动小手点进Github仓库,不要吝啬你的Star,您的肯定是我前进的最大动力


最后, 希望大家早日实现:成为前端高手的伟大梦想!
欢迎交流~

微信公众号

本文版权归原作者曜灵所有!未经允许,严禁转载!对非法转载者, 原作者保留采用法律手段追究的权利!
若需转载,请联系微信公众号:连先生有猫病,可获取作者联系方式!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK