当前位置: 首页 > news >正文

搭建node脚手架(一)

核心代码(init.ts)

🧩 代码流程解析

项目初始化配置

const cwd = options.cwd || process.cwd(); // 获取当前工作目录
const isTest = process.env.NODE_ENV === 'test'; // 检测是否为测试环境
const checkVersionUpdate = options.checkVersionUpdate || false; // 版本检查开关
const disableNpmInstall = options.disableNpmInstall || false; // 禁用依赖安装开关
const pkgPath = path.resolve(cwd, 'package.json');
let pkg: PKG = fs.readJSONSync(pkgPath);

🔹 功能:获取项目路径、运行环境设置,并解析package.json文件

工具版本检查(可选)

if (!isTest && checkVersionUpdate) {await update(false);
}

🔹 当非测试环境且开启版本检查时,自动检测CLI工具更新

交互式配置收集

  • 默认启用ESLint
  • 选择ESLint类型(JS/TS + React/Vue)
  • 交互式确认是否启用Stylelint、Markdownlint、Prettier

🔹 通过inquirer.prompt实现用户交互配置

依赖处理

pkg = await conflictResolve(cwd, options.rewriteConfig);
spawn.sync(npm, ['i', '-D', PKG_NAME], { stdio: 'inherit', cwd });

🔹 解决依赖冲突后,安装当前工具的开发依赖

脚本命令配置

pkg.scripts[`${PKG_NAME}-scan`] = `${PKG_NAME} scan`;
pkg.scripts[`${PKG_NAME}-fix`] = `${PKG_NAME} fix`;

🔹 在package.json中添加扫描和修复命令

Git Hooks配置

pkg.husky.hooks['pre-commit'] = `${PKG_NAME} commit-file-scan`;
pkg.husky.hooks['commit-msg'] = `${PKG_NAME} commit-msg-scan`;

🔹 配置提交前和提交信息时的自动化检查

配置文件生成

generateTemplate(cwd, config);

🔹 根据用户选择生成对应的配置文件模板

完成提示

log.success(`${PKG_NAME} 初始化完成 :D`);

⚙️ 典型应用场景

执行 my-cli init 命令即可实现:
✅ 全套Lint工具配置
✅ 依赖自动安装
✅ package.json脚本自动配置
✅ Git hooks自动化配置
✅ 配置文件模板生成

流程图

搭建脚手架流程
对应代码:

import path from 'path';
import fs from 'fs-extra';
import inquirer from 'inquirer';
import spawn from 'cross-spawn';
import update from './update';
import npmType from '../utils/npm-type';
import log from '../utils/log';
import conflictResolve from '../utils/conflict-resolve';
import generateTemplate from '../utils/generate-template';
import { PROJECT_TYPES, PKG_NAME } from '../utils/constants';
import type { InitOptions, PKG } from '../types';let step = 0;/*** 选择项目语言和框架*/
const chooseEslintType = async (): Promise<string> => {const { type } = await inquirer.prompt({type: 'list',name: 'type',message: `Step ${++step}. 请选择项目的语言(JS/TS)和框架(React/Vue)类型:`,choices: PROJECT_TYPES,});return type;
};/*** 选择是否启用 stylelint* @param defaultValue*/
const chooseEnableStylelint = async (defaultValue: boolean): Promise<boolean> => {const { enable } = await inquirer.prompt({type: 'confirm',name: 'enable',message: `Step ${++step}. 是否需要使用 stylelint(若没有样式文件则不需要):`,default: defaultValue,});return enable;
};/*** 选择是否启用 markdownlint*/
const chooseEnableMarkdownLint = async (): Promise<boolean> => {const { enable } = await inquirer.prompt({type: 'confirm',name: 'enable',message: `Step ${++step}. 是否需要使用 markdownlint(若没有 Markdown 文件则不需要):`,default: true,});return enable;
};/*** 选择是否启用 prettier*/
const chooseEnablePrettier = async (): Promise<boolean> => {const { enable } = await inquirer.prompt({type: 'confirm',name: 'enable',message: `Step ${++step}. 是否需要使用 Prettier 格式化代码:`,default: true,});return enable;
};export default async (options: InitOptions) => {const cwd = options.cwd || process.cwd();const isTest = process.env.NODE_ENV === 'test';const checkVersionUpdate = options.checkVersionUpdate || false;const disableNpmInstall = options.disableNpmInstall || false;const config: Record<string, any> = {};const pkgPath = path.resolve(cwd, 'package.json');let pkg: PKG = fs.readJSONSync(pkgPath);// 版本检查if (!isTest && checkVersionUpdate) {await update(false);}// 初始化 `enableESLint`,默认为 true,无需让用户选择if (typeof options.enableESLint === 'boolean') {config.enableESLint = options.enableESLint;} else {config.enableESLint = true;}// 初始化 `eslintType`if (options.eslintType && PROJECT_TYPES.find((choice) => choice.value === options.eslintType)) {config.eslintType = options.eslintType;} else {config.eslintType = await chooseEslintType();}// 初始化 `enableStylelint`if (typeof options.enableStylelint === 'boolean') {config.enableStylelint = options.enableStylelint;} else {config.enableStylelint = await chooseEnableStylelint(!/node/.test(config.eslintType));}// 初始化 `enableMarkdownlint`if (typeof options.enableMarkdownlint === 'boolean') {config.enableMarkdownlint = options.enableMarkdownlint;} else {config.enableMarkdownlint = await chooseEnableMarkdownLint();}// 初始化 `enablePrettier`if (typeof options.enablePrettier === 'boolean') {config.enablePrettier = options.enablePrettier;} else {config.enablePrettier = await chooseEnablePrettier();}if (!isTest) {log.info(`Step ${++step}. 检查并处理项目中可能存在的依赖和配置冲突`);pkg = await conflictResolve(cwd, options.rewriteConfig);log.success(`Step ${step}. 已完成项目依赖和配置冲突检查处理 :D`);if (!disableNpmInstall) {log.info(`Step ${++step}. 安装依赖`);const npm = await npmType;spawn.sync(npm, ['i', '-D', PKG_NAME], { stdio: 'inherit', cwd });log.success(`Step ${step}. 安装依赖成功 :D`);}}// 更新 pkg.jsonpkg = fs.readJSONSync(pkgPath);// 在 `package.json` 中写入 `scripts`if (!pkg.scripts) {pkg.scripts = {};}if (!pkg.scripts[`${PKG_NAME}-scan`]) {pkg.scripts[`${PKG_NAME}-scan`] = `${PKG_NAME} scan`;}if (!pkg.scripts[`${PKG_NAME}-fix`]) {pkg.scripts[`${PKG_NAME}-fix`] = `${PKG_NAME} fix`;}// 配置 commit 卡点log.info(`Step ${++step}. 配置 git commit 卡点`);if (!pkg.husky) pkg.husky = {};if (!pkg.husky.hooks) pkg.husky.hooks = {};pkg.husky.hooks['pre-commit'] = `${PKG_NAME} commit-file-scan`;pkg.husky.hooks['commit-msg'] = `${PKG_NAME} commit-msg-scan`;fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));log.success(`Step ${step}. 配置 git commit 卡点成功 :D`);log.info(`Step ${++step}. 写入配置文件`);generateTemplate(cwd, config);log.success(`Step ${step}. 写入配置文件成功 :D`);// 完成信息const logs = [`${PKG_NAME} 初始化完成 :D`].join('\r\n');log.success(logs);
};

检查和处理包的版本更新(update.ts)

完整流程

版本检查 → 提示展示 → 执行更新

  • 主逻辑封装:
// 依赖引入
import { execSync } from 'child_process';
import ora from 'ora';
import log from '../utils/log';
import npmType from '../utils/npm-type';
import { PKG_NAME, PKG_VERSION } from '../utils/constants';/*** 检查最新版本* @returns Promise<string|null> 返回最新版本号或null*/
const checkLatestVersion = async (): Promise<string | null> => {const npm = await npmType;const latestVersion = execSync(`${npm} view ${PKG_NAME} version`).toString('utf-8').trim();if (PKG_VERSION === latestVersion) return null;const [currentVersions, latestVersions] = [PKG_VERSION,latestVersion].map(v => v.split('.').map(Number));for (let i = 0; i < currentVersions.length; i++) {if (currentVersions[i] > latestVersions[i]) return null;if (currentVersions[i] < latestVersions[i]) return latestVersion;}
};/*** 版本检查与更新入口* @param install 是否自动安装更新,默认true*/
export default async (install = true) => {const spinner = ora(`[${PKG_NAME}] 版本检查中...`).start();try {const npm = await npmType;const latestVersion = await checkLatestVersion();spinner.stop();if (!latestVersion && install) {log.info(`当前没有可用的更新`);return;}if (latestVersion && install) {const updateSpinner = ora(`[${PKG_NAME}] 升级至 ${latestVersion}...`).start();execSync(`${npm} i -g ${PKG_NAME}`);updateSpinner.stop();} else if (latestVersion) {log.warn(`发现新版本 ${latestVersion} (当前 ${PKG_VERSION})\n` +`升级命令: ${npm} install -g ${PKG_NAME}@latest\n`);}} catch (e) {spinner.stop();log.error(e);}
};

功能概览

版本检测机制

  • 通过执行 npm view <pkg-name> version 命令获取远程最新版本号
  • 自动比对本地 PKG_VERSION,若发现更新则返回新版本号,否则返回 null

智能更新选项

  • 当 install=true 时:自动执行 npm i -g <pkg-name> 升级到最新版
  • 当 install=false 时:仅显示更新提示和升级命令
  • 当前为最新版本时显示"没有可用更新"提示

交互体验优化

  • 集成 ora 实现命令行动态加载指示器(检查/更新状态)
  • 采用统一日志工具管理 info/warn/error 输出

核心函数

checkLatestVersion()

  • 自动识别当前包管理器(npm/yarn/pnpm)
  • 执行版本查询并返回远程最新版本
  • 实现语义化版本号比较(Major.Minor.Patch)

package.json 配置讲解

对应 package.json 配置:

"scripts": {"dev": "npm run copyfiles && tsc -w","build": "rm -rf lib && npm run copyfiles && tsc","copyfiles": "copyfiles -a -u 1 \"src/config/**\" lib","test": "npm run build && jest","coverage": "nyc jest --silent --forceExit","prepublishOnly": "npm run test"
}

🔹 1. “dev”: “npm run copyfiles && tsc -w”

开发模式启动脚本

顺序执行两个命令:

  1. npm run copyfiles - 将 src/config 目录下的配置文件复制到 lib 目录
  2. tsc -w - 启动 TypeScript 编译器监听模式,实时将 .ts 文件编译至 lib 目录

用途:本地开发时同步监听文件变更并确保配置文件同步更新

🔹 2. “build”: “rm -rf lib && npm run copyfiles && tsc”

完整构建流程

执行步骤:

  1. rm -rf lib - 清除旧的 lib 目录(Linux/Mac 命令,Windows 需使用 rimraf)
  2. npm run copyfiles - 复制 src/config 下的文件到 lib
  3. tsc - 编译 TypeScript 源码到 lib 目录

用途:生成干净的 lib 目录,包含编译后的 JS 和配置文件

🔹 3. “copyfiles”: “copyfiles -a -u 1 “src/config/**” lib”

配置文件复制脚本

参数说明:

  • -a:保留文件属性(时间戳、权限等)
  • -u 1:移除路径第一层(src/),确保文件复制到 lib/config/ 而非 lib/src/config/

用途:解决 TypeScript 不处理非 .ts 文件(如 JSON 等)的问题

🔹 4. “test”: “npm run build && jest”

测试流程

执行顺序:

  1. npm run build - 完整构建
  2. jest - 运行单元测试

用途:确保测试基于最新编译代码

🔹 5. “coverage”: “nyc jest --silent --forceExit”

测试覆盖率检查

参数说明:

  • nyc:基于 Istanbul 的覆盖率工具
  • --silent:抑制 console.log 输出
  • --forceExit:测试完成后强制退出进程

用途:生成代码覆盖率报告

🔹 6. “prepublishOnly”: “npm run test”

npm 发布前钩子

用途:确保只有通过测试的代码才能发布到 npm

🔑 脚本总结

这套脚本构建了一个完整的 TypeScript + Jest 开发流程:

  • dev:开发实时编译
  • build:完整构建
  • copyfiles:辅助文件处理
  • test:构建+测试
  • coverage:覆盖率检查
  • prepublishOnly:发布质量保障

文章转载自:

http://G9gJ8C5K.fmrrr.cn
http://Tegpf1vt.fmrrr.cn
http://cWg28IZM.fmrrr.cn
http://5YtCOP0O.fmrrr.cn
http://JJkofyzK.fmrrr.cn
http://5QDZRoBN.fmrrr.cn
http://BJuIYp2H.fmrrr.cn
http://URwH37Re.fmrrr.cn
http://fkFBHTCQ.fmrrr.cn
http://OT1I5rKD.fmrrr.cn
http://yjZAnCOA.fmrrr.cn
http://uV90wmcy.fmrrr.cn
http://834HrlKL.fmrrr.cn
http://d9QLpHJM.fmrrr.cn
http://vB9NopTI.fmrrr.cn
http://F0XTviD1.fmrrr.cn
http://Iqo6Nsoy.fmrrr.cn
http://uYfC9XbM.fmrrr.cn
http://fCxBY48W.fmrrr.cn
http://O5VmDPtu.fmrrr.cn
http://8YWUZcq0.fmrrr.cn
http://m6rt2giQ.fmrrr.cn
http://aMpJzKHw.fmrrr.cn
http://wMdTBYic.fmrrr.cn
http://AY8gHf6c.fmrrr.cn
http://BTKtZxbC.fmrrr.cn
http://kp49UeVR.fmrrr.cn
http://NCVXiKHN.fmrrr.cn
http://z8wor2og.fmrrr.cn
http://EFgFYs7a.fmrrr.cn
http://www.dtcms.com/a/386715.html

相关文章:

  • ARM(9) - UART
  • STM32H743-ARM例程1-IDE环境搭建与调试下载
  • 向量数据库的作用
  • 深度学习预备知识学习总结
  • C51单片机——开发学习(基础学习代码梳理)
  • 在 Windows 10 中通过 WSL2 安装 vLLM 部署本地大模型的方法和步骤
  • MyBatis XML操作
  • 3DGS压缩-Knowledge Distillation for 3DGS
  • 宇视设备视频平台EasyCVR视频设备轨迹回放平台监控摄像头故障根因剖析
  • Mysql 主从复制操作
  • 2.Boost工作原理分析
  • 专题一递归算法
  • 精准选中对象
  • 制作uniapp需要的storyboard全屏ios启动图
  • 嵌入式硬件工程师的每日提问(2)
  • 清华最新发布114页大型推理模型的强化学习综述
  • 软件质量保证(SQA)和测试的关系
  • 22.1 突破单卡显存极限!DeepSpeed ZeRO实战:用1块GPU训练百亿参数大模型
  • 框架-SpringCloud-1
  • Redis 与微服务架构结合:高并发场景下的架构艺术
  • g4f 0.6.2.9版本安装以及服务不太稳定的问题探究
  • I2C通信
  • 经典算法题之x 的平方根
  • 【精品资料鉴赏】RPA财务机器人应用(基于UiPath)教材配套课件
  • 融合A*与蚁群算法的室内送餐机器人多目标路径规划方法研究
  • RustDesk:免费开源的跨平台远程桌面控制软件
  • 超越NAT:如何构建高效、安全的内网穿透隧道
  • RabbitMQ理解
  • 【闪电科创】边缘计算深度学习辅导
  • Linux服务器中Mysql定时备份(清理)数据库