ng Schematics

Notion – The all-in-one workspace for your notes, tasks, wikis, and databases.

什么是Angular Schematics

Angular Schematics 是基于模板(Template-based)的,Angular 特有的代码生成器,当然它不仅仅是生成代码

  • 安装

    npm install -g @angular-devkit/schematics-cli
    
  • 创建项目

    // 安装完成之后新建一个schematics项目
    schematics blank --name=ng-schematics
    
    ng-schematics
     ┣ src
     ┃ ┣ ng-schematics
     ┃ ┃ ┣ index.ts
     ┃ ┃ ┗ index_spec.ts
     ┃ ┗ collection.json // 定义你的相关命令
     ┣ .gitignore
     ┣ .npmignore
     ┣ README.md
     ┣ package-lock.json
     ┣ package.json
     ┗ tsconfig.json // 主要与项目打包编译相关
    

    collection.json

    {
      "$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json",
      "schematics": {
        "ng-schematics": { // 命令的名字 ng g ng-schematics:ng-schematics
          "description": "A blank schematic.", // 对该条命令的描述
          "factory": "./ng-schematics/index#ngSchematics" // 命令执行的入口函数
        }
      }
    }
    

    index.ts

    import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
    
    // You don't have to export the function as default. You can also have more than one rule factory
    // per file.
    export function ngSchematics(_options: any): Rule {
      return (tree: Tree, _context: SchematicContext) => {
        return tree;
      };
    }
    
    // tree:在这里你可以将 tree 理解为我们整个的 angular 项目,你可以通过 tree 新增文件,修改文件,以及删除文件。
    // _context:该参数为 schematics 运行的上下文,比如你可以通过 context 执行 npm install。
    // Rule:为我们制定的操作逻辑。
    
  • 新增ng-add指令

    ng-add
     ┣ files
     ┃ ┣ app.component.html.template
     ┃ ┣ app.component.scss.template
     ┃ ┗ app.component.ts.template
     ┣ index.ts
     ┣ schema.json
     ┗ schema.ts
    
    // app.component.html.template
    <div class="my-app">
      <% if (defaultLanguage === 'zh-cn') { %>你好,Angular Schematics!<% } else { %>Hello, My First Angular Schematics!<% } %>
      <h1>{{ title }}</h1>
    </div>
    
    // app.component.scss.template
    .app {
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    // app.component.ts.template
    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.scss']
    })
    export class AppComponent {
      title = <% if (defaultLanguage === 'zh-cn') { %>'你好'<% } else { %>'Hello'<% } %>;
    }
    
    // schema.json:在该文件中定义与用户的交互
    {
      "$schema": "<http://json-schema.org/schema>",
      "id": "SchematicsDevUI",
      "title": "DevUI Options Schema",
      "type": "object",
      "properties": {
        "defaultLanguage": {
          "type": "string",
          "description": "Choose the default language",
          "default": "zh-cn",
          "x-prompt": {
            "message": "Please choose the default language you want to use: ",
            "type": "list",
            "items": [
              {
                "value": "zh-cn",
                "label": "简体中文 (zh-ch)"
              },
              {
                "value": "en-us",
                "label": "English (en-us)"
              }
            ]
          }
        },
        "i18n": {
          "type": "boolean",
          "default": true,
          "description": "Config i18n for the project",
          "x-prompt": "Would you like to add i18n? (default: Y)"
        }
      },
      "required": []
    }
    
    // 命令将会接收两个参数分别为 defaultLanguage,i18n
    // defaultLanguage
    	// type 代表该参数的类型是 string。
    	// default 为该参数的默认值为 zh-cn。
    	// x-prompt 定义与用户的交互,message 为我们对用户进行的相关提问,在这里我们的 type 为 list 代表我们会为用户提供 items 中列出的选项供用户进行选择。
    
    // schema.ts:在该文件中定义我们接收到的参数类型
    export interface Schema {
      defaultLanguage: string;
      i18n: boolean;
    }
    
    // index.ts:在该文件中实现我们的操作逻辑,假设在此次 ng-add 操作中,
    // 我们根据用户输入的 defaultLanguage, i18n 来对用户的项目进行相应的更改,并且插入相关的 npm 包,再进行安装。
    
    import { apply, applyTemplates, chain, mergeWith, move, Rule, SchematicContext, SchematicsException, Tree, url } from '@angular-devkit/schematics';
    import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
    import { Schema as AddOptions } from './schema';
    
    let projectWorkspace: {
      root: string;
      sourceRoot: string;
      defaultProject: string;
    };
    
    export type packgeType = 'dependencies' | 'devDependencies' | 'scripts';
    export const PACKAGES_I18N = ['@devui-design/icons@^1.2.0', '@ngx-translate/core@^13.0.0', '@ngx-translate/http-loader@^6.0.0', 'ng-devui@^11.1.0'];
    export const PACKAGES = ['@devui-design/icons@^1.2.0', 'ng-devui@^11.1.0'];
    export const PACKAGE_JSON_PATH = 'package.json';
    export const ANGULAR_JSON_PATH = 'angular.json';
    
    export default function (options: AddOptions): Rule {
      return (tree: Tree, context: SchematicContext) => {
        // 获取项目空间中我们需要的相关变量
        getWorkSpace(tree);
    
        // 根据是否选择i18n插入不同的packages
        const packages = options.i18n ? PACKAGES_I18N : PACKAGES;
        addPackage(tree, packages, 'dependencies');
    
        // 执行 npm install
        context.addTask(new NodePackageInstallTask());
    
        // 自定义的一系列 Rules
        return chain([removeOriginalFiles(), addSourceFiles(options)]);
      };
    }
    
    // getWorkSpace
    function getWorkSpace(tree: Tree) {
      let angularJSON;
      let buffer = tree.read(ANGULAR_JSON_PATH);
      if (buffer) {
        angularJSON = JSON.parse(buffer.toString());
      } else {
        throw new SchematicsException('Please make sure the project is an Angular project.');
      }
    
      let defaultProject = angularJSON.defaultProject;
      projectWorkspace = {
        root: '/',
        sourceRoot: angularJSON.projects[defaultProject].sourceRoot,
        defaultProject,
      };
    
      return projectWorkspace;
    }
    // removeOriginalFiles
    // 根据自己的需要选择需要删除的文件
    function removeOriginalFiles() {
      return (tree: Tree) => {
        [
          `${projectWorkspace.sourceRoot}/app/app.component.ts`,
          `${projectWorkspace.sourceRoot}/app/app.component.html`,
          `${projectWorkspace.sourceRoot}/app/app.component.scss`,
          `${projectWorkspace.sourceRoot}/app/app.component.css`,
        ]
          .filter((f) => tree.exists(f))
          .forEach((f) => tree.delete(f));
      };
    }
    
    // 将 files 下的文件拷贝到指定的路径下,chain, mergeWith, apply, template 的详细使用方法可以参考 Schematics
    // addSourceFiles
    function addSourceFiles(options: AddOptions): Rule {
      return chain([
        mergeWith(
          apply(url('./files'), [
            applyTemplates({
              defaultLanguage: options.defaultLanguage,
            }),
            move(`${projectWorkspace.sourceRoot}/app`),
          ])
        ),
      ]);
    }
    // readJson
    function readJson(tree: Tree, file: string, type?: string): any {
      if (!tree.exists(file)) {
        return null;
      }
    
      const sourceFile = tree.read(file)!.toString('utf-8');
      try {
        const json = JSON.parse(sourceFile);
        if (type && !json[type]) {
          json[type] = {};
        }
        return json;
      } catch (error) {
        console.log(`Failed when parsing file ${file}.`);
        throw error;
      }
    }
    
    // writeJson
    export function writeJson(tree: Tree, file: string, source: any): void {
      tree.overwrite(file, JSON.stringify(source, null, 2));
    }
    
    // readPackageJson
    function readPackageJson(tree: Tree, type?: string): any {
      return readJson(tree, PACKAGE_JSON_PATH, type);
    }
    
    // writePackageJson
    function writePackageJson(tree: Tree, json: any): any {
      return writeJson(tree, PACKAGE_JSON_PATH, json);
    }
    
    // addPackage
    function addPackage(tree: Tree, packages: string | string[], type: packgeType = 'dependencies'): Tree {
      const packageJson = readPackageJson(tree, type);
    
      if (packageJson == null) {
        return tree;
      }
    
      if (!Array.isArray(packages)) {
        packages = [packages];
      }
      packages.forEach((pck) => {
        const splitPosition = pck.lastIndexOf('@');
        packageJson[type][pck.substr(0, splitPosition)] = pck.substr(splitPosition + 1);
      });
    
      writePackageJson(tree, packageJson);
      return tree;
    }
    
  • 测试

    ng new name // 新建ng项目
    npm link schematics // link到schematics项目
    npm bulid // schematics项目bulid
    
    ng add ng-schematics
    

EJS

GitHub - SeriousLose/schematics-study

Schematics的介绍

NG Schematics

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值