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": "
" , - "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