在库中使用schematics——ng add与ng update

起步

当创建 Angular 库时,你可以为同时为它打包进一组原理图,并把它与 Angular CLI 集成在一起。借助原理图,用户可以用 ng add 来安装你这个库的初始版本,可以用 ng generate 来创建你在库中定义的一些工件,可以用 ng update 来调整他们的项目,以支持你在库的新版本中引入的重大变更。

创建一个angular库

ng new demo --create-application=false
ng g library my-lib

可见如下目录结构

├── node_modules/                 
├── projects                           
│   └── my-lib        
│       ├── src               
│       │   ├── lib
│       │   ├── public-api.ts
│       │   └── test.ts
│       ├── karma.conf.js
│       ├── ng-package.json       
│       ├── package.json
│       ├── README.md
│       ├── tsconfig.lib.json
│       ├── tsconfig.lib.prod.json
│       ├── tsconfig.spec.json
│       └── tslint.json
├── README.md
├── package.json
├── .editorconfig
├── angular.json
├── tsconfig.base.json
├── tslint.json
└── tsconfig.json

添加projects/my-lib/schematics/collection.json文件

{
  "$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json",
  "schematics": {
    "ng-add": {
      "description": "Add my library to the project.",
      "factory": "./ng-add/index#ngAdd"
    }
  }
}

修改projects/my-lib/package.json文件

{
  "name": "my-lib",
  "version": "0.0.1",
  "schematics": "./schematics/collection.json",
}

添加projects/my-lib/schematics/ng-add/index.ts文件

import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
import { strings } from '@angular-devkit/core';

// Just return the tree
export function ngAdd(options: any): Rule {
  return (tree: Tree, context: SchematicContext) => {
    context.addTask(new NodePackageInstallTask());
     // 如果不是 Angular 项目则抛出错误
    const workspaceConfig = tree.read('angular.json');
    if (!workspaceConfig) {
      throw new SchematicsException('Not an Angular CLI workspace');
    }

     const templateSource = apply(url('./files'), [
      applyTemplates({
        ...strings
      }),
      move(normalize('')),
    ]);

    return chain([mergeWith(templateSource)]);
  };
}

添加projects/my-lib/schematics/ng-add/files 文件

└── files                           
      └──index.ts

index.ts内容如下

// #docregion template
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class <%= classify(name) %>Service {
  constructor(private http: HttpClient) { }
}

定义依赖类型,在projects/my-lib/package.json中添加

"ng-add": {
  "save": "devDependencies"
}

修改projects/my-lib/tsconfig.schematics.json

{
  "compilerOptions": {
    "baseUrl": ".",
    "lib": [
      "es2018",
      "dom"
    ],
    "declaration": true,
    "module": "commonjs",
    "moduleResolution": "node",
    "noEmitOnError": true,
    "noFallthroughCasesInSwitch": true,
    "noImplicitAny": true,
    "noImplicitThis": true,
    "noUnusedParameters": true,
    "noUnusedLocals": true,
    "rootDir": "schematics",
    "outDir": "../../dist/my-lib/schematics",
    "skipDefaultLibCheck": true,
    "skipLibCheck": true,
    "sourceMap": true,
    "strictNullChecks": true,
    "target": "es6",
    "types": [
      "jasmine",
      "node"
    ]
  },
  "include": [
    "schematics/**/*"
  ],
  "exclude": [
    "schematics/*/files/**/*"
  ]
}
属性 说明
rootDir 指出在你的 schematics/ 文件夹中包含要编译的输入文件。
outDir 映射到了库的输出目录下。默认情况下,这是工作空间根目录下的 dist/my-lib 文件夹

修改projects/my-lib/package.json

{
  "name": "my-lib",
  "version": "0.0.1",
  "scripts": {
    "build": "../../node_modules/.bin/tsc -p tsconfig.schematics.json",
    "copy:files": "cp --parents -p -r schematics/*/files/** ../../dist/my-lib/",
    "copy:collection": "cp schematics/collection.json ../../dist/my-lib/schematics/collection.json",
    "postbuild":"npm run copy:files && npm run copy:collection"
  },
  "peerDependencies": {
    "@angular/common": "^7.2.0",
    "@angular/core": "^7.2.0"
  },
  "schematics": "./schematics/collection.json",
  "ng-add": {
    "save": "devDependencies"
  }
}
属性 说明
build 脚本使用自定义的 tsconfig.schematics.json 文件来编译你的原理图。
copy *语句将已编译的原理图文件复制到库的输出目录下的正确位置,以保持目录的结构。
postbuild 脚本会在 build 脚本完成后复制原理图文件。

修改根目录下的package.json,在scripts下加入

 "build": "ng build my-lib --prod && cd ./projects/my-lib && npm run build",
 "publish": "cd ./dist/my-lib && npm publish"

补充:
创建 schema.d.ts,定义的各个选项的值
一般的,可以手动创建 schema.d.ts,如本生成 component 的原理图,它的 schema.json 中属性只有一个必填的 name,那么编写的 schema.d.ts 内容就如下:

export interface Schema {
    name: string;
}

实际上,这个文件可以使用指令生成,在 schema.json 的同级目录下,开启终端输入指令,如下:

npx -p dtsgenerator dtsgen schema.json -o schema.d.ts

开始发布

npm publish

试一试ng add吧!

使用schematics修改项目

如何使用schematics修改项目中的package.json

方法

说明

tree.overwrite() 重新编排package.json
tree.read() 读取文件内容

修改projects/my-lib/schematics/ng-add/index.ts

    //读取angular.json的文件内容
    const workspaceConfig = tree.read('angular.json');
    // 如果不是 Angular 项目则抛出错误
   if (!workspaceConfig) {
      throw new SchematicsException('Not an Angular CLI workspace');
    }
    // 读取package.json的文件内容
    const workspaceJson = tree.read('package.json');
    let json=JSON.parse(workspaceJson)
    json.script.hello="print('123')"
    tree.overwrite('./package.json',JSON.stringify(json))
    

在node_modules添加引入与注入项

方法 说明
addImportToModule(source: ts.SourceFile, modulePath: string, classifiedName: string, importPath: string): Change[] 添加引入到module
ts.createSourFile 创建资源文件

import * as ts from '@schematics/angular/third_party/github.com/Microsoft/TypeScript/lib/typescript';
import { addImportToModule } from '@schematics/angular/utility/ast-utils';

......

  const modulePath = `/app.module.ts`;
  const sourText = _tree.read(modulePath).toString('utf-8');
  const sourceFile =  ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
  const importPath = '@fortawesome/angular-fontawesome';
  const moduleName = 'FontAwesomeModule';  //文件名为驼峰命名
  const declarationChanges = addImportToModule(sourceFile, modulePath, moduleName, importPath);
  const declarationRecorder = _tree.beginUpdate(modulePath);
  for (const change of declarationChanges) {
    if (change instanceof InsertChange) {
        declarationRecorder.insertLeft(change.pos, change.toAdd);
    }
  }
 _tree.commitUpdate(declarationRecorder);

更新文件

方法 说明
tree.beginUpdate() 更新内容
   const htmlPath = `./app/src/app.component.html`;
   const htmlStr = `\n\n`;
   const modulePath = `/app.module.ts`;
   const sourText = _tree.read(htmlPath).toString('utf-8');
   const htmlSourceFile =  ts.createSourceFile(htmlPath, sourceText, ts.ScriptTarget.Latest, true);
   const htmlRecorder = _tree.beginUpdate(htmlPath);
   htmlRecorder.insertLeft(htmlSourceFile.end, htmlStr);
   _tree.commitUpdate(htmlRecorder);

package.json依赖项安装

方法 说明
addPackageToPackageJson 添加依赖支持
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';

......

const dependencies = [{ name: '@fortawesome/fontawesome-svg-core', version: '~1.2.25' }],
addPackageToPackageJson(
    _tree,
    dependency.name, //依赖包名
    dependency.version,//版本
);

ng Update使用

在collection.json 同级目录,新建 migration.json 文件,并写入以下内容:

{
    "$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json",
    "schematics": {
        "migration002": {
            "version": "0.0.2",
            "description": "更新angular-schematics-tutorial到0.0.2版本时执行的更新",
            "factory": "./ng-update/index#update"
        }
    }
}

在 package.json 中声明 ug-update 配置
在 package.json 中,添加以下项目:

  "ng-update": {
    "migrations": "./schematics/migration.json"
  },

其作用,就是在执行 ng update 时,能够找到对应的配置文件
在projects/my-lib/schematics/ 目录下新建index.ts

import { Rule, Tree, SchematicContext, SchematicsException } from '@angular-devkit/schematics';
import { buildDefaultPath } from '@schematics/angular/utility/project';
import * as ts from 'typescript';

export function update(): Rule {
    return (_tree: Tree, _context: SchematicContext) => {

        // 解析angular项目
        const workspaceConfigBuffer = _tree.read('angular.json');
        if (!workspaceConfigBuffer) {
            throw new SchematicsException('Not an Angular CLI workspace');
        }
        return tree.create('index.md','我更新了')
      }
    
}


完结撒花!!

发表评论

评论已关闭。

相关文章