【前端基建】-- 搭建cli脚手架

138 阅读3分钟

前言

搭建脚手架 基本已成为前端必备技能,无论是为公司基础建设 还是团队内部统一 都是一个很好的方向

脚手架的功能

脚手架的意义

快速创建项目,统一项目结构,基础建设

脚手架工作流程

  1. 全局安装cli 命令
  2. 选择配置项
  3. 生成代码

脚手架的搭建

下面我们来分步介绍如何搭建一个脚手架

1. 如何能全局安装自己的cli命令

全局安装命令 需要我们将代码发布成npm包 publish到仓库 就像vue create-react-app一样 通过npm intalll -g xx 来安装。然后vue/create-react-app 命令就已经在本地了,即可通过vue xxx 初始化工程 当然,也可以用npx create-react-app xxx 来直接初始化

搭建项目结构

  1. pnpm init 初始化package.json, 并修改对应的字段
{
  "name": "arch-cli",
  "version": "0.0.1",
  "description": "arch-cli",
  "keywords": [
    "arch",
    "material",
    "init"
  ],
  "bin": {
    "arch-cli": "lib/bin.js" // 将arch-cli命令 指向lib/bin.js文件
  },
  "scripts": {
    "dev": "tsc -w",
    "build": "tsc",
    "clean": "rm -rf lib",
    "prepublishOnly": "npm run build"
  },
  "dependencies": {
    "chalk": "^4.0.0",
    "commander": "^9.4.1",
    "fs-extra": "^11.1.0",
    "inquirer": "^9.1.4"
  },
  "files": [
    "lib"
  ],
  "license": "MIT"
}
  1. tsc --init 初始化配置typescript
{
  "compilerOptions": {
    "declaration": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "module": "CommonJS",
    "target": "es6",
    "moduleResolution": "node",
    "jsx": "preserve",
    "lib": ["ES2020", "DOM", "ES2015"]
  },
  "exclude": ["node_modules"]
}
  1. mkdir src && cd src && touch bin.ts 新建src/bin.ts
#!/usr/bin/env node

console.log('hello arch-cli');
  1. pnpm run dev && pnpm link --global 将命令link到全局 方便本地调试 (注意:link的时候是根据package.json中的name字段来的)
  2. 在命令行输入 arch-cli 基本工作完成

目录结构

.
├── LICENSE
├── README.md
├── node_modules
│   ├── axios -> .pnpm/axios@1.2.1/node_modules/axios
│   ├── chalk -> .pnpm/chalk@4.1.2/node_modules/chalk
│   ├── commander -> .pnpm/commander@9.4.1/node_modules/commander
│   ├── fs-extra -> .pnpm/fs-extra@11.1.0/node_modules/fs-extra
│   └── inquirer -> .pnpm/inquirer@9.1.4/node_modules/inquirer
├── package.json
├── pnpm-lock.yaml
├── src
│   └── bin.ts
└── tsconfig.json

Q&A

  1. 执行pnpm link --global时,怎么确定可以在本地使用命令了?

答: 可以用which arch-cli看看本地是否找得到命令

至于typescript 和 package.json 相关的配置暂不细说,根据上述代码直接配置即可,我们遇到问题后再慢慢展开

2. 交互设计

在第一步完成的基础上, 需要用户输入一些命令参数来帮助完成。这时候就需要用到一些第三方的package了,大致列举了一下常用的几个包名

包名功能介绍
commander自定义命令行
inquirer命令行交互
chalk控制台输出美化
download-git-repo下载远程代码
ora控制台打印loading

每个包的使用 这里就不一一列举了, 核心代码会贴在下面

src/bin.ts

#!/usr/bin/env node

import { program } from 'commander';
import chalk from 'chalk';
import { createApp } from './createApp';

const SUBCOMMANDS = ['create', 'init', 'version'];

program
  .name('arch-cli')
  .usage('[commands] [options]')
  .arguments('<cmd>')
  .action((cmd) => {
    if (SUBCOMMANDS.indexOf(cmd) === -1) {
      chalk.red('Invalid command...');
      program.help();
    }
  });
program
  .command('create <projectName> [destination]')
  .description('create a new project')
  .action((name) => {
    console.log('name', name);
    createApp(name);
  });

program.parse(process.argv);

src/createApp.ts

import ora from 'ora';
import inquirer from 'inquirer';
import download from 'download-git-repo';

const createApp = async (projectName: string) => {
  const question = [
    {
      type: 'list',
      name: 'appType',
      message: '你想创建什么类型的项目?',
      choices: [
        { name: 'PC', value: 'PC' },
        { name: 'H5(未完成)', value: 'H5' },
      ],
    },
  ];
  const answer = await inquirer.prompt(question);

  if (answer?.appType === 'PC') {
    const spinner = ora({
      spinner: 'soccerHeader',
      prefixText: `loading ${answer?.appType} template`,
    });
    spinner.start('正在下载模板...');
    download(
      'harry118/typescript-react-template',
      `${process.cwd()}/${projectName}`,
      function (err: any) {
        if (!err) {
          spinner.succeed('下载成功,😁');
        } else {
          return spinner.fail(
            '下载失败😭,确保你的网络连接正常,能访问github.com'
          );
        }
      }
    );
  }
};

export { createApp };

注意事项

  1. inquirerchalk包都是安装的指定版本, 如果想升级最新包, 需要改成ESM的方式,新包不支持CommonJS导入 参考文章 可借机了解node commonjs esm typescript 结合开发时的来龙去脉
  2. typescript几乎是项目必备了,开发前需要提前 pnpm run dev 即时编译
  3. 该项目只是简单的走完基本流程,细节还有很多没有做。本来想贴一个github链接 想了下,自己亲自动手不是更好吗