我正在参加「掘金·启航计划」
在开始一个项目时,框架会给我们提供一些快速创建的命令
# react
npx create-react-app my-app
# vite
yarn create vite my-vue-app
那么他们是如何实现的呢?对于重复的项目结构,我们是不是也能自动生成一个模板。
笔者使用的 yarn, 所以本篇所有的内容都是基于 yarn。
yarn create <starter-kit-package> [<args>]
我们先了解下 yarn create 这个命令:
根据文档描述, 该命令一共做了两件事:
- 全局安装
create-<starter-kit-package>(如果存在就将其更新到最新版) - 运行 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。
目录结构:
├── 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 等文件
...
}