DEV Community

Daniel.xiao
Daniel.xiao

Posted on

The new generation of code generator

WX20210509-175440@2x.png

Speaking from a scaffold

Daniel: Recently I want to build a code generator that can quickly generate project codes. What recommendation does Brother Egg have?

Mr. Egg: I have always used yeoman in the past, and it is an open source project that is nearly 10k stars. However, what I want to recommend to you today is not yeoman, but a new generation code generator ncgen, it may seem more approachable.

Daniel: I like the simple one the most. How does it work?

Mr. Egg: Old rules, you say your needs, I will try to answer them one by one

First, you need a project template

Daniel: I have a project template (for example: vue3-ncgen-demo), I hope all new projects come from this project template, so I only need to concentrate on maintaining this project template.

Mr. Egg: OK, this is the function of the project scaffolding. Let's take a look at how ncgen is processed.

The first step is to install ncgen

$ npm i ncgen -g # yarn global add ncgen
Enter fullscreen mode Exit fullscreen mode

The second step is to generate the configuration file ncgen-config.js, which describes the logic of the code generator

$ ncgen genConf
Enter fullscreen mode Exit fullscreen mode

Step 3 Modify main.tmplSource in ncgen-config.js to the url of the project template.

export default {
  main: {
    tmplSource: "https://github.com/daniel-dx/vue3-ncgen-demo.git",
  },
};
Enter fullscreen mode Exit fullscreen mode

Try it out:

ncgen ncgen-config.js
Enter fullscreen mode Exit fullscreen mode

Simple copying is not enough, modification is necessary

Daniel: Whoops is good. However, the currently generated project is exactly the same as the project template, but it will always have its own information that is different from the project template, such as the name of the project, the name of the author, etc. I don’t want to modify these manually every time the project is generated.

Mr. Egg: OK, the request is very reasonable. Since this information can only be provided by the person who created the project, we need to collect this information through some questions, and then we can make some modifications to the generated project based on this information. We modify the main.prompt and main.updateFiles in ncgen-config.js

Example description:

Perform string replacement on the package.json file in the generated project, the rules are as follows:

Replace the string vue3-ncgen-demo with the project name entered by the user

Replace the string Daniel.xiao with the author name entered by the user

export default {
  main: {
    prompt: [
      {
        type: "input",
        name: "author",
        message: "What is the author's name",
      },
    ],

    ...

    updateFiles: {
      "package.json": function (content, options) {
        const answers = this.$answers
        return api.replace(content, {
          "vue3-ncgen-demo": answers.projectNameObj.kebabCase,
          "Daniel.xiao": answers.author,
        });
      },
    },
  }
};
Enter fullscreen mode Exit fullscreen mode

Daniel: Hey, I noticed that the template engine is not used here, but the string replacement is used directly

Mr. Egg: Yes, this design has a lot of meaning. Using a template engine to replace files may cause the project template itself to fail to run normally, because the template engine requires placeholders, and placeholders may cause code parsing errors

Daniel: Yes, in this way the project template is just an ordinary project, and there is no need to make some template placeholder transformations.

What about extra files? please delete

Daniel: Then I will continue. There are some template directories and files (such as module template directories, component template files) in my project template, but I don't want to see these templates in the generated project.

Mr. Egg: OK, no problem, just delete the specified files and directories. Let's modify the main.removeFiles in ncgen-config.js.

Example description:

Delete the ncgen-config.js and src/components/base/Template.vue files in the generated project

export default {
  main: {
    removeFiles: ["ncgen-config.js", "src/components/base/Template.vue"],
  },
};
Enter fullscreen mode Exit fullscreen mode

One-stop service: automatic installation of dependencies

Daniel: I just noticed that the above example will automatically install dependencies when it runs. It should be installed with npm, can this support yarn? If I am a non-NodeJS project, such as Python, Go, etc., can I do it?

Mr. Egg: Of course! The generated ncgen-config.js uses npm i to install dependencies by default. See the example below. If you want to change to yarn, just change command to yarn install. And if it is Python, Go and other languages, you only need to change command to the corresponding dependency installation command.

export default {
  main: {
    installDependencies: {
      skip: false,
      tips: "Dependencies are being installed, it may take a few minutes",
      command: "npm i",
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

Finally, give some friendly tips

Daniel: That’s great. The project scaffolding was completed in just a few clicks. I think a friendly welcome and beautiful ending is needed in the end.

Mr. Egg: Simply modify the main.welcome and main.complete as you wish

export default {
  main: {
    welcome: "Welcome to use (Vue 3 + TypeScript + Vite) project generator",

    ...

    complete: "Congratulations, the operation is successful",
  },
};
Enter fullscreen mode Exit fullscreen mode

High-frequency use is not scaffolding

Daniel: The scaffolding is done, but it is only used when building a new project. High-frequency operations are still part of the increase in code, such as adding a functional module, adding a component, adding an API, etc.

Mr. Egg: I understand what you mean. Old rules, you ask me to answer

The code template exists in the project template

Daniel: I want to add a new component to a project. I don't want to copy an existing component and then perform various operations to modify and delete the code. In fact, there is a component template in the project template

Mr. Egg: OK. Let's first add a subcommand called add-component in ncgen-config.js.

Example description (assuming that the values ​​of category and name are'busi' and'demo' respectively):

description is used to describe the function of a subcommand.

api.listDirs This API is very useful for allowing users to choose where to insert the code.

The configuration of addFilesTo will insert the src/components/base/Template.vue in the project template into the src/components/busi/Demo.vue file in the project.

export default {
  sub: {
    "add-component": {
      description: "Add vue component",

      prompt: [
        {
          type: "list",
          choices: function () {
            return api.listDirs("src/components/");
          },
          name: "category",
          message: "Please select the category",
        },
        {
          type: "input",
          name: "name",
          message: "What is the component name",
          validate(input) {
            if (!input) return "The component name is required";
            return true;
          },
        },
      ],

      tmplSource: "https://github.com/daniel-dx/vue3-ncgen-demo.git",

      addFilesTo: function () {
        const answers = this.$answers;
        return {
          "src/components/base/Template.vue": `src/components/${answers.category}/${answers.nameObj.upperFirstCamelCase}.vue`,
        };
      },
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

The code template does not exist in the project template

Daniel: Pretty. But for the existing projects, these projects are not from the project template, and I also want to add some sub-commands to generate part of the code for the project, how to do?

Mr. Egg: The subcommand supports two ways to add files, one is the code template from the project template mentioned above, and the other is dynamically created by you. Both can be used at the same time. The following example demonstrates how to dynamically create a code file

Example description (assuming that the values ​​of category and name are'busi' and'demo' respectively):

The configuration of addFiles will create a src/components/busi/Demo.md file in the project. The content of this file is # Demo

export default {
  sub: {
    "add-component": {
      addFiles: function () {
        const answers = this.$answers;
        return {
          [`src/components/${answers.category}/${answers.nameObj.upperFirstCamelCase}.md`]: function () {
            return `# ${answers.nameObj.upperFirstCamelCase}`;
          },
        };
      },
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

Highly recommended replacement technique

Daniel: Next, some file contents are modified (for example, when a page is added, the routing rules file will be automatically modified to register routes for the page), right? The operation is the same as the main command.

Mr. Egg: Well savvy. A tip recommended here is to add some identification comments where you need to insert the fragment code, as shown in the src/App.vue code:

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <HelloWorld msg="Hello Vue 3 + TypeScript + Vite" />
  <!-- Don't touch me-place component -->
</template>

<script lang="ts">
import {defineComponent} from'vue'
import HelloWorld from'./components/busi/HelloWorld.vue'
// <!-- Don't touch me-import component -->
export default defineComponent({
  name:'App',
  components: {
    HelloWorld,
    // <!-- Don't touch me-register component -->
  }
})
</script>
Enter fullscreen mode Exit fullscreen mode

Cooperate with api.insertBefore this API to insert the specified content before the specified matching position of the file

export default {
  sub: {
    updateFiles: function () {
      const answers = this.$answers;
      return {
        "src/App.vue": function (content, options) {
          return api.insertBefore(content, {
            "// <!-- Don't touch me-import component -->": `import ${answers.nameObj.upperFirstCamelCase} from'./components/${answers.category}/${answers.nameObj.upperFirstCamelCase }.vue'`,
            "// <!-- Don't touch me-register component -->": `${answers.nameObj.upperFirstCamelCase},`,
            "<!-- Don't touch me-place component -->": `<${answers.nameObj.upperFirstCamelCase}/>`,
          });
        },
        [`src/components/${answers.category}/${answers.nameObj.upperFirstCamelCase}.vue`]: function (
          content,
          options
        ) {
          return api.replace(content, {
            Template: `${answers.nameObj.upperFirstCamelCase}`,
          });
        },
      };
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

Daniel: Perfect. Thank you brother egg. I am now eager to try my first code generator

Mr. Egg: Welcome, looking forward to your feedback

Written at the end

Above - complete configuration

For the complete configuration of ncgen-config.js in the example, please view: https://github.com/daniel-dx/vue3-ncgen-demo/blob/master/ncgen-config.js

Below - ncgen official website


Keywords: ncgen, scaffolding, generator, code generator

Top comments (0)