Angular Schematics 三部曲之 Add (2)

Angular Schematics 三部曲之 Add

初始化安装

在 schematics 中,我们可以通过 NodePackageInstallTask 方法安装 package

export default function(options: any): Rule { return (host: Tree, context: SchematicContext) => { // Add CDK first! addKeyPkgsToPackageJson(host); // Since the Angular Material schematics depend on the schematic utility functions from the // CDK, we need to install the CDK before loading the schematic files that import from the CDK. const installTaskId = context.addTask(new NodePackageInstallTask()); context.addTask(new RunSchematicTask('ng-add-setup-project', options), [installTaskId]); return host; }; }

初始化的过程是先将依赖包添加到 package.json 中,然后执行 npm install,以上代码实际执行了两次 npm install,在执行 Add 主逻辑之前,首先安装了 cdk,parse5 等依赖包。

除了在代码中安装依赖以外,也可以在 schematics 的 package.json 中定义 cdk、parse5,只要保证在执行 Add 主逻辑的时候已经安装了上述包即可,但是这种方式过于死板,在 package.json 中更新依赖包的版本号有些繁琐。

更新文件

在执行 ng add 拷贝项目模板的时候,会有一些需要更新的文件,但是 schematics 没有办法直接替换这些文件,所以必须先删除再拷贝,如果没有提前删除重复的文件,则会报错终止。

以下是安装 Ng-Matero 时对 ng new 生成的项目文件进行删除的方法。

/** delete exsiting files to be overwrite */ function deleteExsitingFiles() { return (host: Tree) => { const workspace = getWorkspace(host); const project = getProjectFromWorkspace(workspace); [ `${project.root}/tsconfig.app.json`, `${project.root}/tsconfig.json`, `${project.root}/tslint.json`, `${project.sourceRoot}/app/app-routing.module.ts`, `${project.sourceRoot}/app/app.module.ts`, `${project.sourceRoot}/app/app.component.spec.ts`, `${project.sourceRoot}/app/app.component.ts`, `${project.sourceRoot}/app/app.component.html`, `${project.sourceRoot}/app/app.component.scss`, `${project.sourceRoot}/environments/environment.prod.ts`, `${project.sourceRoot}/environments/environment.ts`, `${project.sourceRoot}/main.ts`, `${project.sourceRoot}/styles.scss`, ] .filter(p => host.exists(p)) .forEach(p => host.delete(p)); }; }

注意:在删除文件时先要遍历文件确定目录中有该文件再删除,否则同样会报错终止。

拷贝文件

在执行完一系列规则之后,最终需要将 files 文件夹中的文件复制到项目目录,直接拷贝整个文件夹就可以,方法如下:

/** Add starter files to root */ function addStarterFiles(options: Schema) { return chain([ mergeWith( apply(url('./files'), [ template({ ...strings, ...options, }), ]) ), ]); }

在拷贝完成之后,命令行会列出文件的创建、更新等信息。

关于 chain mergeWith apply template 等方法的使用详见 Schematics 的 README,不过 Schematics 的 README 上面的方法并不全,很多方法还是需要参考 @angular/material 以及其它库的使用方式。

简单说一下 template 和 applyTemplates 的不同之处:

template 作用于原始文件

applyTemplates 作用于后缀名为 .template 的文件。

添加 .template 后缀的文件可以避免 VS Code 报错。

schematics 中的 files 模板文件是从 Ng-Matero 项目中拷贝的,拷贝方式有多种,可以通过 shell 命令,也可以通过 gulp,这取决于你的喜好。

文件修改

JSON 文件的修改非常简单,比如在 angular.json 中添加 hmr 的设置。

/** Add hmr to angular.json */ function addHmrToAngularJson() { return (host: Tree) => { const workspace = getWorkspace(host); const ngJson = Object.assign(workspace); const project = ngJson.projects[ngJson.defaultProject]; // build project.architect.build.configurations.hmr = { fileReplacements: [ { replace: `${project.sourceRoot}/environments/environment.ts`, with: `${project.sourceRoot}/environments/environment.hmr.ts`, }, ], }; // serve project.architect.serve.configurations.hmr = { hmr: true, browserTarget: `${workspace.defaultProject}:build:hmr`, }; host.overwrite('angular.json', JSON.stringify(ngJson, null, 2)); }; }

对于 JSON 文件的修改主要用到的就是 overwrite 方法。而对于非 JSON 文件的修改,相对麻烦一点,比如添加 hammer.js 的声明:

/** Adds HammerJS to the main file of the specified Angular CLI project. */ export function addHammerJsToMain(options: Schema): Rule { return (host: Tree) => { const workspace = getWorkspace(host); const project = getProjectFromWorkspace(workspace, options.project); const mainFile = getProjectMainFile(project); const recorder = host.beginUpdate(mainFile); const buffer = host.read(mainFile); if (!buffer) { return console.error( `Could not read the project main file (${mainFile}). Please manually ` + `import HammerJS in your main TypeScript file.` ); } const fileContent = buffer.toString('utf8'); if (fileContent.includes(hammerjsImportStatement)) { return console.log(`HammerJS is already imported in the project main file (${mainFile}).`); } recorder.insertRight(0, `${hammerjsImportStatement}\n`); host.commitUpdate(recorder); }; }

关于 host.beginUpdate、recorder.insertRight、host.commitUpdate 这几个方法,可以看一下 angular cli 的源码。

除了上述提到的方法之外,在修改文件的时候,还可能用到 AST,需要更精细的操作代码文件,我会在 Generation 部分重点讲解。

调试

在编写 schematics 的时候,调试很重要,简单说一下关于调试的问题以及技巧。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsffyj.html