跳至主要内容

Exec 协议

exec: 协议在获取时使用预先配置的运行时环境在临时目录中执行 Node.js 脚本。然后,此脚本应填充环境中定义的特殊目录,并在生成完成后退出。

yarn add my-pkg@exec:./package-builder.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]);