Preface
In the previous article, we implemented a general scaffolding tool for web engineering, the purpose is to quickly build the project. In actual development, especially the background management system, there are many similar code implementations. Ever since, we can continue to implement a tool to quickly generate web code templates, bid farewell to copy/paste.
Basic process
The basic idea is actually very simple, that is, to call the defined template through the command, and then generate the code file:
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
│ ├─ reactFuncTS.js
│ └─ reactFuncTSX.js
├─ utils
│ └─ index.js
├─ .gitignore
├─ LICENSE
├─ package.json
└─ README.md
Implementation
The usefulness of many dependencies has been mentioned in the previous article, so this article will not introduce too much.
Initialize the project
It can be created with npm init or modified according to the package.json listed below.
{
"name": "xman-tcli",
"version": "1.0.0",
"description": "web-cli工具,可以快速创建template",
"bin": {
"xmant": "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"
}
}
Write bin/xman.js
#!/usr/bin/env node
const { program } = require('commander');
program
.version(require('../package').version, '-v, --version');
program.parse(process.argv); // 这里是必要的
if (!program.args.length) {
program.help();
}
In the current xmant-cli directory, npm link
, you can debug the scaffolding tools locally.
Then execute in the current directory:
xmant -v
It shows that the project was initially successfully built.
Quickly create a single code template through commands
Write a template
Code templates can be extracted according to actual projects. Here are a few simple templates 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>
)
}
}
`
}
templates/js/reactFuncJSX.js
module.exports = function (funcName) {
return `
import React, {useEffect, useState} from 'react';
const ${funcName} = (props) => {
return (
<div></div>
)
}
export default ${funcName};
`
}
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>
)
}
}
`
}
templates/ts/reactFuncTS.js
module.exports = function (funcName) {
return `
export const ${funcName} = () => {
}
`
}
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};
`
}
After the template is defined, it is index.js
uniformly through 0619519032a406.
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');
// 命名规范:name由“-”链接,前面为模板名,后面为创建后文件的后缀
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
}
]
The "naming convention" here is to get the corresponding suffix when creating files later.
Create the utility function utils/index.js:
module.exports = {
getFileSuffix: (name) => {
if(typeof name === 'string') {
return name.split('-')[1]
}
}
}
Write logic for creating files
Ready to work, the next step is the file creation logic command/createFile.js:
// 创建单个文件
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: '请选择你想要生成的代码模板:',
choices: templates
},
{
name: 'filename',
type:'input',
message: '请输入代码文件中类名或方法名:',
validate: function (value) {
if (value.length) {
return true;
} else {
return '请输入代码文件中类名或方法名';
}
},
}
])
.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}`
// 检验当前文件夹下是否有同名文件
fs.access(file, function(err) {
if(!err) {
console.log('创建失败:', chalk.yellow('文件已存在'))
} else {
fs.writeFile(file, item.src(filename), function(err) {
if(err) {
console.log('创建失败:', chalk.red(err))
} else {
console.log(chalk.green(`创建文件成功!${file}`));
}
})
}
})
}
})
})
}
It should be noted here that if you do not check whether there is a file with the same name in the current folder before the file is created, the original file with the same name will be overwritten.
Write command
The final step is to write bin/xman.js:
#!/usr/bin/env node
const { program } = require('commander');
...
program
.command('create')
.description("Create a file")
.alias('c')
.action(() => {
require('../command/createFile')()
});
...
debugging
In the current implementation of the project folder npm link --force
, then just find the next file xmant c
:
Open our newly created file to see:
You can also choose other templates to create a try.
Quickly create code templates in batches through commands
What if we want to create a large number of code templates at once? Of course, files are created in batches through commands.
The idea here: read the configuration file and then create it in batches.
Write configuration file
// 说明:
// folder: 文件夹名,可以嵌套,用 “/”分隔
// fileName: 文件名
// funcName: 类名或函数名
// template: 用到的文件模板
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'
}
]
The file template used here is the one we wrote before.
Write logic for creating files in batches
Batch creation of folders and files according to the configuration file command/createManyFiles.js:
// 批量创建文件
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('正在创建...');
// 递归创建目录 同步方法
function mkdirsSync(dirname) {
if (fs.existsSync(dirname)) {
return true;
} else {
if (mkdirsSync(path.dirname(dirname))) {
fs.mkdirSync(dirname);
console.log(chalk.green(`创建目录成功-${dirname}`));
}
}
}
module.exports = () => {
inquirer.prompt([
{
name: 'choices',
type:'list',
message: '请确认配置好模板批量生成列表',
choices: ['yes', 'no']
}
])
.then(answers => {
const choices = answers.choices
if(choices === 'yes') {
// 批量创建目录
fileList.forEach(item => {
if(item.folder) {
mkdirsSync(`${item.folder}`)
}
})
// 批量创建文件
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('创建失败:', chalk.red(err))
} else{
console.log(chalk.green(`创建文件成功!${fileName}`));
}
})
}
})
})
}
})
}
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')()
});
...
debugging
In the current implementation of the project folder npm link --force
, then just find the next file xmant cm
:
Take a look at the files and folders we created in batches:
Summarize
There are many ways to quickly create code templates, such as VSCode plugins, CLI tools, and ways to create lowcode/nocode platforms. The advantage of this method in this article is that it is flexible enough, and we can make flexible transformations according to specific needs to make it more applicable.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。