Pont automatically generates TypeScript interface code and automatically generates types to align with the backend.

Pont

gitHub:https://github.com/alibaba/pont

Why use front-end and back-end interface synchronization tools such as Pont?

A very typical scenario where front-end and back-end students collaborate in daily development: the invocation and processing of interface data requests. In the past, collaboration was carried out through manual writing , but the correlation brought about by this method is very fragile. If the server-side code or documents change, the interface code associated with the front-end will face errors and failures.

In addition, now that TypeScript is booming, in order to make full use of the type capabilities of TS to standardize the field types of the interface, we usually also write the type definition of the interface based on the interface document. In actual development, each interface may have a large number of fields. At this time, it is very boring to transfer type definitions according to the interface document, not to mention the possibility of errors during the transfer.

Compare mainstream solutions

  • Pont : Alibaba’s interface swagger docking solution supports vscode plug-in, which is very powerful

  • swagger-api : Swagger ecological code generation tool, but requires a java runtime environment (it still needs to rely on other js libraries to use)

  • YAPI : Supports interface management, MOCK and other functions. Code generation requires java running environment (one-click generation of Ts interface code is not supported)

  • JsonToAny ( Gitee / GitHub ) is an open source tool that directly copies the imported JSON file to a custom type (I personally think this can be used in transformation projects)

Pont concept:

pont means "bridge" in French, implying the bridge between the front and rear ends.

Pont converts various interface document platforms such as swagger, rap, and dip into Pont metadata . Pont uses interface metadata to generate highly customized front-end interface layer code , interface mock platform and interface test platform.

Among them, swagger data source is perfectly supported by Pont. It has been used in some large-scale projects for nearly two years, and various highly customized needs can be met.

work process:

img

quick start

Install pont-engine globally

# 选择一个你喜欢的包管理器

# NPM
$ npm i -g pont-engine

# Yarn
$ yarn global add pont-engine

# pnpm
$ pnpm add -g pont-engine

1. Installation

Install pont-engine globally

# 选择一个你喜欢的包管理器

# NPM
$ npm i -g pont-engine

# Yarn
$ yarn global add pont-engine

# pnpm
$ pnpm add -g pont-engine

2. Initialization

Use pont startthe command to quickly create an initial template
Insert image description here

3. Install the VSCode plug-in

Open the VSCode plug-in store and enter vscode-pontsearch and install

Insert image description here

Demo

Insert image description here

Using help

Initial boot

  • The pont-config.json file does not exist in the current workspace
  • pont-enging is not installed in the current workspace

Insert image description here

Switch data sources (multiple data sources display)

Click the switch data source icon button on the right to pop up the data menu and search for switch data source
Insert image description here

Update local modules and base classes

Quickly and incrementally update modules and base classes by clicking the Update Module or Base Class icon button on the right.
Insert image description here

Interface code snippet

  • Click to open the interface search panel, quickly search for the interface that needs to be called, and generate the calling code snippet after selecting the interface.
  • Through rewriting CodeGenerator.codeSnippet, customizations are required to generate code snippets. For specific usage, please refer to the pont-config.json configuration item.
    Insert image description here

Pull remote data source

Pull and synchronize the latest remote data source data, and perform diff comparison with the local api-lock.json to calculate the modules and base classes that can be incrementally updated.
Insert image description here

Update all data sources

Use remote data source data to update the local api-lock.json file without generating interface code

Insert image description here

Generate interface code

Generate interface code by reading and parsing the api-lock.json file

Insert image description here

4. Command line mode

In order to prevent some users and technical teams from not using vscode-pont, pont can provide services in the form of command line commands.

The commands provided by the command line are currently relatively basic. The commands provided are as follows:

start point

Access pont with one click. If pont-config.jsonthe configuration file exists locally, the duplicate configuration items will be overwritten.

pont check

Verify whether the local pont-lock.json file is missing or damaged. It is recommended that users add the pont check command in pre-commit in the project to prevent pont-lock.json from being accidentally deleted or damaged when resolving conflicts in the file during team collaboration.

ls bridge

View all data sources

pont select [dsName]

Switch current data source

pont diff

Check the differences between remote data and local data in modules and base classes for targeted and selective synchronization.

updateBo bridge [boName]

Selectively update local base classes

pont updateMod [modName]

Selectively update local modules

5. pont-config.jsonConfiguration items

For the configuration of pont-config.json, functions such as automatic prompts, automatic completion, and configuration item description reminders have been implemented in the vscode-pont plug-in. The specific configuration items are introduced as follows:

originUrl

Value type: string

Description: The interface platform provides the open api url of the data source (requires login). Currently, it only supports Swagger. Such as "https://petstore.swagger.io/v2/swagger.json"

outDir

Value type: string

Description: The storage path of the generated code, just use a relative path. For example: "./src/api"

templatePath

Value type: string

Description: Specifies the path to the custom code generator (specified using a relative path). Once specified, pont will generate a default custom code generator. A custom code generator is a ts file that can be used to generate custom code by overriding the default code generator. The default code generator contains two classes, one is responsible for managing the directory structure, and the other is responsible for managing how to generate code for each file in the directory structure. The custom code generator achieves customization by inheriting these two classes (the type is perfect, you can view the prompts and meanings) and overwriting the corresponding code. Please refer to the custom code generator documentation for specific usage .

Example: You can refer to the template in the example demo.

prettierConfig

Value type: object

Description: The generated code will be beautified with prettier. Just configure the prettier configuration items here. For details, please refer to the prettier documentation .

usingMultipleOrigins

Value type: boolean

Description: pont supports configuring multiple Swagger sources in one project. Configure here whether to enable multiple data sources

origins

Value type: array

Description: Configure each data source

Configuration items:

{
    
    
  "originType": "SwaggerV2 | SwaggerV3", // 注:暂不支持 SwaggerV1
  "originUrl": string,
  "name": string,
  "usingOperationId": boolean,
  "transformPath"?: string,
  "fetchMethodPath"?: string
}

Example:

"origins": [{
    
    
  "name": "pet",
  "originUrl": "",
}, {
    
    
  "name": "fruit",
  "originUrl": ""
}]

transformPath

Value type: string

Description: Optional. Specify the data source preprocessing path (specify using relative paths). Once specified, Pont will generate a default data preprocessor. After converting the Swagger.json data to an internal standard data source, Pont will try to call transformPaththe specified conversion program, so that the user has the opportunity to perform some processing on the data.

Data preprocessor example:

// transfrom.ts 根据 Mod.name进行过滤
import {
    
     StandardDataSource } from 'pont-engine';

export default function transform(data: StandardDataSource) {
    
    
  if (data.name === 'fooapi') {
    
    
    const filterMods = ['modName1', 'modName2', 'modName3'];
    let {
    
     mods, baseClasses } = filterModsAndBaseClass(filterMods, data);
    data.mods = mods;
    data.baseClasses = baseClasses;
  }
  return data;
}

/**
 * 过滤mod及所依赖的baseClass
 * @param filterMods Mod.name数组
 * @param data StandardDataSource
 */
function filterModsAndBaseClass(filterMods: string[], data: StandardDataSource) {
    
    
  let mods = data.mods.filter(mod => {
    
    
    return filterMods.includes(mod.name);
  });
  // 获取所有typeName
  let typeNames = JSON.stringify(mods).match(/"typeName":".+?"/g);

  typeNames = Array.from(new Set(typeNames)) // 去重
    // 取typeName的值
    .map(item => item.split(':')[1].replace(/\"/g, ''));

  // 过滤baseClasses
  let baseClasses = data.baseClasses.filter(cls => typeNames.includes(cls.name));

  return {
    
     mods, baseClasses };
}

fetchMethodPath

Value type: string

Description: Optional, relative to the project root directory path. Used in scenarios where the Swagger data source requires login before the request can be successful. You can specify the method to obtain the Swagger source data. The default is the fetch method of node-fetch. You can obtain the document of the interface with authentication through the customized fetch method.

Example:

NOTE: This file can currently only use .tsthe suffix

// ./myFetchMethod.ts
import axios from 'axios';

export default async function(url: string): Promise<string> {
    
    
  const {
    
     data } = await axios.post('/api/login', {
    
    
    username: 'my_name',
    password: '123456'
  });

  return axios
    .get(url, {
    
    
      headers: {
    
    
        Authorization: data.token
      }
    })
    .then(res => JSON.stringify(res.data));
}

Configuration item example:

Note: The path field does not need to be .tssuffixed

{
    
    
  // ...
  "fetchMethodPath": "./myFetchMethod", 
}

mocks

Value type: object

Subfield:

  • Field name: "enable" Type: boolean Default value: true Meaning: Whether to take effect
  • Field name: "basePath" Type: string Default value: "" Meaning: basePath of the interface
  • Field name: "port" Type: string Default value: 8080 Meaning: Port number of the mocks service
  • Field name "wrapper" Type: string Default value: "{"code": 0, "data": {response}, "message": ""}" Meaning: The interface returns a structure, pont can calculate the return data type (such as this will be replaced by {response}), where the interface return structure can be specified.

like:

{
    
    
  "mocks": {
    
    
    "enable": true,
    "basePath": "",
    "port": 8080,
    "wrapper": "{\"code\": 0, \"data\": {response}, \"message\": \"\"}"
  }
}

templateType

Value type: string

Optional values: 'fetch' | 'hooks'

Description: Optional. Built-in template for generating pont. When configuring this item, once it is detected that the local template file does not exist, the configured template type will be automatically used to generate a template file.

The built-in templates are powerful. For how to use them, please refer to the built-in template usage method and contribution process .

6.pontTemplate.ts (automatic code template generation)

Example:

import {
    
     Interface, BaseClass, Property, CodeGenerator } from "pont-engine";

export default class MyGenerator extends CodeGenerator {
    
    
  getInterfaceContentInDeclaration(inter: Interface) {
    
    
    const method = inter.method.toUpperCase();

    const paramsCode = inter
      .getParamsCode("Params")
      .replace("lock: number", "lock?: number")
      .replace(": file", ": FormData");

    return `
      export ${
      
      paramsCode}

      export type HooksParams = () => Params | Params;

      export type Response = ${
      
      inter.responseType}

      export function mutate(params?: HooksParams, newValue?: any, shouldRevalidate = true);
  
      export function trigger(params?: HooksParams, shouldRevalidate = true);

      ${
      
      
        method === "GET"
          ? `
        export function useRequest(params?: HooksParams, options?: ConfigInterface): { isLoading: boolean; data: Response, error: Error };`
          : `
        export function useRequest(params?: HooksParams, options?: ConfigInterface): { isLoading: boolean; data: Response, error: Error };
        `
      }

      export const method: string;

      export function request(params?: Params, option = {}): Promise<Response>;
    `;
  }

  getBaseClassInDeclaration(base: BaseClass) {
    
    
    const originProps = base.properties;

    base.properties = base.properties.map(prop => {
    
    
      return new Property({
    
    
        ...prop,
        required: false
      });
    });

    const result = super.getBaseClassInDeclaration(base);
    base.properties = originProps;

    return result;
  }

  getCommonDeclaration() {
    
    
    return `
    declare type ConfigInterface = import("swr").ConfigInterface;
    `;
  }

  getInterfaceContent(inter: Interface) {
    
    
    const method = inter.method.toUpperCase();

    return `
    /**
     * @desc ${
      
      inter.description}
     */

    import * as defs from '../../baseClass';
    import * as Hooks from '../../hooks';

    import * as SWR from 'swr';

    import { PontCore } from '../../pontCore'

    export ${
      
      inter.getParamsCode("Params", this.surrounding)}

    export const method = "${
      
      method}";

    export function mutate(params = {}, newValue = undefined, shouldRevalidate = true) {
      return SWR.mutate(Hooks.getUrlKey("${
      
      
        inter.path
      }", params, "${
      
      method}"), newValue, shouldRevalidate);
    }

    export function trigger(params = {}, shouldRevalidate = true) {
      return SWR.trigger(Hooks.getUrlKey("${
      
      
        inter.path
      }", params, "${
      
      method}"), shouldRevalidate);
    }

    ${
      
      
      method === "GET"
        ? `
      export function useRequest(params = {}, swrOptions = {}) {
        return Hooks.useRequest("${ 
        inter.path}", params, swrOptions);
      };`
        : `
      export function useDeprecatedRequest(params = {
      
      }, swrOptions = {
      
      }) {
      
      
        return Hooks.useRequest("${inter.path}", params, swrOptions, {
      
       method: ${
      
      method} });
      }
      `
    }

    export function request(params = {
    
    }, option  = {
    
    }) {
    
    
      return PontCore.fetch(PontCore.getUrl("${
    
    
        inter.path
      }", params, "${
    
    method}"), {
    
    
        ...option,
        method: "${method}",
      });
    }`;
  }
}

inter variable definition:

Insert image description here

7. Customized Pont

Please refer to https://github.com/alibaba/pont/blob/master/docs/customizedPont.md

8. Summary

​ Pont is a tool that can be used as a well-defined interface specification for back-end and front-end. Compared with other tools on the market, its advantage is that it is easy to use and can configure templates to automatically generate interface code. What I currently practice is that in the early stages of project construction, if the backend can cooperate with Swagger, the entire generated code directory can be directly configured into the src directory through config.json. If the backend updates the content of the interface, it will The key generation can be synchronized.

There are many advantages, but also some disadvantages:

​ 1. The custom pontTemplate.ts format is generated by returning a string format. It is not very friendly to change and needs to be generated in the form of string matching and replacement.

2. It is not friendly to secondary development projects or old projects. The code styles are too different and difficult to modify.

​ 3. It is closely connected to the back-end docking. If the back-end Swagger format is wrong, it will not be easy to use, and there may be more manual configuration.

4. For JS projects, there is no need to use it, because the biggest role of using this tool is to define types. If it is a pure JS project, the introduction is more complicated and it will not play any role in verifying types. On the contrary, it increases the complexity of development.

Guess you like

Origin blog.csdn.net/m0_65035567/article/details/130278459