手写脚手架开发

107 阅读5分钟

预置命令

 pnpm init
// 初始化一个新的 npm 项目
pnpm install commander inquirer chalk ini ora
//commander 参数解析- inquirer 交互式命令行工具 实现命令行选择功能 - chalk 改变控制台颜色 - ini 配置文件ini格式控制器 - ora 实现命令行loading 
pnpm install @types/node typescript nodemon --save-dev
//安装ts类型将ts代码转义成js,nodemon-监控文件变化执行编译命令

文件

npm link

npm link 作用 npm link 临时链接到全局,将全局的包链接到本地,方便调试包,创建一个软链映射到全局下

bin文件夹-index.js入口文件

#!/usr/bin/env node 就是告诉系统可以在PATH目录中查找,然后使用Node运行。 存放脚手架的命令行执行脚本的地方,这个文件会解析用户在命令行中输入的参数,然后调用相关模块或功能来执行相应的操作。bin 文件夹是存放可执行文件的地方,用于解析命令行参数并执行相应的脚手架功能模块。

实现ts编译

tsconfig.json

{
  "compilerOptions": {
    "target": "es6" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,

    "module": "ESNext" /* Specify what module code is generated. */,
    "moduleResolution": "NodeNext" /* Specify how TypeScript looks up a file from a given module specifier. */,
    "outDir": "bin",
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "rootDir": "src",
    "baseUrl": "./src"
  },
  "include": ["src"],
  "exclude": ["node_modules"]
}

加入命令 "build": "tsc",tsc tsc是 TypeScript 编译器的命令行工具。它会根据项目中的 TypeScript 源代码文件(通常是以 .ts 或 .tsx 扩展名结尾的文件)来生成相应的 JavaScript 文件。

使用nodemon解决反复npm run build 问题

npx nodemon

nodemon.json
{
  "watch": ["src"],
  "ext": "ts",
  "exec": "tsc"
}

解决命令行的参数

process 对象是一个全局变量,它提供当前 Node.js 进程的有关信息,以及控制当前 Node.js 进程。

#!/usr/bin/env node
import { program } from "commander";
import path from "path";
//解析用户传递参数
program.parse(process.argv);
console.log("runner ts");
console.log(path.join("a/b/c/d"));

ESM不支持把json文件当成模块进行引入,这在commonjs却可以,如果需要引入json文件,还需要借助createRequire函数:

获取外部package.json文件

代码:

#!/usr/bin/env node
import { program } from "commander";
import path from "path";
import { dirname, join } from "path";
import { createRequire } from "module";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const require = createRequire(import.meta.url);
const pkg = require(join(__dirname, "../package.json"));
//import.meta.url 返回模块的绝对的 `file:` URL
//解析用户传递参数
program.parse(process.argv); //直接解析用户参数
console.log("runner ts");
console.log(pkg);

pkgpkg 变量使用 require() 函数来引入位于当前文件的上级目录下的 package.json 文件。我们使用 join() 函数将 __dirname 和相对路径 "../package.json" 结合起来,形成 package.json 的完整路径。然后,通过 require() 函数引入该 package.json 文件,并将结果赋值给 pkg 变量。这样,pkg 变量就包含了 package.json 文件的内容。

结果

name: 'my-cli-study',
version: '1.0.0',
description: '',
main: 'index.js',
bin: { 'my-cli': './bin/index.js' },
scripts: { build: 'tsc', test: 'echo "Error: no test specified" && exit 1' 
},
type: 'module',
keywords: [],
author: '',
license: 'ISC',
dependencies: {
  chalk: '^5.2.0',
  commander: '^11.0.0',
  ini: '^4.1.1',
  inquirer: '^9.2.7',
  ora: '^6.3.1'
},
devDependencies: {
  '@types/node': '^20.3.2',
  nodemon: '^2.0.22',
  typescript: '^5.1.6'
}
}

命令开发

program对象提供了一系列方法和属性,可以用于定义命令、注册选项、解析参数等操作。通过操作program对象,可以实现对命令行程序的配置和控制。 1.通过脚手架创建一个项目 create(拉取仓库的模板,下载方式可以采用github gitlab gitee)

2.配置拉取的信息,配置系统文件 config

config实现

  • 获取当前工作目录
  • 判断是否存在
  • 创建项目

utils实现

loading

ora是一个实现命令行loading效果的库。使用方法也很简单:

模板拉取

创建项目整体划分成下面步骤:

  1. 通过获取仓库的 API 获取模板信息: Vue2 or Vue 3

  2. 将模板信息渲染为交互框,用户选择自己需要的模板

  3. 根据用户选择的模板,获取版本信息

  4. 将版本信息渲染成交互框,用户选择需要的版本

  5. 通过用户选取的模板及版本,下载对应模板到指定目录

  6. 将模板渲染为项目

自动化实现

configstore存储

创建子进程

node.js是基于单线程模型架构,这样的设计可以带来高效的CPU利用率,但是无法却利用多个核心的CPU,为了解决这个问题,node.js提供了child_process模块,通过多进程来实现对多核CPU的利用.

child_process 模块提供了以下 4 个方法用于创建子进程,并且每一种方法都有对应的同步版本

  • spawn: 启动一个子进程来执行命令;

  • exec:  启动一个子进程来执行命令,与 spawn 不同的是,它有一个回调函数获知子进程的状况;

  • execFile: 启动一个子进程来执行可执行文件;

  • fork:  与 spawn 类似,不同点在于它创建 Node 的子进程只需指定要执行的 JavaScript 文件模块即可;

  • spawn 与 exec、execFile 不同的是,后两者创建时可以指定 timeout 属性设置超时时间,一旦创建的进程运行超过设定的时间将会被杀死;

  • exec 与 execFile 不同的是,exec 适合执行已有的命令,execFile 适合执行文件;

  • exec、execFile、fork 都是 spawn 的延伸应用,底层都是通过 spawn 实现的;

image.png 玩转 node 子进程 — child_process - 掘金 (juejin.cn)

Node.jsstdin,stdout, 和stdin

当我们运行一个Node.js程序时,会启动一个进程来执行该程序

stdout,stdin, 和stderr 是标准的流,当程序执行时,它们在程序和环境之间互连输入和输出通信通道。

每个进程初始化时都有三个开放的文件描述符,分别称为stdin 、stdout 、stderr 。

这三个文件描述符被统称为标准流。

为一个进程启动了一组三个标准流,我们可以通过Node.js中的process 对象访问它们。

  • process.stdin(0):标准输入流,它是程序的输入源。
  • process.stdout(1):标准输出流,它是程序的输出源。
  • process.stderr(2):标准错误流,用于由程序发出的错误信息和诊断。