Node.js脚手架开发完全指南「TypeScript版」
前言
本文将事无巨细地介绍如何用node.js+typescript开发脚手架。
成品的github地址先贴一下:koa-generator-cli
还有npm的地址:koa-generator-cli
将会学到
- node.js脚手架开发
- node.js命令行工具开发
- 几个好用的npm库
简介
什么是脚手架?
我们都用过脚手架,像vue-cli、react-native-cli、express-generator等等。
脚手架提供这些的功能:
- 快速初始化项目
- 保证协作团队项目的统一
- 添加通用的组件或者配置
确定脚手架要提供什么样的功能?
我们的脚手架起名为koa-generator-cli
,顾名思义,这是一个koa项目的生成器,主要功能是生产koa项目,拆分细节,我们的功能点有以下这些。
- 下载koa2模板代码到本地。
- 接收用户输入的项目名称、描述等,用于确定目录名称和修改package文件。
- 接收用户的输入,定制项目内容(比如对中间件的选择)。
- 查看help和version。
- 对创建进度和创建结果,给出反馈。
开始操作
确定了需求之后,我们开始按部就班,操作起来!
准备工作
创建npm项目
首先创建npm项目。
npm init
然后补充必要的信息,其中main是入口文件,bin用于引入一个全局的命令,映射到lib/index.js,有了bin字段后,我们就可以直接运行koa-generator-cli
命令,而不需要node lib/index.js
了。
// package.json
{
"name": "koa-generator-cli",
"version": "0.0.1",
"description": "Koa2+TypeScript application generator",
"main": "lib/index.js",
"bin": {
"koa-generator-cli": "lib/index.js"
},
}
支持用ES6和TypeScript开发
安装typescript
和@types/node
。
npm install -save-dev typescript @types/node
初始化tsconfig.json
tsc --i
然后按我们工程的实际情况,修改下入口和输出。
// tsconfig.json
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"sourceMap": true,
"outDir": "./lib",
},
"include": [".eslintrc.js", "src/**/*"],
"exclude": ["node_modules", "lib/**/*"],
}
我们在src/index.ts
写个hello world,测试下ts编译是否正常。
// src/index.ts
#!/usr/bin/env node
const msg: string = 'Hello World'
console.log(msg)
然后执行tsc
,可以看到lib/index.js
输出了编译后的js文件,而且node lib/index.js
输出正常。
这一part完成。
引入ESLint
安装ESLint和其ts插件。
npm install eslint -save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin
然后加上.eslintrc.js
配置。
// .eslintrc.js
module.exports = {
root: true,
env: {
node: true,
es2021: true,
},
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 12,
sourceType: 'module',
tsconfigRootDir: __dirname,
project: ['./tsconfig.json'],
},
plugins: ['@typescript-eslint'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
],
}
验收一下,package.json
加上两条命令。
// package.json
"scripts": {
"lint": "eslint --ext .js .",
"lint:fix": "eslint --fix --ext .js ."
},
运行npm run lint:fix
,没有异常,完成!
npm link-本地调试
记得我们前面在package.json
中有个bin
配置,那我们直接跑koa-generator-cli
这个命令试试。
bash: koa-generator-cli: command not found
!!!嗯?原来我们现在的npm包还没发布和安装,没办法找到命令,为了方便调试,我们需要跑一下这个命令。
npm link
重新koa-generator-cli
,可以了!
为了方便调试,我们在package.json
中再加两个配置,用于调试和打包。
// package.json
"scripts": {
"dev": "tsc && node lib/index.js",
"build": "tsc",
},
命令行工具开发
接下来我们开始真正的脚手架开发。
commander-处理命令
我们用到commander来处理命令。commander是一个用于简化node.js命令行开发的库。
安装commander
。
npm install -save commander
我们先从简单的开始,接收一个输入作为新建工程的名称,先不做处理直接输出出来。
#!/usr/bin/env node
// src/index.ts
import { Command } from 'commander';
const program = new Command();
program
.name('koa-generator-cli')
.description('"Koa2+TypeScript application generator')
.version('0.0.1');
program
.command('init <name>')
.description('init a koa project')
.action((name: string) => {
console.log('start init koa project:', name)
})
program.parse();
tsc
后,运行 koa-generator-cli init firstProject
,成功输出start init koa project: firstProject
,可以了!
inquirer-处理交互
下面开始搞用户交互。
为了脚手架尽量简单易用,我们先只运行用户有少量的交互操作,inquirer是简化node.js命令行开发的一个库。
我们先确定交互有哪些,思考一下,我们先确定有下面这几个交互。
- 输入项目描述
- 输入项目作者
安装inquirer
。
npm install -save inquirer @types/inquirer
继续完善一下代码,添加交互提示。
#!/usr/bin/env node
import { Command } from 'commander';
import inquirer from 'inquirer'
import gitclone from 'git-clone';
const InitPrompts = [
{
name: 'description',
message: 'please input description',
default: '',
},
{
name: 'author',
message: 'please input author',
default: '',
}
];
const program = new Command();
program
.name('koa-generator-cli')
.description('"Koa2+TypeScript application generator')
.version('0.0.1');
program
.command('init <name>')
.description('init a koa project')
.action(async (name: string) => {
console.log('start init koa project:', name);
const initOptions = await inquirer.prompt(InitPrompts);
console.log('initOptions', initOptions);
})
program.parse();
好了,现在我们试验一下。运行npm run dev init myproject
,输出下面的结果。
LUCIOZHANG-MC1:koa-generator-cli luciozhang$ npm run dev init myproject
> koa-generator-cli@0.0.1 dev
> tsc && node lib/index.js "init" "myproject"
start init koa project: myproject
? please input description
? please input author
? 请选择子模块(comm子模块为必选项) koa-body, koa-router, koa-static, koa-views, koa-logger
initOptions {
description: '',
author: '',
middleware: [ 'koa-body', 'koa-router', 'koa-static', 'koa-views', 'koa-logger' ]
}
? install npm now Yes
OK,没问题,继续下一part。
git-clone-下载模板
不使用download-git-repo
是因为这个库有些依赖有安全问题,且已经不在维护。
我们使用git-clone这个库来下载git上的模板,这个库更小而且功能也够用。
安装git-clone
。
npm install -save git-clone @types/git-clone
新建一个download.ts
,加上下载模板的代码,并在index.ts
中引用。
注意!!下载完模板,要删除.git目录。
// download.ts
import gitclone from 'git-clone/promise';
import fs from 'fs-extra';
import path from 'path';
export const downloadTemplate = (name: string, templateGitUrl: string, downloadPath: string)=>{
return new Promise(async (resolve, reject)=>{
try {
await gitclone(templateGitUrl, downloadPath, { checkout: 'master', shallow: true });
fs.removeSync(path.join(downloadPath, '.git'))
resolve('download success');
} catch (error) {
reject(error);
}
});
}
运行npm run dev init myproject
,发现myproject目录被创建了,而且下载了github仓库的内容。
又搞定一个,继续继续!!
handlebars-语义化模板
继续完善,接下来我们要用输入的名称和描述、作者等文本,替换模板的对应字段。
在替换前,我们需要修改模板的package.json
,添加一些插槽,方便后面替换。
// 模板仓库的package.json
{
"name": "{{name}}",
"version": "1.0.0",
"description": "{{description}}",
"author": "{{author}}"
}
下面开始修改package.json
。
安装handlebars
。
npm install -save handlebars
安装fs-extra
。
npm install -save fs-extra @types/fs-extra
开始修改package.json
。
import fs from 'fs-extra';
import path from 'path';
import handlebars from 'handlebars'
export const modifyPackageJson = function (downloadPath: string, options: any) {
console.log('modifying package.json……');
const packagePath = path.join(downloadPath, 'package.json');
if (fs.existsSync(packagePath)) {
const content = fs.readFileSync(packagePath).toString();
const template = handlebars.compile(content);
const param = { name: options.name, description: options.description, author: options.author };
const result = template(param);
fs.writeFileSync(packagePath, result);
console.log('modify package.json complate');
} else {
throw new Error('no package.json');
}
}
ora-命令行美化
功能部分已经完成了,但是现在的提示是下面这样的,比较简陋。
我们来升级一下。
安装ora
。
npm install -save ora
我们已下载的代码为例,美化下输出。
import gitclone from 'git-clone/promise';
import fs from 'fs-extra';
import path from 'path';
import ora from 'ora';
export const downloadTemplate = (name: string, templateGitUrl: string, downloadPath: string)=>{
const loading = ora('download template');
return new Promise(async (resolve, reject)=>{
try {
loading.start('start download template');
await gitclone(templateGitUrl, downloadPath, { checkout: 'master', shallow: true });
fs.removeSync(path.join(downloadPath, '.git'))
loading.stop();
loading.succeed('download success');
resolve('download success');
} catch (error) {
reject(error);
loading.stop();
loading.fail('download fail');
}
});
}
再运行下,这次有了loading动画,美观多了。
总结
本文实现了最简单的一个koa生成器组件,实现的理念是,脚手架和模板都尽可能的简单。
实现过程的亮点,是使用了ts和大量ES7的语法糖,个人感觉代码风格算比较优秀的了哈哈。
成品的github地址再贴一下:koa-generator-cli。
还有npm的地址:koa-generator-cli
参考文献
使用Typescript开发node.js项目——简单的环境配置
往期好文
koa入门系列
“告别烂代码”
代码人生
前端踩坑必看指南
转载自:https://juejin.cn/post/7086340583697940516