scripts
pretest
"scripts": {
"prepare": "husky install",
"format": "prettier --write .",
"build": "zx ./scripts/build.mjs",
"snapshot": "zx ./scripts/snapshot.mjs",
"pretest": "run-s build snapshot",
"test": "zx ./scripts/test.mjs",
"prepublishOnly": "zx ./scripts/prepublish.mjs"
},
"scripts": {
"prepare": "husky install",
"format": "prettier --write .",
"build": "zx ./scripts/build.mjs",
"snapshot": "zx ./scripts/snapshot.mjs",
"pretest": "run-s build snapshot",
"test": "zx ./scripts/test.mjs",
"prepublishOnly": "zx ./scripts/prepublish.mjs"
},
run-s是npm-run-all包的命令,表示同时运行多个命令,上方代码表示同时执行build和pretest命令
scripts/snapshot.mjs
源码使用了 Node.js 和 zx
库来生成一系列项目的快照。在运行过程中,它会使用特性标记(feature flags)的不同组合,通过命令行参数的方式来控制每个项目的特性。在循环中,它会创建一个新的项目目录,并将该目录中的文件复制到指定的文件夹中。这段代码中还包含了一些注释来解释具体的实现细节,包括使用了一个函数来生成特性标记的所有组合,并使用 path
和 cd
方法来操作文件路径和进入目录等
删除模板项目命令(修复在windows上无法使用的问题)
await $`rm -rf ${projectName}`
await $`rm -rf ${projectName}`
在 Windows 环境下,使用 $
模板字符串的方式执行 shell 命令时,该命令无法正常工作。可以改写为使用 Node.js 的 fs
模块来删除文件夹。以下是一个可能的实现:
import fs from 'fs/promises';
// ...
console.log(`Removing previously generated project ${projectName}`);
await fs.rm(projectName, { recursive: true, force: true });
import fs from 'fs/promises';
// ...
console.log(`Removing previously generated project ${projectName}`);
await fs.rm(projectName, { recursive: true, force: true });
们使用 Node.js 的 fs.rm()
方法来递归地删除指定的目录,其中 { recursive: true, force: true }
参数用于确保删除目录及其所有内容。请注意,这种方式删除目录时不会打印与 rm
命令类似的信息,如果需要输出信息,可以自行添加 console.log
项目命令解决方法(修复在windows上无法使用的问题)
另一种解决方法是使用 child_process
模块的 spawn
或 exec
方法来执行命令,这样可以避免对命令行字符串的拼接和处理。以下是使用 child_process.spawn
方法的示例代码:
import { spawn } from 'child_process';
// ...
const command = [bin, projectName, ...flags.map((flag) => `--${flag}`), '--force'];
console.log(command.join(' '));
spawn('node', command, { stdio: 'inherit' });
import { spawn } from 'child_process';
// ...
const command = [bin, projectName, ...flags.map((flag) => `--${flag}`), '--force'];
console.log(command.join(' '));
spawn('node', command, { stdio: 'inherit' });
这里,我们首先使用模板字符串构造了一个命令行字符串,然后使用 console.log
打印该字符串以进行调试。最后,我们使用 child_process.spawn
方法来执行该命令,并将 stdio
参数设置为 inherit
,以便将命令行的输出重定向到当前进程的标准输出。请注意,这种方式可以避免对命令行字符串的拼接和处理,但是需要手动处理命令行参数的转义和处理,否则可能会导致命令行注入等安全问题。
scripts/prepublish.mjs
脚本流程:
- pnpm build生成
outfile.cjs
- pnpm snapshot使用
outfile.cjs
在playground文件夹下生成所有模板 - cd进入playground文件夹
- pnpm install
- git add -A .
- git commit提交快照
- git tag打上版本标签
- git push提交快照
修复windows不能正常运行的问题
顶部需要加入这两行
$.shell = 'powershell.exe'
$.prefix = ''
$.shell = 'powershell.exe'
$.prefix = ''
完整代码
#!/usr/bin/env zx
import 'zx/globals'
$.shell = 'powershell.exe'
$.prefix = ''
await $`pnpm build`
await $`pnpm snapshot`
let { version } = JSON.parse(await fs.readFile('./package.json'))
const playgroundDir = path.resolve(__dirname, '../playground/')
cd(playgroundDir)
await $`pnpm install`
// git add -A 的意思是将工作区中所有变动(包括新增、修改、删除)的文件提交到暂存区,即把所有变化的文件都加入到Git的暂存区,以便下一步提交。其中,-A 是 --all 的缩写,表示添加所有文件。
await $`git add -A .`
try {
await $`git commit -m "version ${version} snapshot"`
} catch (e) {
if (!e.stdout.includes('nothing to commit')) {
throw e
}
}
await $`git tag -m "v${version}" v${version}`
// git push --follow-tags 是一个将本地代码和标签一起推送到远程仓库的命令。具体来说,它会将本地所有的提交以及与这些提交相关联的标签一起推送到远程仓库
// 如果你在本地创建了一个新的标签,而你想要将这个标签与之前提交的代码一起推送到远程仓库,那么使用 git push --follow-tags 命令就可以自动将标签推送到远程仓库
// 需要注意的是,--follow-tags 参数只在推送的时候有效,它并不会自动为你打标签。如果你想要打标签,请使用 git tag 命令
await $`git push --follow-tags`
// 提交完快照后回到项目目录,提交下更新快照的commit文本
const projectRoot = path.resolve(__dirname, '../')
cd(projectRoot)
await $`git add playground`
// --allow-empty允许提交空的commit,commit文本为更新快照
await $`git commit -m 'chore: update snapshot' --allow-empty`
await $`git push --follow-tags`
#!/usr/bin/env zx
import 'zx/globals'
$.shell = 'powershell.exe'
$.prefix = ''
await $`pnpm build`
await $`pnpm snapshot`
let { version } = JSON.parse(await fs.readFile('./package.json'))
const playgroundDir = path.resolve(__dirname, '../playground/')
cd(playgroundDir)
await $`pnpm install`
// git add -A 的意思是将工作区中所有变动(包括新增、修改、删除)的文件提交到暂存区,即把所有变化的文件都加入到Git的暂存区,以便下一步提交。其中,-A 是 --all 的缩写,表示添加所有文件。
await $`git add -A .`
try {
await $`git commit -m "version ${version} snapshot"`
} catch (e) {
if (!e.stdout.includes('nothing to commit')) {
throw e
}
}
await $`git tag -m "v${version}" v${version}`
// git push --follow-tags 是一个将本地代码和标签一起推送到远程仓库的命令。具体来说,它会将本地所有的提交以及与这些提交相关联的标签一起推送到远程仓库
// 如果你在本地创建了一个新的标签,而你想要将这个标签与之前提交的代码一起推送到远程仓库,那么使用 git push --follow-tags 命令就可以自动将标签推送到远程仓库
// 需要注意的是,--follow-tags 参数只在推送的时候有效,它并不会自动为你打标签。如果你想要打标签,请使用 git tag 命令
await $`git push --follow-tags`
// 提交完快照后回到项目目录,提交下更新快照的commit文本
const projectRoot = path.resolve(__dirname, '../')
cd(projectRoot)
await $`git add playground`
// --allow-empty允许提交空的commit,commit文本为更新快照
await $`git commit -m 'chore: update snapshot' --allow-empty`
await $`git push --follow-tags`
scripts/test.mjs
#!/usr/bin/env zx
import 'zx/globals'
// Vitest would otherwise enable watch mode by default.
process.env.CI = '1'
const playgroundDir = path.resolve(__dirname, '../playground/')
let projects = fs
.readdirSync(playgroundDir, { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name)
.filter((name) => !name.startsWith('.') && name !== 'node_modules')
if (process.argv[3]) projects = projects.filter((project) => project.includes(process.argv[3]))
cd(playgroundDir)
console.log('Installing playground dependencies')
await $`pnpm install`
for (const projectName of projects) {
cd(path.resolve(playgroundDir, projectName))
const packageJSON = require(path.resolve(playgroundDir, projectName, 'package.json'))
console.log(`Building ${projectName}`)
await $`pnpm build`
if ('@playwright/test' in packageJSON.devDependencies) {
await $`pnpm playwright install --with-deps`
}
if ('test:e2e' in packageJSON.scripts) {
console.log(`Running e2e tests in ${projectName}`)
await $`pnpm test:e2e`
}
if ('test:unit' in packageJSON.scripts) {
console.log(`Running unit tests in ${projectName}`)
await $`pnpm test:unit`
}
}
#!/usr/bin/env zx
import 'zx/globals'
// Vitest would otherwise enable watch mode by default.
process.env.CI = '1'
const playgroundDir = path.resolve(__dirname, '../playground/')
let projects = fs
.readdirSync(playgroundDir, { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name)
.filter((name) => !name.startsWith('.') && name !== 'node_modules')
if (process.argv[3]) projects = projects.filter((project) => project.includes(process.argv[3]))
cd(playgroundDir)
console.log('Installing playground dependencies')
await $`pnpm install`
for (const projectName of projects) {
cd(path.resolve(playgroundDir, projectName))
const packageJSON = require(path.resolve(playgroundDir, projectName, 'package.json'))
console.log(`Building ${projectName}`)
await $`pnpm build`
if ('@playwright/test' in packageJSON.devDependencies) {
await $`pnpm playwright install --with-deps`
}
if ('test:e2e' in packageJSON.scripts) {
console.log(`Running e2e tests in ${projectName}`)
await $`pnpm test:e2e`
}
if ('test:unit' in packageJSON.scripts) {
console.log(`Running unit tests in ${projectName}`)
await $`pnpm test:unit`
}
}
这段代码使用了 zx 库来执行命令行命令。主要作用是自动化地对某个目录下的多个项目进行构建和测试。
具体实现过程如下:
- 获取 playground 目录下的所有子目录作为项目名称。
- 遍历每个项目,进入该项目目录。
- 执行
pnpm install
安装该项目的依赖。 - 执行
pnpm build
构建该项目。 - 如果该项目中安装了 Playwright,则执行
pnpm playwright install --with-deps
安装 Playwright 依赖。 - 如果该项目中有定义
test:e2e
,则执行pnpm test:e2e
进行 e2e 测试。 - 如果该项目中有定义
test:unit
,则执行pnpm test:unit
进行单元测试。
这段代码的主要目的是为了自动化测试和构建多个项目,方便开发人员进行快速测试和部署。