位置上下文和路径
Yeoman 文件实用程序基于这样一个理念:您始终在磁盘上拥有两个位置上下文。这些上下文是您的生成器很可能从中读取和写入的文件夹。
目标上下文
第一个上下文是目标上下文。目标是 Yeoman 将在其中搭建新应用程序的文件夹。它是您的用户项目文件夹,您将在其中编写大部分脚手架。
目标上下文定义为当前工作目录或包含 .yo-rc.json
文件的最近的父文件夹。 .yo-rc.json
文件定义了 Yeoman 项目的根目录。此文件允许您的用户在子目录中运行命令并在项目上执行操作。这确保了最终用户的一致行为。
您可以使用 this.destinationRoot()
获取目标路径,或者通过使用 this.destinationPath('sub/path')
连接路径来获取。
// Given destination root is ~/projects
class extends Generator {
paths() {
this.destinationRoot();
// returns '~/projects'
this.destinationPath('index.js');
// returns '~/projects/index.js'
}
}
您也可以使用 this.destinationRoot('new/path')
手动设置它。但是为了保持一致性,您可能不应该更改默认目标。
如果您想知道用户从哪里运行 yo
,那么您可以使用 this.contextRoot
获取路径。这是 yo
被调用的原始路径;在我们使用 .yo-rc.json
确定项目根目录之前。
模板上下文
模板上下文是您存储模板文件的文件夹。通常是从您读取和复制的文件夹。
默认情况下,模板上下文定义为 ./templates/
。您可以使用 this.sourceRoot('new/template/path')
覆盖此默认值。
您可以使用 this.sourceRoot()
获取路径值,或者通过使用 this.templatePath('app/index.js')
连接路径来获取。
class extends Generator {
paths() {
this.sourceRoot();
// returns './templates'
this.templatePath('index.js');
// returns './templates/index.js'
}
};
“内存中”文件系统
在覆盖用户文件时,Yeoman 非常小心。基本上,对预先存在文件的每次写入都将经历一个冲突解决过程。此过程要求用户验证覆盖其文件内容的每个文件写入。
此行为可以防止意外情况并降低错误风险。另一方面,这意味着每个文件都是异步写入磁盘的。
由于异步 API 更难使用,因此 Yeoman 提供了一个同步文件系统 API,其中每个文件都写入 内存文件系统,并且仅在 Yeoman 运行完成后才写入磁盘。
此内存文件系统在所有 组合生成器 之间共享。
文件实用程序
生成器在 this.fs
上公开所有文件方法,这是一个 mem-fs 编辑器 的实例 - 请务必查看 模块文档 以获取所有可用方法。
值得注意的是,尽管 this.fs
公开了 commit
,但您不应在生成器中调用它。Yeoman 在运行循环的冲突阶段之后内部调用此方法。
示例:复制模板文件
这是一个示例,我们希望复制和处理模板文件。
假设 ./templates/index.html
的内容为
<html>
<head>
<title><%= title %></title>
</head>
</html>
然后,我们将使用 copyTpl
方法复制文件,同时将内容作为模板进行处理。 copyTpl
使用 ejs 模板语法。
class extends Generator {
writing() {
this.fs.copyTpl(
this.templatePath('index.html'),
this.destinationPath('public/index.html'),
{ title: 'Templating with Yeoman' }
);
}
}
生成器运行完成后,public/index.html
将包含
<html>
<head>
<title>Templating with Yeoman</title>
</head>
</html>
一个非常常见的场景是在 提示阶段 存储用户答案并将其用于模板化
class extends Generator {
async prompting() {
this.answers = await this.prompt([{
type : 'input',
name : 'title',
message : 'Your project title',
}]);
}
writing() {
this.fs.copyTpl(
this.templatePath('index.html'),
this.destinationPath('public/index.html'),
{ title: this.answers.title } // user answer `title` used
);
}
}
通过流转换输出文件
生成器系统允许您对每个文件写入应用自定义过滤器。自动美化文件、规范化空格等都是完全可能的。
在每个 Yeoman 进程中,我们将每个修改后的文件写入磁盘。此过程通过 vinyl 对象流(就像 gulp)传递。任何生成器作者都可以注册一个 transformStream
来修改文件路径和/或内容。
通过 registerTransformStream()
方法注册新的修改器。这是一个示例
var beautify = require("gulp-beautify");
this.registerTransformStream(beautify({ indent_size: 2 }));
请注意,**任何类型的每个文件都将通过此流传递**。确保任何转换流都将通过它不支持的文件。像 gulp-if 或 gulp-filter 这样的工具将帮助过滤无效类型并将其传递。
您基本上可以使用任何gulp 插件与 Yeoman 转换流一起在写入阶段处理生成的文件。
提示:更新现有文件的内容
更新预先存在的文件并不总是简单的任务。最可靠的方法是解析文件 AST(抽象语法树)并对其进行编辑。此解决方案的主要问题是编辑 AST 可能很冗长且有点难以理解。
一些流行的 AST 解析器是
- Cheerio 用于解析 HTML。
- Esprima 用于解析 JavaScript - 您可能对 AST-Query 感兴趣,它提供了一个更低级别的 API 来编辑 Esprima 语法树。
- 对于 JSON 文件,您可以使用本机
JSON
对象方法。 - Gruntfile 编辑器 用于动态修改 Gruntfile。
使用 RegEx 解析代码文件是一条危险的道路,在这样做之前,您应该阅读 这篇 CS 人类学答案 并掌握 RegEx 解析的缺陷。如果您确实选择使用 RegEx 而不是 AST 树来编辑现有文件,请务必小心并提供完整的单元测试。- 请务必不要破坏用户的代码。