🚀 借助 yarn create / npm init 快速创建一个模版项目

3,512 阅读1分钟

我正在参加「掘金·启航计划」

在开始一个项目时,框架会给我们提供一些快速创建的命令

# react
npx create-react-app my-app

# vite
yarn create vite my-vue-app

那么他们是如何实现的呢?对于重复的项目结构,我们是不是也能自动生成一个模板。

笔者使用的 yarn, 所以本篇所有的内容都是基于 yarn

yarn create <starter-kit-package> [<args>]

我们先了解下 yarn create 这个命令: 根据文档描述, 该命令一共做了两件事:

  1. 全局安装 create-<starter-kit-package>(如果存在就将其更新到最新版)
  2. 运行 bin 下的脚本

用代码展示下,yarn create react-app my-app 等同于:

yarn global add create-react-app
create-react-app my-app

这里做了约定,<starter-kit-package> 是以 create- 开头的 npm 包。

实现

首先创建一个 npm 项目。

mkdir create-rollup-pkg
cd create-rollup-pkg
yarn init -y

package.json 中添加 bin 字段,用来标明项目的入口。

"bin": {
    "create-rollup-pkg": "./index.js"
},

入口文件 index.js

#!/usr/bin/env node

console.log("in create-rollup-pkg, args: ", process.argv.slice(2));

执行 yarn link 将组件连接到全局

现在我们可以试一下命令 yarn create rollup-pkg name 12 看到下面 就证明成功了,并且拿到了传递的参数

$ yarn create rollup-pkg name 12

yarn create v1.22.17
info Using linked package for "create-rollup-pkg".
in create-rollup-pkg, args:  [ 'name', '12' ]
✨  Done in 0.14s.

发布到 npm 就可使用了

# npm 6.x
npx create-rollup-pkg <pkg-name> [<args>]

# npm 7+, extra double-dash is needed:
npm init rollup-pkg <pkg-name> [<args>]

# yarn
yarn create rollup-pkg <pkg-name> [<args>]

# pnpm
pnpm create rollup-pkg <pkg-name> [<args>]

到此我们就完成了基于 create 的脚手架,下面简单介绍下自己的实践。

实践:个人项目 create-rollup-pkg

项目介绍:基于 rollup 的组件打包模板,支持选择 js、ts。

项目地址:github.com/mingyuLi97/…

目录结构:

├── README.md
├── index.js        # 项目的入口
├── package.json
├── template      # 存放生成项目的模板
│   ├── common
│   ├── js
│   └── ts
├── utils
│   └── index.js
└── yarn.lock

代码简介:

1. 校验传入的项目名称

// 获取项目名称
const pkgName = process.argv[2];

// 对项目名称校验并提示
validateName(pkgName);

2. 创建目标文件夹

// 获取要创建的文件夹的绝对路径,需要特殊处理带有 scope 的包
const dir = path.resolve(
  pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName
);

// 处理目标文件夹, 如果存在询问是否强制覆盖
if (fs.existsSync(dir)) {
  const { yes } = await prompts({
    name: "yes",
    type: "confirm",
    message: chalk.bold("是否强制覆盖已存在的目录?"),
    initial: true,
  });
  if (!yes) process.exit(1);

  console.log(`Removing ${chalk.cyan(dir)}...`);
  await fs.remove(dir);
}
// 创建目标文件夹
fs.mkdirpSync(dir);

3. 交互式的获取用户的选择

async function getOption() {
  const options = await prompts([
    {
      name: "name",
      type: "text",
      message: "项目名称",
      initial: pkgName,
    },
    {
      name: "desc",
      type: "text",
      message: "项目描述",
    },
    {
      name: "useTS",
      type: "confirm",
      initial: true,
      message: "是否创建 TS 项目",
    },
    {
      name: "packageManager",
      type: "select",
      choices: ["pnpm", "yarn", "npm"].map((i) => ({ title: i, value: i })),
      message: "请选择要使用的包管理工具",
    },
  ]);
  return options;
}

4. 生成项目

async function generate(options) {
  const { name, desc, useTS } = options;
  fs.copySync(path.resolve(__dirname, "./template/common"), dir);
  if (useTS) {
    fs.copySync(path.resolve(__dirname, "./template/ts"), dir);
  } else {
    fs.copySync(path.resolve(__dirname, "./template/js"), dir);
  }
  // 根据用户输入修改 package.json、README.md 等文件
  ...
}