创建脚手架
⏱️最后更新: 2025/07/27
创建一个 Node.js 脚手架项目可以帮助你快速初始化新项目结构,节省重复配置时间。
- win10
- vscode v1.98.2
- node v22.14.0
- npm v10.9.2
我们的脚手架同步发布在 npm,持续维护更新中。
node-cli
1. 初始化脚手架项目
bash
# 创建项目目录
mkdir node-template
cd node-template
# 初始化 npm 项目
npm init -y
2. 安装必要依赖
bash
npm install commander inquirer chalk figlet shelljs fs-extra --save
commander
: 处理命令行参数inquirer
: 交互式命令行界面chalk
: 终端字符串样式(用于在命令行输出中添加颜色,提升用户体验。)figlet
: 生成 ASCII 艺术字shelljs
: 执行 shell 命令fs-extra
: Node.js 内置fs
模块的增强版,提供更方便的文件操作方法(例如,复制整个目录)。
3. 创建基本文件结构
node-template/
├── bin/
│ └── cli.js # 命令行入口
├── templates/ # 项目模板
│ ├── basic/ # 基础模板
│ │ ├── src/
│ │ ├── test/
│ │ ├── .gitignore
│ │ ├── package.json
│ │ └── README.md
│ └── advanced-001/ # 定制模版001
│ └── advanced-002/ # 定制模版002
│ └── advanced-003/ # 定制模版003
├── lib/
│ ├── create.js # 创建项目逻辑
│ └── utils.js # 工具函数
├── package.json
└── README.md
4. 编写 CLI 入口 (bin/cli.js)
脚手架的主文件
在项目根目录下创建一个主文件,通常命名为 index.js 或者 cli.js。
我们用 cli.js。
javascript
#!/usr/bin/env node
import { program } from "commander";
import { createProject } from "../lib/create.js";
import { readFileSync } from "fs";
import { dirname, join } from "path";
import { fileURLToPath } from "url";
// 获取当前模块路径
const __dirname = dirname(fileURLToPath(import.meta.url));
// 读取 package.json
const pkg = JSON.parse(readFileSync(join(__dirname, "../package.json")));
program
.version(pkg.version)
.command("create <project-name>")
.description("创建一个新项目")
.option("-t, --template <template>", "选择项目模板")
.action((name, cmd) => {
createProject(name, cmd.template);
});
program.parse(process.argv);
5. 编写创建逻辑 (lib/create.js)
javascript
// 使用 ES Module 导入
import fs from "fs-extra";
import path from "path";
import chalk from "chalk";
import inquirer from "inquirer";
import shell from "shelljs";
import { successLog, errorLog } from "./utils.js"; // 导入 ESM 模块
// ES Module 中,__dirname 和 __filename 需要特殊处理
import { dirname } from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
export async function createProject(projectName, templateName) {
console.log(chalk.blue("欢迎使用 Node.js 脚手架!"));
// 检查项目名称是否合法
if (!/^[a-zA-Z0-9_-]+$/.test(projectName)) {
errorLog("项目名称只能包含字母、数字、下划线和横线");
return;
}
// 检查目录是否已存在
const targetPath = path.join(process.cwd(), projectName);
if (fs.existsSync(targetPath)) {
errorLog(`目录 ${projectName} 已存在`);
return;
}
// 如果没有指定模板,让用户选择
if (!templateName) {
const { selectedTemplate } = await inquirer.prompt([
{
type: "list",
name: "selectedTemplate",
message: "请选择项目模板",
choices: ["basic", "advanced-001"], // 确保你的 templates 目录下有这些文件夹
},
]);
templateName = selectedTemplate;
}
// 复制模板文件
const templatePath = path.join(__dirname, "../templates", templateName);
console.log(templatePath);
if (!fs.existsSync(templatePath)) {
errorLog(`模板 ${templateName} 不存在`);
return;
}
console.log(`模板 ${templateName} 存在`);
// shelljs 在 ESM 环境下使用没有问题,但如果它内部有 CommonJS 依赖,需要注意
// 复制模板文件到目标目录(以下二选一复制都OK)
// shell.cp("-R", `${templatePath}`, targetPath);
await fs.copy(templatePath, targetPath);
// 可以在这里进行一些文件内容的替换(例如,修改 package.json 的 name)
// 让我们先简单地复制
const packageJsonPath = path.join(targetPath, "package.json");
if (await fs.pathExists(packageJsonPath)) {
const packageJson = await fs.readJson(packageJsonPath);
packageJson.name = projectName;
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
}
// 更新 package.json 中的项目名称
const packagePath = path.join(targetPath, "package.json");
if (fs.existsSync(packagePath)) {
// 在 ESM 中,直接 require 一个 JSON 文件会失败,需要使用 import
// 如果 packageJson 不是 .mjs 文件,直接 require 会有警告
// 更好的做法是使用 fs.readFileSync 和 JSON.parse
const packageJsonContent = fs.readFileSync(packagePath, "utf-8");
const packageJson = JSON.parse(packageJsonContent);
packageJson.name = projectName;
fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2));
}
successLog(`项目 ${projectName} 创建成功!`);
console.log(chalk.blue(`\ncd ${projectName}`));
console.log(chalk.blue("npm install\n"));
}
6. 添加工具函数 (lib/utils.js)
javascript
import chalk from "chalk";
function successLog(message) {
console.log(chalk.green(`✓ ${message}`));
}
function errorLog(message) {
console.log(chalk.red(`✗ ${message}`));
}
export { successLog, errorLog };
7. 配置 package.json
添加 bin 字段和准备脚本:
json
{
"name": "eai_nodejs_template",
"version": "1.0.0",
"type": "module",
"bin": {
"node-cli": "./bin/cli.js"
},
"scripts": {
"prepublishOnly": "npm run build"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "Node.js 项目脚手架",
"dependencies": {
"chalk": "^5.4.1",
"commander": "^14.0.0",
"execa": "^9.6.0",
"figlet": "^1.8.2",
"fs-extra": "^11.3.0",
"inquirer": "^12.8.2",
"shelljs": "^0.10.0"
}
}
8. 创建模板文件
在 templates/basic/
目录下创建基础项目模板文件,例如:
package.json
README.md
src/index.js
test/
.gitignore
9. 测试和使用脚手架
bash
# 在开发时链接到全局
npm link
# 测试创建项目
node-cli create my-new-project
10. 发布到 npm (可选)
bash
# 1. 切换源
nrm use npm
# 2. 登录
npm login
# 3. 移除全局符号链接
npm unlink -g eai_nodejs_template
# 4. 发布
npm publish