本文主要介绍如何开发一个脚手架的基本框架。
# 使用工具
Commander.js (opens new window) 用来接收处理用户命令。
inquirer.js (opens new window) 用来完成与用户的交互。
download-git-repo (opens new window) 能够用来下载 github 项目。
chalk (opens new window) 能使文字以某种颜色高亮显示。
ora (opens new window) 用来实现 loading 效果。
figlet (opens new window) 可以用来实现一些简单的字符画。
@darkobits/lolcatjs (opens new window) 可以让字符显示随机的颜色。
execa (opens new window) 能够用于传入命令,直接执行。可用于自动化测试和持续集成。
semver (opens new window) 是一个专门分析 Semantic Version(语义化版本)的工具,通常用来处理、管理版本。
multimatch (opens new window) 是一个用来匹配文件的库。
user-home (opens new window) 用来获取用户的主目录路径。
shelljs (opens new window) 能够让我们在 js 中编写 shell 脚本。
# 基本要求
能够完成与用户的交互。
能够根据用户的选择,从远端仓库下载 github 项目。
下载下来的项目能够放到用户指定的目录。
帮用户完善最终的操作。npm install / yarn install
注意
使用脚手架之前,要确保有一个模版项目可供下载,不然这个脚手架就发挥不了用处了。
# 基本步骤
# 初始化项目
新建一个 star-cli 项目,并新建一个 bin 文件夹。然后进入到这个项目中,执行 npm init -y 初始化项目。
# 自定义命令 npm link
在 star-cli 项目的 bin 目录下新建 starcli 文件。我们可以在项目目录下使用 Node.js 去执行这个文件:node ./bin/starcli。但是如果想全局去执行这个文件,就需要在 package.json 中加上 bin。
"bin": {
"star": "bin/starcli"
}
2
3
然后执行 npm link,就会链接到全局的 node。可以使用 where node 命令查看全局的 node 路径。
此时在全局就有一个 star 命令了。
在 starcli 文件中输入以下内容:
#!/usr/bin/env node
console.log(123);
2
然后在终端执行 star 就能看到输出结果了。
#!/usr/bin/env node 的作用
首先,
#!
其实是一个符号,这个符号在 Linux 或 Unix 中被称为Shebang
。用于指明这个脚本文件的解释程序,因此加这一行的目的就是指定 Node.js 来执行脚本文件。其次,由于不同用户或者不同的脚本解释器有可能安装在不同的目录下,那么系统如何知道要去哪里找解释程序呢?
/usr/bin/env
就是告诉系统可以去 PATH 目录中查找,这就解决了不同用户的 Node.js 路径不同的问题,可以让系统动态的去查找 Node.js 来执行你的脚本文件。有时候如果加了这一行,碰到了No such file or directory
这样的错误,那应该是你的 Node.js 没有添加到系统的 PATH 中,配置下就好了。最后,这句命令一定要放在第一行才会生效!
注意
在网上有看到过这个说法:“如果是 windows 系统,就不需要加这一行了,也能执行,因为 windows 系统并不支持 Shebang,它是通过文件的扩展名来确定使用什么解释器来执行脚本”,经过实测发现,windows 系统下还是有必要加上这一句的,不加的话 npm link 之后还是无法全局使用。
全局执行自定义命令的原理
package.json 中的 bin 字段就是专门用来放置用户自定义命令的。比如在 vue-cli 中:
"bin": {
"vue": "bin/vue",
"vue-init": "bin/vue-init",
"vue-list": "bin/vue-list"
}
2
3
4
5
如果我们想使用自定义的命令,就需要把它加到环境变量 PATH 中,加到 PATH 中的方法就是通过 bin 目录,bin 目录再通过 npm link
命令(命令的软链接,链接到全局或者某个空间下)进行添加。
全局命令之所以可以直接使用,就是因为添加到了 PATH 中。
而 node_modules/.bin/ 目录下的命令只是下载下来了,并没有添加到 PATH 中,所以不能直接使用,要想使用就需要添加 npm 脚本才能使用。比如:
"scripts": {
"createUid": "uid"
}
2
3
然后执行 npm run createUid
就可以使用 uid 命令了。这是利用 npm 做了一个中转,因为执行 npm run xxx 之前,会先把对应的命令添加到 PATH 中,就可以使用了。运行完之后,再从 PATH 中删除。
执行 npm link 之后会做以下两步处理:
C:\Program Files\nodejs\suncli -> C:\Program Files\nodejs\node_modules\sun-cli\bin\suncli
C:\Program Files\nodejs\node_modules\sun-cli -> D:\project\custom\sun-cli
2
当我们在执行 npm install -g xxx
的时候,实际上是把包下载到了全局的 /usr/local/lib/node_modules
目录下,而 /usr/local/bin 这个目录就在 PATH 中,所以全局安装之后我们可以直接使用命令。
在 mac 中可以通过 echo $PATH
命令来查看环境变量 PATH 中有哪些值。
在 windows 中可以通过 path
命令来查看环境变量 PATH 中有哪些值。
# 使用字符画 figlet
接下来就可以开始整活了。首先我们可以弄一个好玩的字符画,这需要用到 figlet (opens new window)。直接 npm install figlet 安装就行了。
在 starcli 中编辑以下代码:
#!/usr/bin/env node
const figlet = require("figlet");
const fontStr = figlet.textSync("linnan");
console.log(fontStr);
2
3
4
然后执行 star 命令,就可以看到终端输出字符画了。不过这个字符画看起来有点丑,我们是可以自己设置它的字体、大小什么的,具体参考 figlet 的用法。
# 字符画添加颜色 lolcatjs
我们可以给这个字符画加一些颜色,需要用到 lolcatjs (opens new window)。一定要全局安装它,然后补充 starcli 的内容。
这里需要注意的一点是,lolcatjs 官方的用法是需要用 import 来使用的,但是我们这里暂时不支持 import,只能用 require 的方式,可是使用 require 方法引入包会有问题,会报 “Printer.fromString is not a function” 的错误。解决方法就是通过 Printer.default.fromString
的方式调用。
#!/usr/bin/env node
const figlet = require("figlet");
const fontStr = figlet.textSync("linnan");
const Printer = require("@darkobits/lolcatjs");
const transformed = Printer.default.fromString(fontStr);
console.log(transformed);
2
3
4
5
6
执行后就会看到以下效果。
注意
在 window 上还会报找不到 @darkobits/lolcatjs 这个包的错误,原因是这个包是全局安装的,而 Node.js 默认不在全局找包。我们需要配置环境变量才能解决这个问题,那么如何配置呢?
首先,通过
where node
命令查看自己本机的 Node.js 的安装路径,比如我自己的是在:C:\Program Files\nodejs\node.exe。然后,打开文件夹,输入
控制面板\系统和安全\系统
打开系统设置页面,再点击高级系统设置
->环境变量
。最后,在当前用户的环境变量里或者是系统变量里增加一个环境变量
NODE_PATH
,值是C:\Program Files\nodejs\node_modules
。配置好之后重新打开终端,执行命令就没问题了。
# 接收用户输入的命令 Commander.js
弄好字符画之后,接下来看看怎么实现接受用户的命令执行不同的操作。
这块功能需要用到两个经典的库:shelljs (opens new window) 和 commander (opens new window)。shelljs 是一个在 js 文件中编写 shell 脚本的库,而 commander 就是用来接收处理用户命令的库。
在 starcli 文件中补充以下代码:
#!/usr/bin/env node
const figlet = require("figlet");
const fontStr = figlet.textSync("linnan");
const Printer = require("@darkobits/lolcatjs");
const transformed = Printer.default.fromString(fontStr);
const { program } = require("commander");
program.version(transformed);
program
.option("-u, --update", "Get lastest version")
.option("-d, --download", "Download a project")
.option("-c, --create", "Create a project");
program.parse(process.argv);
// console.log(transformed);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
然后执行 star --help 或者 star -h 就可以看到我们创建的那些命令了。
执行 star -V 或者 star --version 就可以看到字符画。
我们也可以直接读取 package.json 里面的 version,然后输出出来。
#!/usr/bin/env node
const figlet = require("figlet");
const fontStr = figlet.textSync("linnan");
const version = require("../package").version;
const Printer = require("@darkobits/lolcatjs");
const transformed = Printer.default.fromString(
` 星际开发脚手架:${version} \n ${fontStr}`
);
const { program } = require("commander");
program.version(transformed);
program
.option("-u, --update", "Get lastest version")
.option("-d, --download", "Download a project")
.option("-c, --create", "Create a project");
program.parse(process.argv);
// console.log(transformed);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
执行效果如下:
# 让文字高亮显示 chalk
quicktype (opens new window) 是一个能将 JSON 转换为任何语言的包。需要全局安装。
chalk (opens new window) 是一个能使文字以某种颜色高亮显示的包。
#!/usr/bin/env node
const figlet = require("figlet");
const fontStr = figlet.textSync("linnan");
const version = require("../package").version;
const Printer = require("@darkobits/lolcatjs");
const transformed = Printer.default.fromString(
` 星际开发脚手架:${version} \n ${fontStr}`
);
const chalk = require("chalk");
const shell = require("shelljs");
const { program } = require("commander");
program.version(transformed);
program
.option("-u, --update", "Get lastest version")
.option("-d, --download", "Download a project")
.option("-c, --create", "Create a project")
.option("-j, json", "Convert JSON to any language");
const handlers = {
json(dataURL) {
shell.exec(
`quicktype ${dataURL} -o ${
shell.pwd().stdout
}/Weather.ts --runtime-typecheck`
);
// shell.exec(`quicktype ${dataURL} -o Weather.ts --runtime-typecheck`);
}
};
program
.usage("[cmd] <options>")
.arguments("<cmd> [env]")
.action((cmd, otherParams) => {
const handler = handlers[cmd];
if (typeof handler == "undefined") {
console.log(chalk.blue(`${cmd}`) + chalk.red("暂未支持"));
} else {
handler(otherParams);
}
});
program.parse(process.argv);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 发布到 npm
发包方法
发包注意点
注意,脚手架中需要安装 @babel/runtime (opens new window) 这个包(安装就行了,不需要配置),不然发布成功全局安装后无法正常使用,会报错。
# 开发一个 CSS CLI
这个 CLI 的功能是扫描团队的 CSS 代码并转换成 AST,然后使⽤ CSS matrix() 代替全部基础 API,从而增强渲染性能。
这个 CLI 的实现主要包含以下几个步骤:
# 1. 创建 CLI 应⽤
可以使⽤ Node.js 创建 CLI 应⽤。⾸先,确保已经安装了 Node.js 和 npm。创建⼀个新的项⽬⽬录,并初始化 npm:
mkdir css-matrix-transformer
cd css-matrix-transformer
npm init -y
2
3
安装必要的库:
npm install postcss postcss-safe-parser yargs fs
postcss 和 postcss-safe-parser ⽤于解析和处理 CSS。
yargs ⽤于处理命令⾏参数。
fs(Node.js 内置模块)⽤于⽂件读写。
# 2. 解析 CSS ⽂件
创建⼀个⽂件 transform.js ,并添加基本的 CSS 解析逻辑:
const fs = require("fs");
const postcss = require("postcss");
const safeParser = require("postcss-safe-parser");
const yargs = require("yargs/yargs");
const { hideBin } = require("yargs/helpers");
async function transformCss(file) {
const css = fs.readFileSync(file, "utf8");
const root = postcss.parse(css, { parser: safeParser });
// 遍历并转换 CSS 规则...
// ...
// 写回⽂件
fs.writeFileSync(file, root.toString());
}
const argv = yargs(hideBin(process.argv)).argv;
if (argv.file) {
transformCss(argv.file);
} else {
console.error("Please specify a CSS file.");
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 3. 转换 AST
在 transformCss 函数中添加逻辑以转换 CSS 属性。以下是⼀个示例,需要根据实际情况编写适当的转换逻辑:
root.walkRules((rule) => {
rule.walkDecls((decl) => {
if (shouldBeTransformed(decl)) {
decl.value = convertToMatrix(decl.value);
}
});
});
function shouldBeTransformed(decl) {
// 根据需要检查声明是否应该转换
// 例如,检查是否使⽤了 transform 属性
return decl.prop === "transform";
}
function convertToMatrix(value) {
// 将 CSS 转换为 matrix() 形式
// 这需要你编写具体的转换逻辑
return `matrix(${value})`;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 4. 使⽤ CLI
现在,可以使⽤这个 CLI ⼯具来转换 CSS ⽂件。例如:
node transform.js --file path/to/your/style.css
后续步骤
完善转换逻辑:根据需求完善 shouldBeTransformed 和 convertToMatrix 函数。
错误处理:增加适当的错误处理逻辑。
测试:在不同的 CSS ⽂件上进⾏测试,确保转换逻辑正确⽆误。