Yeoman使用踩坑记

官网传送门: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

你可能感兴趣的:(Yeoman使用踩坑记)