preface

In the last article, we implemented a generic scaffolding tool for Web engineering to quickly build projects. In the actual development, especially the background management system, there are many similar code implementation. As a result, we can move on to implementing a tool that quickly generates Web code templates and say goodbye to copy/paste.

The basic flow

The basic idea is very simple, is to command the defined template, and generate code file:

The project structure

Xman - tcli ├ ─ bin │ └ ─ xmant. Js ├ ─ command │ ├ ─ createFile. Js │ └ ─ createManyFiles. Js ├ ─ config │ └ ─ fileList. Js ├ ─ Templates │ ├ ─ index. Js │ ├ ─ js │ │ ├ ─ reactClassJSX. Js │ │ └ ─ reactFuncJSX. Js │ └ ─ ts │ ├ ─ reactClassTSX. Js │ ├ ─ ├─ ├─ download.txt ├─ download.txt ├─ download.txt ├─ download.txtCopy the code

The specific implementation

Many of the dependencies on the usefulness of the article has been mentioned, so this article will not be too much introduction.

Initialize the project

This can be created using NPM init or modified according to package.json listed below.

{"name": "xman-tcli", "version": "1.0.0", "description": "web-cli ", "bin": {"xman-tcli" : "xman-tcli", "bin": {"xman-tcli" : "bin/xmant.js" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "https://github.com/XmanLin/xman-tcli.git" }, "keywords": [ "cli" ], "author": "xmanlin", "license": "MIT", "dependencies" : {" chalk ":" ^ 4.1.2 ", "clui" : "^ 0.3.6", "commander" : "^ 8.2.0", "figlet" : "^ 1.5.2," "handlebars" : "^ 4.7.7 inquirer", ""," ^ 8.1.5 ", "update - notifier" : "^ 5.1.0"}}Copy the code

Write the bin/xman. Js

#! /usr/bin/env node

const { program } = require('commander');

program
	.version(require('.. /package').version, '-v, --version');
	
program.parse(process.argv); // This is necessary

if(! program.args.length) { program.help(); }Copy the code

After executing NPM link in the current Xmant – CLI directory, you can debug the scaffolding tool locally.

Then execute in the current directory:

xmant -v
Copy the code

This indicates that the preliminary construction is successful.

Create a single code template quickly with commands

Write a template

Code templates can be detached depending on the actual project, and a few simple templates are used as examples.

templates/js/reactClassJSX.js

    return `
import * as React from 'react';

export class ${className} extends React.Component{
    constructor(props){
        super(props);

        this.state = {}
    }

    componentDidMount(){

    }

    render() {
        return (
            <div></div>
        )
    }
}
    ` 
}
Copy the code

templates/js/reactFuncJSX.js

module.exports = function (funcName) {
    return `
import React, {useEffect, useState} from 'react';

const ${funcName} = (props) => {
    
    return (
        <div></div>
    )
}

export default ${funcName};
    ` 
}
Copy the code

templates/ts/reactClassTSX.js

module.exports = function (className) {
    return `
import * as React from 'react';

interface Props {}
interface State {}

export class ${className} extends React.Component<Props, State>{
    constructor(props: Props){
        super(props);

        this.state = {}
    }

    componentDidMount(){

    }

    render() {
        return (
            <div></div>
        )
    }
}
    ` 
}
Copy the code

templates/ts/reactFuncTS.js

module.exports = function (funcName) {
    return `
export const ${funcName}= () => {} ' 
}
Copy the code

templates/ts/reactFuncTSX.js

module.exports = function (funcName) {
    return `
import React, {useEffect, useState} from 'react';

const ${funcName} = (props: any) => {
    
    useEffect(() => {
        
    },[])
    
    return (
        <div></div>
    )
}

export default ${funcName};
    ` 
}
Copy the code

Once the template is defined, it is exported through index.js.

templates/index.js

const reactClassJSX = require('./js/reactClassJSX');
const reactFuncJSX = require('./js/reactFuncJSX');
const reactClassTSX = require('./ts/reactClassTSX');
const reactFuncTSX = require('./ts/reactFuncTSX');
const reactFuncTS = require('./ts/reactFuncTS');
// Naming conventions: Name is linked by hyphens (-), followed by the template name and the suffix of the created file

module.exports = [
    {
        name: 'reactClass-jsx'.src: reactClassJSX
    },
    {
        name: 'reactFunc-jsx'.src: reactFuncJSX
    },
    {
        name: 'reactClass-tsx'.src: reactClassTSX
    },
    {
        name: 'reactFunc-tsx'.src: reactFuncTSX
    },
    {
        name: 'reactFunc-ts'.src: reactFuncTS
    }
]
Copy the code

The purpose of naming conventions here is to obtain suffixes for later file creation.

Create utility function utils/index.js: utils/index.js

module.exports = {
    getFileSuffix: (name) => {
        if(typeof name === 'string') {
            return name.split('-')[1]
        }
    }
}
Copy the code

Write the logic to create the file

Command/createfile.js command/ createfile.js

// Create a single file
const templates = require('.. /templates/index');
const chalk = require('chalk');
const inquirer = require('inquirer');
const fs = require("fs");
const utils = require('.. /utils/index');


module.exports = () = > {
    inquirer.prompt([
        {
            name: 'templateName'.type:'list'.message: Please select the code template you want to generate:.choices: templates
        },
        {
            name: 'filename'.type:'input'.message: 'Please enter the class name or method name in the code file:'.validate: function (value) {
                if (value.length) {
                    return true;
                } else {
                    return 'Please enter the class or method name in the code file';
                }
            },
        }
    ])
    .then(answers= > {
        const templateName = answers.templateName;
        const filename = answers.filename;
        templates.forEach((item) = > {
            if(item.name === templateName) {
                const suffix = utils.getFileSuffix(item.name)
                const file = `./index.${suffix}`
                // Check if there are files with the same name in the current folder
                fs.access(file, function(err) {
                    if(! err) {console.log('Failed to create:', chalk.yellow('File already exists'))}else {
                        fs.writeFile(file, item.src(filename), function(err) {
                            if(err) {
                                console.log('Failed to create:', chalk.red(err))
                            } else {
                                console.log(chalk.green('File created successfully!${file}`)); }})}})})})})}Copy the code

Note here: files with the same name will be overwritten if you do not check the current folder for files with the same name before creating them.

Write command

Bin /xman.js:

#! /usr/bin/env node

const { program } = require('commander'); . program .command('create')
	.description("Create a file")
	.alias('c')
	.action(() = > {
		require('.. /command/createFile') ()}); .Copy the code

debugging

NPM link –force in the current project folder, then XPM C in a random file:

Open our newly created file and see:

You can also choose to create another template to try.

Create code templates in batches using commands quickly

What if we want to create a lot of code templates at once? Create files in batches by command, of course.

The idea here is to read configuration files and batch create them.

Writing configuration files

/ / description:
// Folder: Indicates the name of a folder. The folder names can be nested and separated by slash (/)
// fileName: indicates a fileName
FuncName: name of a class or function
// template: specifies the file template to use

module.exports = [
    {
        folder: './home'.fileName: 'index'.funcName: 'Home'.template: 'reactFunc-tsx'
    },
    {
        folder: './home/compnent'.fileName: 'index'.funcName: 'Compnent'.template: 'reactFunc-tsx'
    },
    {
        folder: './home/service'.fileName: 'index'.funcName: 'service'.template: 'reactFunc-ts'
    },
    {
        folder: './news'.fileName: 'index'.funcName: 'News'.template: 'reactFunc-tsx'
    },
    {
        folder: './news/service'.fileName: 'index'.funcName: 'service'.template: 'reactFunc-ts'}]Copy the code

The file template used here is the one we wrote earlier.

Write bulk file creation logic

According to the configuration file for batch of folders and files to create the command/createManyFiles js:

// Create files in batches

const chalk = require('chalk');
const inquirer = require('inquirer');
const fs = require('fs');
const path = require('path');
const utils = require('.. /utils/index');
const fileList = require('.. /config/fileList');
const templates = require('.. /templates/index');
const clui = require('clui');
const Spinner = clui.Spinner;
const status = new Spinner('Creating... ');

// Create a directory synchronization method recursively
function mkdirsSync(dirname) {
    if (fs.existsSync(dirname)) {
        return true;
    } else {
        if (mkdirsSync(path.dirname(dirname))) {
            fs.mkdirSync(dirname);
            console.log(chalk.green('Directory created successfully -${dirname}`)); }}}module.exports = () = > {
    inquirer.prompt([
        {
            name: 'choices'.type:'list'.message: 'Make sure template batch generation list is configured'.choices: ['yes'.'no']
        }
    ])
    .then(answers= > {
        const choices = answers.choices
        if(choices === 'yes') {
            // Create directories in batches
            fileList.forEach(item= > {
                if(item.folder) {
                    mkdirsSync(`${item.folder}`)}})// Create files in batches
            fileList.forEach(item= > {
                templates.forEach(tpl= > {
                    if(item.template === tpl.name) {
                        const suffix = utils.getFileSuffix(item.template)
                        const fileName = `${item.fileName}.${suffix}`
                        fs.writeFile(`${item.folder}/${fileName}`, tpl.src(item.funcName), function(err) {
                            if(err) {
                                console.log('Failed to create:', chalk.red(err))
                            } else{
                                console.log(chalk.green('File created successfully!${fileName}`)); }})}})})}})}Copy the code

Write command

Finally write bin/xman.js:

#! /usr/bin/env node

const { program } = require('commander'); . program .command('create-many')
	.description("Create many folders and files")
	.alias('cm')
	.action(() = > {
		require('.. /command/createManyFiles') ()}); .Copy the code

debugging

Execute NPM link –force under the current project folder, then xmant CM under a random file:

Take a look at the files and folders we created in bulk:

conclusion

There are many ways to quickly create code templates, including VSCode plug-ins, CLI tools, lowcode/nocode platforms, and so on. The advantage of this method in this paper is that it is flexible enough. We can make flexible modifications according to specific needs to make it more applicable.