介绍
工作中常会使用一些类似vue-cli
的脚手架工具,快速生成所需要的项目结构、文件以及一些基本的功能等。本文所介绍的koa-setup可以帮助我们快速生成一个简单的Node-koa
的项目。
基本思想
回顾一下正常使用npm
创建项目的步骤:
- 使用
npm init
命令初始化项目,此时会要求我们输入项目名称、版本号等内容; - 完成后,会生成项目必需的文件
package.json
,里面包含项目的基本信息、配置以及项目所需要的第三方依赖包等; - 再创建一个项目入口文件,
index.js
,即可完成一个简单项目的搭建; - 如需搭建vue项目或node项目,可在当前基础上,添加相关对应配置文件、第三包依赖包等。
因此,我们要搭建一个koa项目快速创建的脚手架工具,这个脚手架工具所必须的功能就是帮我们创建package.json
文件以及入口文件index.js
,其次就是帮我们创建koa相关内容,以及下载koa
所需要的第三方依赖包。
注:package.json详细内容可查看Node官方相关介绍
详细内容
koa-setup项目文件结构
1. koa-setup主入口js文件
该文件为本项目的主要文件,用于生成项目文件夹、index.js、package.json文件、以及第三方依赖的安装。
#! /usr/bin/env node
// 上面这句话非常重要
import fs from 'fs';
import chalk from 'chalk';// 用于日志打印的插件
import { execa } from 'execa';// 用于执行shell命令的插件
import createIndexTemplate from './createIndexTemplate.js';// 创建index.js
import createPackageTemplate from './createPackageTemplate.js';// 创建package.json
import question from './question/index.js';// 用户交互问题
import { createConfig } from './config.js';// 生成项目配置
const answer = await question(); // 获取用户交互的答案
const config = createConfig(answer); // 根据答案生成最终的配置文件
// 1.创建项目文件夹 =》 根据用户输入的项目名称
console.log(chalk.blue(`创建文件夹 -> ${config.packageName}`));
fs.mkdirSync(getRootPath());
// 2.创建入口文件 =》 index.js
console.log(chalk.blue('创建入口文件'));
fs.writeFileSync(`${getRootPath()}/index.js`, createIndexTemplate(config));
// 3.创建package.json
console.log(chalk.blue('创建package.json'));
fs.writeFileSync(`${getRootPath()}/package.json`, createPackageTemplate(config));
// 4.安装依赖
console.log(chalk.blue('安装依赖'));
execa('yarn', {
cwd: getRootPath(),
stdio: [2, 2, 2],
});
// 获取项目路径
function getRootPath() {
return `./${config.packageName}`;
}
2. koa-setup
项目配置文件
{
"name": "koa-setup",
"version": "1.0.0",
"description": "",
"main": "index.js",
"bin": {
"mykoa": "./bin/index.js"
},
"type": "module",
"files": [
"bin",
"package.json"
],
"scripts": {
"test": "rm -rf hei && node index.js",
"test-g": "rm -rf hei && koa-setup"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"chalk": "^5.0.0",
"ejs": "^3.1.6",
"execa": "^6.0.0",
"inquirer": "^8.2.0",
"prettier": "^2.5.1"
},
"devDependencies": {
"@types/node": "^17.0.8"
}
}
3. 用户交互配置question
文件夹
使用第三方库inquirer
进行用户交互操作,要求用户输入项目名称、输入项目端口、选择项目中间件等操作。该插件提供了文本输入、数字输入、选择框选择、密码输入、记事本输入等功能,根据需要进行使用。
以下举例文本输入使用:
// bin/question/packageName.js
export default () => {
return {
type: 'input',
name: 'packageName',
message: 'set package name',
validate(val) {
if (val) return true;
return 'Please enter package name';
},
};
};
最终bin/question/index.js
中会抛出用户给出的选项供后续使用。
import inquirer from 'inquirer';
import packageName from './packageName.js';
import port from './port.js';
import middleware from './middleware.js';
export default () => {
return inquirer.prompt([packageName(), port(), middleware()]);
};
由于inquirer
插件最终给出的用户选项与所使用的的数据结构不同,通过config.js对用户选项进行重新组装抛出,得到用户的选项,并可用于创建内容使用。
// bin/config.js
export function createConfig(answer) {
function haveMiddle(name) {
return answer.middleware.indexOf(name) !== -1;
}
return {
packageName: answer.packageName,
port: answer.port,
middleware: {
static: haveMiddle('koaStatic'),
router: haveMiddle('koaRouter'),
},
};
}
注:inquirer
详细配置及其他使用方法可点击此处
4. 根据用户选项创建入口文件index.js
和配置文件package.json
上面已经获取到用户交互过程中所输入或选择的内容,接下来就根据用户的答案来创建相关文件,主要通过createIndexTemplate.js
和createPackageTemplate.js
,以及两个对应的模板,bin/template/index.ejs
,bin/template/package.ejs
来进行。
createIndexTemplate.js
中,调用模板index.ejs
,根据项目配置内容来创建index.js
,
// bin/createIndexTemplate.js
import ejs from 'ejs';
import fs from 'fs';
import prettier from 'prettier';
import path from 'path';
import { fileURLToPath } from 'url';
export default config => {
const __dirname = fileURLToPath(import.meta.url);
const indexTemplate = fs.readFileSync(path.resolve(__dirname, '../template/index.ejs'));
const code = ejs.render(indexTemplate.toString(), {
middleware: config.middleware,
port: config.port,
});
return prettier.format(code, { parser: 'babel' });
};
使用ejs模板,根据传入的参数对中间件的需要进行控制,并且实现自行配置端口号的操作。
// bin/template/index.ejs
const Koa = require('koa');
<% if (middleware.static) { %>
const serve = require('koa-static');
<% } %>
<% if (middleware.router) { %>
const Router = require('koa-router');
<% } %>
const app = new Koa();
<% if (middleware.static) { %>
app.use(serve(__dirname + '/static'));
<% } %>
<% if (middleware.router) { %>
const router = new Router();
router.get('/', ctx => {
ctx.body = 'hello koa-setup';
});
app.use(router.routes());
<% } %>
app.listen(<%= port %> , () => {
console.log('open server localhost:<%= port %> ');
});
同理,调用createPackageTemplate.js
,来创建package.json
,根据传入的参数对中间件进行控制,name字段使用用户输入的项目名称。
// bin/createPackageTemplate.js
import ejs from 'ejs';
import fs from 'fs';
import prettier from 'prettier';
import path from 'path';
import { fileURLToPath } from 'url';
export default config => {
const __dirname = fileURLToPath(import.meta.url);
const indexTemplate = fs.readFileSync(path.resolve(__dirname, '../template/package.ejs'));
const code = ejs.render(indexTemplate.toString(), {
packageName: config.packageName,
middleware: config.middleware,
});
return prettier.format(code, { parser: 'json' });
};
// bin/template/package.ejs
{
"name": "<%= packageName %>",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ICS",
"dependencies": {
"koa": "^2.13.1"
<% if (middleware.router) { %>
,"koa-router": "^10.0.0"
<% } %>
<% if (middleware.static) { %>
,"koa-static": "^5.0.0"
<% } %>
}
}
5. 应用
到此,整个项目基本已完成,核心内容就是为了生成index.js
和package.json
,再安装好依赖,一个简单的koa项目就可以生成好了,接下来看一下如何应用:
本地测试:
开发完成后,可以在本地先进行测试,使用npm link
命令将当前项目链接到npm
全局后,即可在命令行窗口中使用全局命令koa-setup
开始创建新的koa项目了。
注意:
-
这里使用的命令
koa-setup
,即为创建koa-setup
项目中,package.json
文件中的name
字段。当然也可以在package.json
文件中,通过bin
字段进行命令指定,"bin":{"mykoa": "./bin/index.js"}
,重新执行npm link
后,即可使用mykoa
命令来创建项目。命令默认为name
字段时,bin
字段仍需指定执行的入口文件,"bin": "./bin/index.js"
。 -
在执行项目创建命令的时候,其实就是在执行
./bin/index.js
文件,正常情况下电脑无法直接js
文件,所以我们必须告诉电脑以什么环境执行js
文件,因此在index.js
首行必须添加#! /usr/bin/env node
,用来告诉电脑需要在node环境中去执行js
文件。 -
npm unlink moduleName
,通过此命令可以解除项目到npm
全局的链接。
npm包发布:
在本地测试无误后,也可以将最终的包发布到npm
上,别人也可以通过npm
下载并使用我们开发的工具了。
- 首先需要有
npm
的账号,可去npm官网进行注册; - 其次在命令行内使用
npm login
命令,登录自己的npm
账号; - 在
package.json
文件中指定需要发布的文件;
"files": [
"bin",
"package.json"
],
npm
上不允许有同名的包存在,可以在发布前,去npm
网站上,查看是否有同名的包;- 使用
npm publish
命令即可将包发布到npm
官网上。
总结
-
使用
inquirer
实现用户交互; -
根据
inquirer
获取用户的输入,使用ejs
模板,自动创建入口文件index.js
,package.json
; -
使用
execa
插件执行yarn
命令,完成依赖包安装。
项目代码:GitHub
扩展
-
一款交互式的命令行工具,inquirer 会简化询问终端用户问题,解析,验证答案,提供错误反馈等等功能 。通过交互式的方式获取用户的相关信息。
-
一款用于修改控制台中打印信息样式的工具,可以修改字符的颜色等。
-
execa是可以调用shell和本地外部程序的javascript封装。会启动子进程执行。支持多操作系统,包括windows。如果父进程退出,则生成的全部子进程都被杀死。
本文中使用该工具,执行
yarn
命令实现项目依赖的安装。 -
可在npm官网中查看详细内容,本文主要用的了
npm link
,npm publish
等命令。 -
崔大的视频中详解讲解了关于esm模块化相关内容,有兴趣的可以看看。
-
实现
node.js
命令行环境的loading效果,和显示各种状态的图标等。 -
commander 的核心功能是解析命令行参数,例如:命令
npm init -y
中,对于-y
的处理,就可以通过该工具来进行解析。脚手架工具中,通常也会使用类似的操作,来解析命令行中的参数,从而实现更加方便的功能。
以上是在开发一款脚手架工具的过程中,经常会用到的几款工具。使用相关工具,可以简化我们的开发,丰富脚手架的功能,从而比较便捷的输出一款方便、美观的脚手架工具。