前言
通常情况下,我们为了提高效率,通常会使用npm init < initializer > 来快速生成项目基础结构或添加额外的功能,例如create-react-app、@eslint/config,本文采用bun来实现类似的操作,让你快速拥有自己的增效工具(仅做创建模板示例)
环境搭建
1. 第一步我们需要翻一下npm官方的文档
可以看到npm init会对名为
create-xxx、@usr/create、@usr/create-xxx的包做额外的识别操作
(其他命名不能做npm init操作)
2. 知道前置条件后,我们直接登录github,创建一个create-bun-cli的空白仓库,随后clone进vscode并初始化一下
git clone https://github.com/1596944197/create-bun-cli.git
bun init -y
3. 既然我们是用bun来做cli,那肯定是需要检测是否具备bun环境,创建lib/test.ts文件
import { execSync } from "child_process";
function checkInstallation(command: string) {
try {
execSync(`${command} -v`, { stdio: "ignore" });
return true;
} catch {
return false;
}
}
const isBunInstalled = checkInstallation("bun");
if (!isBunInstalled) {
throw new Error("Bun is not installed");
}
当前项目结构
.
├── README.md
├── bun.lockb
├── index.ts
├── lib
│ └── test.ts
├── package.json
└── tsconfig.json
在index.ts导入lib/test.ts文件
import "./lib/test.ts";
4. 既然是cli,那少不了交互式命令行工具,我们安装inquirer来实现
bun i inquirer && bun i -D @types/inquirer
5. 创建lib/inquirer文件
import inquirer from "inquirer";
/**@description 案例只做简单演示,详细请访问https://www.npmjs.com/package/create-bun-cli */
export default {
ask: () => {
return inquirer.prompt([
// 是否初始化eslint
{
name: "init_eslint",
type: "confirm",
message: "是否初始化eslint",
},
]);
},
};
6. 再创建一个接受参数执行命令的文件lib/exec.ts
import { execSync } from "child_process";
import { writeFile } from "node:fs/promises";
import type inquirer from "./inquirer";
const eslintPath = "eslint.config.js";
export async function handleCommand<
T extends Awaited<ReturnType<typeof inquirer.ask>>
>(params: T) {
execSync("bun init -y", { stdio: "inherit" });
if (params.init_eslint) {
await writeFile(eslintPath, getEslintJson());
/** @description 安装固定版本避免冲突 */
await execSync(
"bun i -D typescript-eslint@8 globals@15 eslint@9 @eslint/js@9",
{
stdio: "inherit",
}
);
}
}
/** @description 模板文件 */
function getEslintJson() {
return `
import pluginJs from "@eslint/js";
import globals from "globals";
import tsEslint from "typescript-eslint";
export default [
{ files: ["**/*.{js,mjs,cjs,ts}"] },
{ languageOptions: { globals: { ...globals.browser, ...globals.node } } },
pluginJs.configs.recommended,
...tsEslint.configs.recommended,
];
`;
}
7. index.ts导入文件
import { handleCommand } from "./lib/exec";
import inquirer from "./lib/inquirer";
import "./lib/test";
const run = async () => {
const credentials = await inquirer.ask();
handleCommand(credentials);
};
run();
8. 我们创建一个打包文件用来将ts打包成js build.ts
import { execSync } from "child_process";
await Bun.build({
target: "node",
entrypoints: ["index.ts"],
outdir: "dist",
});
// 此处必须添加 #! /usr/bin env xxx ,不然npm init时不会执行文件
execSync(
`sleep 2 && echo #!/usr/bin/env node | cat - dist/index.js > temp && mv temp dist/index.js`,
);
9. package.json 增加对应的命令
// scripts打包用
// bin 是npm init 时会执行的操作
// repository 是发布npm包时需要
"scripts": {
"build": "bun run build.ts"
},
"bin": {
"bun/cli": "./dist/index.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/1596944197/create-bun-cli.git"
}
当前目录结构
├── README.md
├── build.ts
├── bun.lockb
├── dist
│ └── index.js
├── index.ts
├── lib
│ ├── exec.ts
│ ├── inquirer.ts
│ └── test.ts
├── package.json
└── tsconfig.json
10. 到此基本完成了,我们把仓库publish到npm后,即可在任意位置进行npm init bun-cli
生产验证
我们直接开个空文件夹进行验证,可以看到正常生效
总结
- npm init 非常适合快速启动项目,只需要以create-xxx发布一个包就可以利用上
- 不仅仅是生成模板文件,也可以把npm init当成一个云脚本库