Exec 协议
exec:
协议在获取时使用预先配置的运行时环境在临时目录中执行 Node.js 脚本。然后,此脚本应填充环境中定义的特殊目录,并在生成完成后退出。
你为什么要这样做
典型的 Yarn 获取器从互联网下载包 - 如果要使用的项目事先已打包,则此方法有效,但只要需要自己捆绑项目,此方法就会失败。Yarn 的内置机制允许你在兼容的 git 存储库上运行 prepare
脚本,并将结果用作最终包,但即使这样也不够 - 你可能需要克隆特定分支、进入特定目录、运行特定构建脚本 ... 所有这些都让我们难以支持每个用例。
exec:
协议表示一种方法,可让你自行定义如何获取指定包。从某种意义上说,它可以被视为 Yarn 提供的 获取器 API 的更高级版本。
生成器脚本和require
由于生成器将在非常特殊的环境(磁盘上尚未安装任何包)中调用,因此它将无法调用require
函数(即使使用相对路径也无法调用)。如果您需要非常复杂的生成器,只需使用 Webpack 或 Rollup 等工具将它们预先打包到单个脚本中即可。
由于此限制,并且生成器几乎总是需要使用 Node 内置模块,因此这些模块在全局范围内可用 - 与 Node REPL 已执行的操作非常类似。因此,无需手动 require fs
模块:它可以通过全局fs
变量获得!
运行时环境
为了让脚本了解生成过程中涉及的各种预定义文件夹,Yarn 将注入一个可供脚本使用的特殊execEnv
全局变量。此对象的接口定义如下
属性 | 类型 | 描述 |
---|---|---|
tempDir | 字符串 | 脚本可以自由使用的空临时目录的绝对路径。在调用脚本之前自动创建。 |
buildDir | 字符串 | 脚本预计生成包文件的空目录的绝对路径。在调用脚本之前自动创建。 |
locator | 字符串 | 字符串化定位符,用于标识生成器包。 |
您可以在execEnv.tempDir
中执行任何操作,但在执行结束时,Yarn 将期望execEnv.buildDir
包含可以压缩到存档中并存储在缓存中的文件。
示例
生成一个 hello world 包
fs.writeFileSync(path.join(execEnv.buildDir, 'package.json'), JSON.stringify({
name: 'hello-world',
version: '1.0.0',
}));
fs.writeFileSync(path.join(execEnv.buildDir, 'index.js'), `
module.exports = 'hello world!';
`);
克隆一个单一存储库并构建一个特定包
const pathToRepo = path.join(execEnv.tempDir, 'repo');
const pathToArchive = path.join(execEnv.tempDir, 'archive.tgz');
const pathToSubpackage = path.join(pathToRepo, 'packages/foobar');
// Clone the repository
child_process.execFileSync(`git`, [`clone`, `[email protected]:foo/bar`, pathToRepo]);
// Install the dependencies
child_process.execFileSync(`yarn`, [`install`], {cwd: pathToRepo});
// Pack a specific workspace
child_process.execFileSync(`yarn`, [`pack`, `--out`, pathToArchive], {cwd: pathToSubpackage});
// Send the package content into the build directory
child_process.execFileSync(`tar`, [`-x`, `-z`, `--strip-components=1`, `-f`, pathToArchive, `-C`, execEnv.buildDir]);