如何快速开发一个脚手架

277 阅读3分钟

为什么我们需要脚手架?

当公司项目逐渐增多,如何快速搭建符合团队技术栈的新项目。不是简单的复制粘贴。
而是需要一个贴合团队的脚手架来帮助我们减少这些重复性工作,提高开发效率。

前端三大框架也各自有自己的脚手架。

ReactVueAngular
create-react-appvue-cliangular-cli

但是真正落地到团队项目,我们在其基础上还需要引入:项目个性化配置、流水线配置、统一版本的第三方库等等。很显然,官方脚手架并不是一个很好的选择。

接下来介绍如何快速生成一个通用项目脚手架

首先要确定我们的需求。我们需要脚手架帮我们做到什么?

1、自动生成新项目
2、可选择的多个项目模板
3、可选择的多种配置

我们开始吧!

创建项目

mkdir my-cli
cd my-cli
npm init

生成 package.json 文件

添加 cli.js

touch cli.js

cli.js 中添加脚本命令

#! /usr/bin/env node
console.log('hello')

这里第一行的意思是 表明脚本是 node 环境,/usr/bin/env 是用来查找nodebin目录,找node执行文件来执行当前脚本文件。

在命令行输入 node cli.js 即可看到输出。

但是我们需要指定命令名称来执行 cli。如何做呢?

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

我们修改 package.json

{
    "name": "my-cli",
    ...
    "bin": {
        "my-cli": "./cli.js"
    }
}

代表用 my-cli 执行 cli.js 脚本文件。

我们执行 npm link,在对应的 node /node_modules/.bin 下创建软连接。

执行 my-cli,就可以看到输出了。

刚才示范了一个简单的 cli 执行的流程,接下来我们开始进入正题。

1、添加 create 命令

使用 commander 来实现
1、添加 create 命令
2、读取用户填写的项目名称

const commander = require('commander');
commander
  .command('create <name>')
  .action((name, options) => {
    console.log(name)
  })
  .parse(process.argv)

2、选择模板

使用 inquirer 来实现
1、可选择创建的模板

const inquirer = require('inquirer');
const selectTemplate = async () => {
  return await inquirer.prompt([
    {
      name: 'template',
      type: 'list',
      message: 'Select a template:',
      choices: [
        {
          name: 'react',
          value: 'react',
        },
        {
          name: 'vue',
          value: 'vue',
        },
      ],
    },
  ]);
};

3、创建模板

1、克隆模板
直接执行 git clone xxx

const { execSync } = require('child_process')
execSync(`git clone ${gitUri}`)

2、处理用户输入

使用 ejs 模板引擎
IF 语句 <% if (name) { %> <% } %>
替换语句 <%= name %>

<% if (name) { %>
  <h2><%= name %></h2>
<% } %>

使用ejs,可以按需加载代码以及第三方库,只需要判断用户输入即可,这里只给最简单的示例。

通过判断用户输入,替换模板的内容

const writeTemplate = (templateDir, targetDir, name) => {
  const files = fs.readdirSync(templateDir);
  files.forEach(file => {
    const templateFile = path.join(templateDir, file); // 模板文件
    const targetFile = path.join(targetDir, file); // 目标文件
    const stats = fs.statSync(templateFile);
    if (stats.isDirectory()) {
      writeTemplate(templateFile, targetFile, name);
    } else {
      fs.ensureFileSync(targetFile);
      ejs.renderFile(templateFile, { name }).then(data => {
        fs.writeFileSync(targetFile, data);
      });
    }
  });
}

发布到 npm

使用 npm publish, 登录后发布到 npm

可能会用到的其他工具库

1、ora Loading 动画
2、chalk 彩色输出
3、madge 分析文件依赖

大功告成!又可以愉快的摸鱼了!!!