官网传送门:https://yeoman.io/authoring/index.html
1.npm link 后,yo name 报错:
Error: EACCES: permission denied, open '/Users/sherry/Library/Preferences/insight-nodejs/insight-yo.json.1293917385'
at Object.openSync (fs.js:443:3)
at Function.writeFileSync [as sync] (/Users/sherry/.nvm/versions/node/v10.16.3/lib/node_modules/yo/node_modules/write-file-atomic/index.js:212:13)
at Conf.set store [as store] (/Users/sherry/.nvm/versions/node/v10.16.3/lib/node_modules/yo/node_modules/conf/index.js:142:19)
at Conf.set (/Users/sherry/.nvm/versions/node/v10.16.3/lib/node_modules/yo/node_modules/conf/index.js:64:14)
at Insight.set optOut [as optOut] (/Users/sherry/.nvm/versions/node/v10.16.3/lib/node_modules/yo/node_modules/insight/lib/index.js:56:15)
at Object. (/Users/sherry/.nvm/versions/node/v10.16.3/lib/node_modules/yo/lib/cli.js:206:18)
at Module._compile (internal/modules/cjs/loader.js:778:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
【解决方式】
sudo chown -R Sherry /Users/sherry/Library/Preferences/insight-nodejs
2.yeoman工程的基础目录结构设计(个人设计,仅供参考)
【各文件夹分工】
generators:存放各种 generator,其中,app/index.js 用于脚手架创建的全流程控制逻辑。可以使用 this.composeWiith('../generator1') 引入其他 generator 的功能。
projects:存放待生成给用户使用的,完整脚手架(这里,脚手架也可以选择不存放到 yeoman 工程本地,放到独立的 git 仓库,进行远程引用)
根目录下的 package.json:服务于 yeoman 工程
projects/vue、projects/react等目录下的package.json:服务于业务。留给用户去手动 install
3.Managing Dependencies(npm包管理)
生命周期 install(),专门用来执行 npm 包的安装。用法如下:
install() {
this.yarnInstall();
// 使用yarn/npm,自选
// this.npmInstall();
}
【解析】
这里,相当于在命令行执行 yarn / npm。
直接安装 根目录 下的package.json。
!!注意!! 无论在哪个子generator 中的 install 调用 this.yarnInstall,都只会安装 根目录 下的 package.json。如果子 generator 文件夹中有定义 package.json,它定义的包不会被安装。
当你需要安装指定的包时:
this.npmInstall(['lodash'], { 'save-dev': true });
相当于在根目录执行:
npm install lodash --save-dev
当你需要向现有的 package.json 中动态添加些依赖,或者,不想创建 package.json 文件时,你可以这样:
这里的 this.destinationPath('package.json'),是在创建 package.json 文件。默认在根目录下创建。
如果根目录下已经存在 package.json,yeoman 会提示用户,存在文件冲突,根据用户的选择,决定是否在已有的 package.json 文件中写入 pkgJson 内容。
【注】
截图中的代码,无论写在 app/index.js 中,还是形如 generator1/index.js 中,this.destinationPath('package.json') 都只是在根目录下创建 package.json,而不是在当前 generator 文件夹下创建。
那么,如果想改变其他目录下的 package.json(如动态改变某个工程的package.json),怎么办?
假如自定义 generator 的目录结构如下(generators和node_modules同级,都是根目录下的一级文件夹):
当期望向 vue/package.json 中动态写入一些 dependencies 定义:
writing() {
const pkgJson = {
dependencies: {
vue: '^2.0.0'
}
};
this.fs.extendJSON(this.destinationPath('projects/vue/package.json'), pkgJson);
}
【个人思考】
动态写入 dependencies,尤其适用于用户选择是否使用某种功能(某个模块)的情况。
例如:
用户交互时,选择使用 redux,则可以这样动态写入 redux 包引用。否则,脚手架的 package.json 中,默认不写入 redux 引用。
但要时刻注意,这里仅仅只是进行 package.json 的写入,并不会执行安装。
任何地方的 install() 中调用 this.yarnInstall(),都只会执行根目录下 package.json 的包安装。
这个也很好理解。因为我们开发的是自定义的 generator,因此,执行包安装,理应服务于这个 yeoman 工程,而不是它内部的其他子工程(脚手架)。
而脚手架中的 package.json,也理应由用户创建项目后,自行手动安装。
4.Interacting with the file system(文件系统交互)
(1)各种路径的获取方式、自定义方式
· destinationRoot:生成文件的存放目录(目标文件夹)
【获取】this.destinationRoot()
***默认值***
如果当前执行 yo name 的目录下包含.yo-rc.json,则为当前文件夹。
否则,则为最近的、包含.yo-rc.json的父文件夹。
如果当前文件夹、及所有父文件夹,都不包含.yo-rc.json,则在当前目录下创建.yo-rc.json,并以当前目录作为生成文件存放的默认目录。
【自定义】this.destinationRoot('new/folder')
【生成文件的路径定义】this.destinationPath('index.js') —— 在 this.destinationRoot() 目录下,生成 index.js
· contextRoot:用户当前执行 yo 指令的目录
【获取】this.contextRoot
·sourceRoot:(用于复制的)模板文件的存放目录
【获取】this.sourceRoot()
***默认值***
与当前调用 this.sourceRoot() 的 generator 的 index.js 同级的 templates 文件夹。
例如,在 app/index.js 调用 this.sourceRoot(),sourceRoot 默认指向如图:
【自定义】this.sourceRoot('new/folder') —— 根目录下的 new/folder 文件夹
【注意】
自定义为:this.sourceRoot('./new/folder') ,指向同上。依然指向根目录下的 new/folder 文件夹,而不是在当前文件所在目录下寻找 new/folder
【模板文件的路径指定】this.templatePath('tpl.html') —— 去 this.sourceRoot() 下,找 tpl.html 文件
(2)格式化生成的文件
例如:
使用这个包,会把生成的文件(也就是写入到this.destinationPath的那个文件),按照图中指定的规则格式化。
5. .yo-rc.json 文件
作用:用来存放所有 generators 的配置对象。
可通过 this.config.xxx api 进行配置的设置、获取等操作。详见官网:https://yeoman.io/authoring/storage.html
形如:
【注】
每一个 generator 一个命名空间。不能通过 this.config.xxx 进行配置信息的共享。
可以通过 options 和 arguments 在多个 generator 间分享数据。(具体使用方式及意义待测试)
6.在任意文件夹下,执行 yo name 创建项目,逻辑流程运行过程中的各种 permission denied 错误(mkdir / rm -rf / this.fs.copyTpl)
网上大家都说设置chmod +w filename,试了,无效。mac系统。
实测,假如在 test 目录下执行 yo name,报 permission denied 错误,请尝试!!👇
sudo chown -R userName test
7.this.fs.copyTpl 的回调问题
首先明确一点,this.fs.copyTpl 是同步方法,并不是异步的,因此,没有提供回调函数。
可能你也遇到了和我一样的问题:
使用 this.fs.copyTpl 生成了一些文件夹,并在 this.fs.copyTpl 调用后面,执行删除操作,期望删掉某些/某个文件夹。但是发现,并删不掉。
于是,很自然的觉得,这是异步问题,我只要在 this.fs.copyTpl 的回调里面去删,就 ok 了。
但是,如前所述,this.fs.copyTpl 其实是同步方法。
那么,是什么原因造成了无法删除的问题呢?
【答案是】
yeoman 在进行文件处理的时候,把所有即将生成的文件/文件夹都放在了内存里,而不是直接写到磁盘上。
因此,这时候执行,形如 shelljs.rm('-rf', 'xxxxx') 的操作,是不会成功的。因为所有的文件,都还没有写到磁盘上。
详见官网说明:https://yeoman.io/authoring/file-system.html
【解决方法】
在 install() 或 end() 中,进行 shelljs.rm('-rf', 'xxxxx') 这类操作。从语义上,建议在 end() 中执行。
原理:在文件/文件夹写入磁盘后操作。
【解析】
yeoman 共计 8 个生命周期函数,执行顺序如下:
initializing: 1
prompting: 2
configuring: 3
default: 4
// 自定义的原型方法在这个地方按顺序执行
writing: 5
conflicts: 6
// 文件/文件夹写入磁盘,在这里进行
install: 7
end: 8
8.关于argument和option
(1)argument
【如何定义】
constructor(args, opts) {
super(args, opts);
this.argument('projectName', {
type: Array,
required: false, // 这里不设置,或 this.argument 不传第二个 options 参数,默认都为 必传
default: this.appname, // 运行 yo name 的文件夹名称
desc: '项目名称'
});
}
【如何使用】
yo name my-project
【如何读取】
this.log('argument projectName:', this.options.projectName);
【是否定义多个argument?】
—— 可以。
多个 argument 如何区分?
—— 通常,yo name argument1 argument2 传入多个 argument 时,按照 this.argument 的定义顺序分别赋值。
eg:
this.argument('name');
this.argument('age');
=> this.options.name === argument1
this.options.age === argument2
【注】
argument 为数组的情况,会取当前定义位置之后的所有 argument 的集合。
定义eg:
this.argument('name');
this.argument('friends', {
type: Array,
required: false,
default: [], // 运行 yo name 的文件夹名称
desc: '朋友们'
})
this.argument('age');
使用eg:
yo name Sherry Dennis Jack Tom 25
结果:
this.options.friends:[Dennis, Jack, Tom, 25]
this.options.age:Jack
(2)option(类似flag)
【如何定义】
this.option('coffee', {
alias: 'co'
})
【如何使用】
yo name --coffee / yo name --co
【如何读取】
this.log('argument projectName:', this.options.coffee);
【查看我们自定义的option】
yo name --help
网友评论