Bỏ qua để đến nội dung

Nx Generator Generator

Thêm một Nx Generator vào dự án TypeScript, giúp bạn tự động hóa các tác vụ lặp đi lặp lại như tạo scaffold cho các component hoặc thực thi các cấu trúc dự án cụ thể.

Bạn có thể tạo một generator theo hai cách:

  1. Install the Nx Console VSCode Plugin if you haven't already
  2. Open the Nx Console in VSCode
  3. Click Generate (UI) in the "Common Nx Commands" section
  4. Search for @aws/nx-plugin - ts#nx-generator
  5. Fill in the required parameters
    • Click Generate
    Parameter Type Default Description
    project Required string - TypeScript project to add the generator to. We recommend using the ts#nx-plugin generator to create this.
    name Required string - Generator name
    description string - A description of your generator
    directory string - The directory within the plugin project's source folder to add the generator to (default: <name>)

    Generator sẽ tạo các tệp dự án sau trong project đã cho:

    • Thư mụcsrc/<name>/
      • schema.json Schema cho đầu vào của generator
      • schema.d.ts Các kiểu TypeScript cho schema
      • generator.ts Triển khai generator cơ bản
      • generator.spec.ts Các bài kiểm tra cho generator
      • README.md Tài liệu cho generator
    • generators.json Cấu hình Nx để định nghĩa các generator
    • package.json Được tạo hoặc cập nhật để thêm mục “generators”
    • tsconfig.json Được cập nhật để sử dụng CommonJS

    Generator này sẽ cập nhật project đã chọn để sử dụng CommonJS, vì Nx Generators hiện chỉ hỗ trợ CommonJS (tham khảo GitHub issue này để biết về hỗ trợ ESM).

    Chọn dự án nx-plugin cục bộ của bạn khi chạy generator ts#nx-generator, và chỉ định tên cùng với thư mục và mô tả tùy chọn.

    Tệp schema.json định nghĩa các tùy chọn mà generator của bạn chấp nhận. Nó tuân theo định dạng JSON Schema với các phần mở rộng đặc biệt của Nx.

    Một tệp schema.json có cấu trúc cơ bản như sau:

    {
    "$schema": "https://json-schema.org/schema",
    "$id": "YourGeneratorName",
    "title": "Your Generator Title",
    "description": "Description of what your generator does",
    "type": "object",
    "properties": {
    // Các tùy chọn generator của bạn ở đây
    },
    "required": ["requiredOption1", "requiredOption2"]
    }

    Đây là một ví dụ đơn giản với một số tùy chọn cơ bản:

    {
    "$schema": "https://json-schema.org/schema",
    "$id": "ComponentGenerator",
    "title": "Create a Component",
    "description": "Creates a new React component",
    "type": "object",
    "properties": {
    "name": {
    "type": "string",
    "description": "Component name",
    "x-priority": "important"
    },
    "directory": {
    "type": "string",
    "description": "Directory where the component will be created",
    "default": "src/components"
    },
    "withTests": {
    "type": "boolean",
    "description": "Whether to generate test files",
    "default": true
    }
    },
    "required": ["name"]
    }

    Bạn có thể tùy chỉnh các lời nhắc hiển thị khi chạy generator thông qua CLI bằng cách thêm thuộc tính x-prompt:

    "name": {
    "type": "string",
    "description": "Component name",
    "x-prompt": "What is the name of your component?"
    }

    Đối với các tùy chọn boolean, bạn có thể sử dụng lời nhắc yes/no:

    "withTests": {
    "type": "boolean",
    "description": "Whether to generate test files",
    "x-prompt": "Would you like to generate test files?"
    }

    Đối với các tùy chọn có một tập hợp các lựa chọn cố định, sử dụng enum để người dùng có thể chọn từ một trong các tùy chọn.

    "style": {
    "type": "string",
    "description": "The styling approach to use",
    "enum": ["css", "scss", "styled-components", "none"],
    "default": "css"
    }

    Một mẫu phổ biến là cho phép người dùng chọn từ các dự án hiện có trong workspace:

    "project": {
    "type": "string",
    "description": "The project to add the component to",
    "x-prompt": "Which project would you like to add the component to?",
    "x-dropdown": "projects"
    }

    Thuộc tính x-dropdown: "projects" cho Nx biết để điền vào dropdown với tất cả các dự án trong workspace.

    Bạn có thể cấu hình các tùy chọn để được truyền dưới dạng đối số vị trí khi chạy generator từ dòng lệnh:

    "name": {
    "type": "string",
    "description": "Component name",
    "x-priority": "important",
    "$default": {
    "$source": "argv",
    "index": 0
    }
    }

    Điều này cho phép người dùng chạy generator của bạn như nx g your-generator my-component thay vì nx g your-generator --name=my-component.

    Sử dụng thuộc tính x-priority để chỉ ra các tùy chọn nào quan trọng nhất:

    "name": {
    "type": "string",
    "description": "Component name",
    "x-priority": "important"
    }

    Các tùy chọn có thể có mức độ ưu tiên là "important" hoặc "internal". Điều này giúp Nx sắp xếp các thuộc tính trong tiện ích mở rộng Nx VSCode và Nx CLI.

    Bạn có thể cung cấp giá trị mặc định cho các tùy chọn:

    "directory": {
    "type": "string",
    "description": "Directory where the component will be created",
    "default": "src/components"
    }

    Để biết thêm chi tiết về schemas, tham khảo tài liệu Nx Generator Options.

    Cùng với schema.json, generator tạo một tệp schema.d.ts cung cấp các kiểu TypeScript cho các tùy chọn generator của bạn:

    export interface YourGeneratorSchema {
    name: string;
    directory?: string;
    withTests?: boolean;
    }

    Interface này được sử dụng trong triển khai generator của bạn để cung cấp type safety và code completion:

    import { YourGeneratorSchema } from './schema';
    export default async function (tree: Tree, options: YourGeneratorSchema) {
    // TypeScript biết các kiểu của tất cả các tùy chọn của bạn
    const { name, directory = 'src/components', withTests = true } = options;
    // ...
    }

    Sau khi tạo generator mới như trên, bạn có thể viết triển khai của mình trong generator.ts.

    Một generator là một hàm biến đổi một hệ thống tệp ảo (Tree), đọc và ghi các tệp để thực hiện các thay đổi mong muốn. Các thay đổi từ Tree chỉ được ghi vào đĩa khi generator kết thúc thực thi, trừ khi nó được chạy ở chế độ “dry-run”. Một generator trống trông như sau:

    export const myGenerator = async (tree: Tree, options: MyGeneratorSchema) => {
    // Sử dụng tree để áp dụng các thay đổi
    };
    export default myGenerator;

    Dưới đây là một số thao tác phổ biến bạn có thể muốn thực hiện trong generator của mình:

    // Đọc một tệp
    const content = tree.read('path/to/file.ts', 'utf-8');
    // Ghi một tệp
    tree.write('path/to/new-file.ts', 'export const hello = "world";');
    // Kiểm tra xem tệp có tồn tại không
    if (tree.exists('path/to/file.ts')) {
    // Làm gì đó
    }

    Bạn có thể tạo các tệp bằng tiện ích generateFiles từ @nx/devkit. Điều này cho phép bạn định nghĩa các template theo cú pháp EJS, và thay thế các biến.

    import { generateFiles, joinPathFragments } from '@nx/devkit';
    // Tạo các tệp từ templates
    generateFiles(
    tree,
    joinPathFragments(__dirname, 'files'), // Thư mục template
    'path/to/output', // Thư mục đầu ra
    {
    // Các biến để thay thế trong templates
    name: options.name,
    nameCamelCase: camelCase(options.name),
    nameKebabCase: kebabCase(options.name),
    // Thêm các biến khác nếu cần
    },
    );

    Bạn có thể sử dụng phương thức tsAstReplace được cung cấp bởi Nx Plugin for AWS để thay thế các phần của một abstract syntax tree TypeScript.

    import { tsAstReplace } from '@aws/nx-plugin/sdk/utils/ast';
    import * as ts from 'typescript';
    // Ví dụ: Tăng số phiên bản trong một tệp
    tsAstReplace(
    tree,
    'path/to/version.ts',
    'VariableDeclaration:has(Identifier[name="VERSION"]) NumericLiteral',
    (node: ts.NumericLiteral) =>
    ts.factory.createNumericLiteral(Number(node.text) + 1));
    import { addDependenciesToPackageJson } from '@nx/devkit';
    // Thêm dependencies vào package.json
    addDependenciesToPackageJson(
    tree,
    {
    'new-dependency': '^1.0.0',
    },
    {
    'new-dev-dependency': '^2.0.0',
    },
    );
    import { formatFilesInSubtree } from '@aws/nx-plugin/sdk/utils/format';
    // Định dạng tất cả các tệp đã được sửa đổi
    await formatFilesInSubtree(tree, 'optional/path/to/format');
    import { readJson, updateJson } from '@nx/devkit';
    // Đọc một tệp JSON
    const packageJson = readJson(tree, 'package.json');
    // Cập nhật một tệp JSON
    updateJson(tree, 'tsconfig.json', (json) => {
    json.compilerOptions = {
    ...json.compilerOptions,
    strict: true,
    };
    return json;
    });

    Mở rộng một Generator từ Nx Plugin for AWS

    Phần tiêu đề “Mở rộng một Generator từ Nx Plugin for AWS”

    Bạn có thể import các generator từ Nx Plugin for AWS, và mở rộng hoặc kết hợp chúng theo ý muốn, ví dụ bạn có thể muốn tạo một generator xây dựng dựa trên một dự án TypeScript:

    import { tsProjectGenerator } from '@aws/nx-plugin/sdk/ts';
    export const myGenerator = async (tree: Tree, schema: MyGeneratorSchema) => {
    const callback = await tsProjectGenerator(tree, { ... });
    // Mở rộng generator dự án TypeScript ở đây
    // Trả về callback để đảm bảo dependencies được cài đặt.
    // Bạn có thể wrap callback nếu muốn thực hiện các thao tác bổ sung trong generator callback.
    return callback;
    };

    Bạn có thể sử dụng và mở rộng các generator mà chúng tôi sử dụng cho TypeScript clients và hooks theo cách tương tự như trên:

    import { openApiTsClientGenerator } from '@aws/nx-plugin/sdk/open-api';
    export const myGenerator = async (tree: Tree, schema: MyGeneratorSchema) => {
    await openApiTsClientGenerator(tree, { ... });
    // Thêm các tệp bổ sung ở đây
    };

    Chúng tôi cũng cung cấp một phương thức cho phép bạn xây dựng một cấu trúc dữ liệu có thể được sử dụng để lặp qua các operations trong một đặc tả OpenAPI và do đó điều khiển việc tạo mã của riêng bạn, ví dụ:

    import { buildOpenApiCodeGenerationData } from '@aws/nx-plugin/sdk/open-api.js';
    export const myGenerator = async (tree: Tree, schema: MyGeneratorSchema) => {
    const data = await buildOpenApiCodeGenerationData(tree, 'path/to/spec.json');
    generateFiles(
    tree,
    joinPathFragments(__dirname, 'files'), // Thư mục template
    'path/to/output', // Thư mục đầu ra
    data,
    );
    };

    Điều này cho phép bạn viết các template như:

    files/my-operations.ts.template
    export const myOperationNames = [
    <%_ allOperations.forEach((op) => { _%>
    '<%- op.name %>',
    <%_ }); _%>
    ];

    Tham khảo codebase trên GitHub để biết các ví dụ template phức tạp hơn.

    Bạn có thể chạy generator của mình theo hai cách:

    1. Install the Nx Console VSCode Plugin if you haven't already
    2. Open the Nx Console in VSCode
    3. Click Generate (UI) in the "Common Nx Commands" section
    4. Search for @my-project/nx-plugin - my-generator
    5. Fill in the required parameters
      • Click Generate

      Các bài kiểm tra đơn vị cho generators rất đơn giản để triển khai. Đây là một mẫu điển hình:

      import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
      import { yourGenerator } from './generator';
      describe('your generator', () => {
      let tree;
      beforeEach(() => {
      // Tạo một workspace tree trống
      tree = createTreeWithEmptyWorkspace();
      // Thêm bất kỳ tệp nào đã tồn tại trong tree
      tree.write(
      'project.json',
      JSON.stringify({
      name: 'test-project',
      sourceRoot: 'src',
      }),
      );
      tree.write('src/existing-file.ts', 'export const existing = true;');
      });
      it('should generate expected files', async () => {
      // Chạy generator
      await yourGenerator(tree, {
      name: 'test',
      // Thêm các tùy chọn bắt buộc khác
      });
      // Kiểm tra rằng các tệp đã được tạo
      expect(tree.exists('src/test/file.ts')).toBeTruthy();
      // Kiểm tra nội dung tệp
      const content = tree.read('src/test/file.ts', 'utf-8');
      expect(content).toContain('export const test');
      // Bạn cũng có thể sử dụng snapshots
      expect(tree.read('src/test/file.ts', 'utf-8')).toMatchSnapshot();
      });
      it('should update existing files', async () => {
      // Chạy generator
      await yourGenerator(tree, {
      name: 'test',
      // Thêm các tùy chọn bắt buộc khác
      });
      // Kiểm tra rằng các tệp hiện có đã được cập nhật
      const content = tree.read('src/existing-file.ts', 'utf-8');
      expect(content).toContain('import { test } from');
      });
      it('should handle errors', async () => {
      // Mong đợi generator ném ra lỗi trong các điều kiện nhất định
      await expect(
      yourGenerator(tree, {
      name: 'invalid',
      // Thêm các tùy chọn sẽ gây ra lỗi
      }),
      ).rejects.toThrow('Expected error message');
      });
      });

      Các điểm chính để kiểm tra generators:

      • Sử dụng createTreeWithEmptyWorkspace() để tạo một hệ thống tệp ảo
      • Thiết lập bất kỳ tệp tiên quyết nào trước khi chạy generator
      • Kiểm tra cả việc tạo các tệp mới và cập nhật các tệp hiện có
      • Sử dụng snapshots cho nội dung tệp phức tạp
      • Kiểm tra các điều kiện lỗi để đảm bảo generator của bạn thất bại một cách nhẹ nhàng

      Bạn cũng có thể sử dụng ts#nx-generator để tạo scaffold cho một generator trong @aws/nx-plugin.

      Khi generator này được chạy trong repository của chúng tôi, nó sẽ tạo các tệp sau cho bạn:

      • Thư mụcpackages/nx-plugin/src/<name>/
        • schema.json Schema cho đầu vào của generator
        • schema.d.ts Các kiểu TypeScript cho schema
        • generator.ts Triển khai generator
        • generator.spec.ts Các bài kiểm tra cho generator
      • Thư mụcdocs/src/content/docs/guides/
        • <name>.mdx Trang tài liệu cho generator
      • packages/nx-plugin/generators.json Được cập nhật để bao gồm generator của bạn

      Sau đó bạn có thể bắt đầu triển khai generator của mình.