Write a UML plug-in to automatically generate code to free cv's hands

Keywords: Javascript less React

Introduction

Recently, I am writing a middle stage project, which uses the UML framework of react.
All kinds of additions, deletions, modifications and searches. It's basically a list page, a new page, a detail page
In order to avoid unnecessary simple repetition (mainly to be lazy), I want to implement my own code generator

explore

First of all, I saw a generator written by the government on the official website

Go to the source code and find the key

In short, the plug-in api is used to register an instruction to generate model. The generator points to model.js in the directory

The code is as follows

import { join } from 'path';
import assert from 'assert';

export default api => {
  const { paths, config } = api;
  const absTemplatePath = join(__dirname, '../template/generators');

  return class Generator extends api.Generator {
    writing() {
       ...
       // Determine whether the directory name is models or models
      const models = config.singular ? 'model' : 'models';
      const name = this.args[0].toString();
      ...
     // Copy the model code in the template directory to the model directory of the project and name it the filename of the instruction input
      this.fs.copyTpl(
        join(absTemplatePath, 'model.js'),
        join(paths.absSrcPath, models, `${name}.js`),
        {
          name,
        },
      );
    }
  };
};
../template/generators/model.js

export default {
  state: '<%= name %>',
  subscriptions: {
    setup({ dispatch, history }) {
    },
  },
  reducers: {
    update(state) {
      return `${state}_<%= name %>`;
    },
  },
  effects: {
    *fetch({ type, payload }, { put, call, select }) {
    },
  },
}

Model is a regular model of dva
The <% = name% > in it is the ejs syntax, which corresponds to the name in the third parameter of copyTpl method
This placeholder in the template js will be replaced by the parameter name

Because in our project, we are used to writing the model to the module folder, and the code in the model has some of our own writing
So you need to customize a build method.

Upper hand

Here is a generation rule I wrote

import { join } from 'path';
const fs=require('fs');
export default api => {
  const  {paths} = api;
  const configPath=join(paths.absSrcPath,'generatorConfig.js');
  const absTemplatePath = join(__dirname, '../template/generators');
  return class Generator extends api.Generator {
    writing() {
      const name = this.args[0].toString();
      // assert(!name.includes('/'), `model name should not contains /, bug got ${name}`);
      const type =this.args[1]&& this.args[1].toString();
     // type is the parameter following the command
      switch (type) {
        case 'list':
          if(!fs.existsSync(configPath)) {
            api.log.error('New list template missing generatorConfig.js')
            return
          }
          const genConfig=require(configPath);
          this.fs.copyTpl(join(absTemplatePath, 'list.js'),join(paths.absSrcPath, `pages/${name}/${type}`, `index.js`), {
            name,
            queryFormItems:genConfig[name]['queryFormItems'],
            columns:genConfig[name]['columns']
          });
      }
      this.fs.copyTpl(join(absTemplatePath, 'model.js'), join(paths.absSrcPath, `pages/${name}`, `model.js`), {
        name
      });
      this.fs.copyTpl(join(absTemplatePath, 'index.less'), join(paths.absSrcPath, `pages/${name}`, `index.less`), {
        name
      });
      this.fs.copyTpl(join(absTemplatePath, 'service.js'), join(paths.absSrcPath, `pages/${name}`, `service.js`), {
        name
      });
    }
  };
};

The following functions have been added

  • Generate the directory according to the directory structure convention in the project (for example, we agree to use service for interface method management)
  • Add different parameters after the command to generate different feature modules (such as list details)
  • Add configuration item to read configuration in node environment and generate it into code (such as columns in antd list)

Follow the process of UMI DVA plugin for command registration and plug-in export

import { join } from 'path';
export default(api, opts = {})=> {
  api.registerGenerator('dva:newPage', {
    Generator: require('./model').default(api),
    resolved: join(__dirname, './model'),
  });
}

Encounter problems

There are many problems in exploration and practice. The summary is as follows
1. Read the source code to identify. Because there are many code thieves in the umi-dva-plugin, and the template function is only one of the non core functions, it has been seen several times that this function does not exist coupling with other codes, which can be proposed separately
2. I didn't know at the beginning that ejs found the copyTpl method

Then I suddenly realized that it's no wonder I look so familiar. By the way, I learned the difference between the ejs template <% =% > and <% -% >
3. A strange problem encountered in compatibility problem node environment compatibility problem
class constructor Generator cannot be invoked without 'new
It seems to be a compatibility problem, and then use the babel converter of the web version to turn off preset es2015 and adjust the node version to 6.4, which is mainly to convert the object's Deconstruction and assignment, otherwise the three-party Generator that depends on may not recognize it

summary

Now it seems that it's not difficult to write this plug-in, but it's still a little tricky when we don't know a lot of knowledge at that time. It's challenging to understand the usage and principle. After all, it's not the code that we write, so we should strengthen the reading of the code.

Project link

Poke me to check it out.

Posted by fitzsic on Tue, 10 Dec 2019 10:23:56 -0800