S09-06 Webpack5-开发脚手架
[TOC]
开发脚手架
知识点
commander
依赖包: commander :完整的 node.js 命令行解决方案。
- 安装:
pnpm i commander
API:
cmd.commad():
(command, description?, options?)
,用于定义命令的一个方法。它允许你添加子命令及其选项,以便创建功能丰富的命令行工具。command:
string
,表示命令的名称及其参数。例如,'add <num1> <num2>'
。description?:
string
,描述命令的功能。通常用于帮助信息。options?:
object
,指定命令的配置选项,如是否允许未知选项等。- js
program .command('greet <name>') .description('Greet a person') .action((name) => { console.log(`Hello, ${name}!`); }); // $ coderwhy greet xxx
cmd.option():
(flags, description, defaultValue?, callback?)
,用于定义命令行选项的方法。选项是以--option
或-o
的形式传递给命令行工具的参数,允许用户在运行命令时修改其行为或设置。flags:
string
,选项的标志,通常以--
开头(例如--verbose
)。可以包含短选项(例如-v
)和长选项(例如--verbose
),用逗号分隔。description:
string
,对选项的描述,用于生成帮助信息。defaultValue:
any
,选项的默认值(可选)。如果用户未提供该选项,则使用此默认值。callback:
() => void
,可选的转换函数,用于处理选项值(例如将字符串转换为布尔值)。- js
program.option('--count <number>', 'Number of items', 5, (val) => parseInt(val, 10)); program.parse(process.argv); const options = program.opts(); console.log(`Count: ${options.count}`); // $ coderwhy --count 10
cmd.opts():
()
,用于获取已解析的选项的值的方法。在定义命令行选项之后,你可以使用program.opts()
来访问这些选项的值。返回:
options:
object
,包含所有已解析选项的键值对。键是选项的名称,值是用户提供的选项值。- js
program .option('-p, --port <port>', 'Port number', 3000) .option('-d, --debug', 'Enable debugging'); program.parse(process.argv); const options = program.opts(); console.log(`Port: ${options.port}`); console.log(`Debug mode: ${options.debug}`); // $ coderwhy --port 8000 --debug
cmd.description():
(description)
,用于为命令或整个程序设置描述的方法。用于说明命令或程序的功能和用途。description:
string
,用于描述命令或程序的功能。这是对用户的说明信息,通常显示在帮助文档中。- js
program .version('1.0.0') .description('A simple CLI tool to manage tasks'); // $ coderwhy --version
cmd.action():
callback
,用于定义命令执行逻辑的方法。它用于指定在命令被触发时应执行的操作。该方法允许你将函数与命令绑定起来,以便在用户调用特定命令时执行自定义逻辑。callback:
(...args, options?) => void
,指定在命令被触发时应执行的操作。- args:
any
,在action
函数中,这些参数对应于命令中的位置参数(例如<num1> <num2>
)。 - options?:
object
,如果命令包含选项,它们会在args
数组的末尾。
- args:
- js
program .command('greet <name>') .option('-p, --port <port>', 'port to listen on', '3000') .description('Greet a person') .action((name, options) => { console.log(`Hello, ${name}!,port is ${options.opts().port}`); }); // $ coderwhy greet xxx
cmd.parse():
(args?, options?)
,用于解析命令行输入的核心方法。它处理命令行参数、选项和命令,并根据这些输入执行相应的操作。args?:
array
,默认:process.argv
,表示命令行参数数组。默认情况下,如果未提供该参数,commander
会使用process.argv
,即 Node.js 进程的命令行参数。options?:
{from?, allowUnknownOption?, unknownOption?}
,用于配置解析行为(比如是否显示错误信息)。- from?:
,指定要解析的起始位置,通常用于处理传递给
program.parse()
的参数数组。 - allowUnknownOption?:
boolean
,默认:false
,是否允许未知的选项。如果为true
,commander
将忽略未知选项而不会抛出错误。 - unknownOption?:
(option) => void
,当检测到未知选项时的处理函数。
- from?:
- js
program.parse(process.argv, { allowUnknownOption: true, unknownOption: (option) => { console.error(`Unknown option: ${option}`); } });
cmd.version():
(version, flags?, description?)
,用于设置和获取 CLI 工具的版本号。version:
string
,表示工具的版本号(如'1.0.0'
)。flags?:
string
,指定用于显示版本号的选项(如'-v, --version'
)。description?:
string
,用于为版本选项提供描述。- js
program.version('2.3.4', '-v, --version', 'Show version information'); // $ coderwhy --version
cmd.on():
(event, listener)
,用于注册事件监听器,处理命令行解析过程中的事件。event:
string
,事件名称。常见的事件有:command:*
:触发在每个命令执行之前。可以用来捕获所有命令的执行前事件。command:<commandName>
:触发特定命令执行前的事件。例如,'command:serve'
会在serve
命令执行之前触发。beforeExit
:在程序退出之前触发,可以用于执行清理任务。--help
:用于监听用户请求帮助信息时的事件,执行自定义的逻辑或输出额外的信息。
listener:
(command?) => void
,事件发生时调用的回调函数。- js
const { Command } = require('commander'); const program = new Command(); program .command('deploy') .description('Deploy the application') .action(() => { console.log('Application deployed'); }); // 1. 每次执行任何命令时,都会触发 'command:*' 事件,并输出所执行的命令。 program.on('command:*', () => { console.log('Deploy command is about to be executed'); }); // 2. 每次执行 deploy 命令时,都会触发 'command:deploy' 事件,并输出特定消息。 program.on('command:deploy', () => { console.log('Deploy command is about to be executed'); }); // 3. 当程序即将退出时,会触发 'beforeExit' 事件,并输出退出前的消息。 program.on('beforeExit', () => { console.log('Program is about to exit'); }); program.parse(process.argv);
download-git-repo
依赖包:
- download-git-repo:用于从 Git 仓库中下载项目或文件。只下载指定的版本或分支,而不涉及 Git 版本控制系统本身的操作。
- 安装:
pnpm i download-git-repo
- 安装:
API:
downloadGitRepo():
(repo, dest, options?, callback?)
,用于从 Git 仓库中下载项目或文件。repo:
string
,要下载的 Git 仓库的 URL。可以是 GitHub、GitLab、Bitbucket 等 Git 服务器的仓库地址。- 注意: 地址上需要添加
direct:
和#main
- 注意: 地址上需要添加
dest:
string
,下载到的本地路径。options?:
DownloadOptions | Function
,额外的选项或一个回调函数。如果提供了回调函数,options
可以省略。- clone:
boolean
,默认:false
,如果需要克隆而不是下载
- clone:
callback?:
(err) => void
,下载完成后的回调函数。- js
const downloadGitRepo = require('download-git-repo'); // 指定分支或标签 const repo = 'direct:github:user/repo#branch'; // 或 'direct:github:user/repo#tag' const destination = 'path/to/destination'; // 克隆仓库而不是仅下载 downloadGitRepo(repo, destination, {clone: true}, (err) => { if (err) { console.error('Download failed:', err); } else { console.log('Branch or tag downloaded successfully!'); } });
EJS
EJS (Embedded JavaScript) 是一种用于生成动态 HTML 内容的模板引擎。它允许在 HTML 模板中嵌入 JavaScript 代码,以实现动态内容的渲染。
语法
输出变量
<%= variable %>
:输出变量的值。默认将输出转义为 HTML 实体,防止 XSS 攻击。<%- variable %>
:输出变量的值。不进行转义,直接输出原始 HTML。执行JS代码
<% code %>
:执行 JavaScript 代码,但不输出。条件语句
<% if (condition) { %>
,...
,<% } %>
:执行条件逻辑。循环语句
<% array.forEach(function(item) { %>
,...
,<% }) %>
:遍历数组。包含文件
<%- include('filename') %>
:包含其他 EJS 模板文件。
示例:
header.ejs
html<header> <h1>My Website</h1> </header>
index.ejs
html<!DOCTYPE html> <html> + <%- include('header') %> <body> <h1>Hello, <%= name %>!</h1> <% if (isAdmin) { %> <p>You have admin access.</p> <% } %> <ul> <% items.forEach(function(item) { %> <li><%= item %></li> <% }); %> </ul> </body> </html>
API
依赖包:
- ejs:EJS语法的扩展API
- 安装:
pnpm i ejs
- 安装:
语法:
ejs.compileFile():
(filename, options)
,用于编译一个 EJS 模板文件为一个可重用的模板函数。该函数可以在多次调用时渲染模板。filename:
string
,模板文件的路径。options:
object
,编译选项对象,用于配置模板的编译行为。- filename?:
string
,用于设置include
的查找路径。通常与模板文件的路径相同。 - cache?:
boolean
,默认:
,是否启用缓存。 - delimiter?:
string
,默认:%
,定义<% %>
中的分隔符。 - escape?:
boolean
,默认:true
,是否对输出进行 HTML 转义。 - views?:
string
,视图路径,用于include
的查找。
- filename?:
返回:
compiledFunction:
(obj) => void
,返回一个函数,调用这个函数时会将传递的数据渲染到模板中。函数接受一个数据对象作为参数,并返回渲染后的 HTML 字符串。- js
const ejs = require('ejs'); // 编译模板文件 const compiledFunction = ejs.compileFile('template.ejs', { cache: true, delimiter: '%', escape: false }); // 使用编译后的模板函数渲染数据 const html = compiledFunction({ name: 'World' }); console.log(html); // 输出渲染后的 HTML
ejs.renderFile():
(filename, data, options?, callback)
,用于从文件渲染 EJS 模板,并返回结果。filename:
string
,模板文件的路径。data:
object
,传递给模板的数据对象。options?:
object
,渲染选项对象,用于配置模板的渲染行为。- filename?:
string
,用于设置include
的查找路径。通常与模板文件的路径相同。 - cache?:
boolean
,默认:
,是否启用缓存。 - delimiter?:
string
,默认:%
,定义<% %>
中的分隔符。 - escape?:
boolean
,默认:true
,是否对输出进行 HTML 转义。 - views?:
string
,视图路径,用于include
的查找。
- filename?:
callback:
(err, str) => void
,渲染完成后的回调函数。- js
const ejs = require('ejs'); // 渲染模板文件 ejs.renderFile('template.ejs', { name: 'World' }, { cache: true, delimiter: '%', escape: false }, (err, str) => { if (err) { console.error(err); return; } console.log(str); // 输出渲染后的 HTML });
ejs.compile():
(templateString, options?)
,用于将模板字符串编译成一个可执行的函数。这个函数可以在运行时接受数据并渲染最终的 HTML。templateString:
string
,要编译的 EJS 模板字符串。options?:
object
,配置对象,包含以下属性:- filename?:
string
,模板文件的路径。如果指定,错误信息中将包含文件路径。 - cache?:
boolean
,默认:
,是否启用缓存。 - delimiter?:
string
,默认:%
,定义<% %>
中的分隔符。 - escape?:
boolean
,默认:true
,是否对输出进行 HTML 转义。 - views?:
string
,视图路径,用于include
的查找。
- filename?:
返回:
compiledFunction:
object
,是一个接受数据对象并返回渲染后的 HTML 字符串的函数。jsconst ejs = require('ejs'); // 模板字符串 const templateString = 'Hello, <%= name %>!'; // 编译模板 const compiledFunction = ejs.compile(templateString, { filename: 'template.ejs', cache: true, delimiter: '%', escape: false }); // 渲染模板 const html = compiledFunction({ name: 'World' }); console.log(html); // 输出: Hello, World!
webpack
API:
require.context():
(directory, useSubdirectories, regExp)
,是 Webpack 特有的一个函数,用于动态加载模块。它在构建时会创建一个上下文,帮助你动态地导入文件。directory:
string
,指定要搜索的目录路径,相对路径。useSubdirectories:
boolean
,是否搜索子目录。regExp:
reg
,只加载与正则表达式匹配的文件。返回:
context:
(item) => Module
,返回一个上下文函数,用于动态加载模块。- js
const context = require.context('./services', false, /\.js$/); const services = context.keys().reduce((acc, key) => { const serviceName = key.replace(/^.*[\\/]/, '').replace(/\.js$/, ''); acc[serviceName] = context(key); // 动态加载模块 return acc; }, {}); console.log(services); // {SomeService: './services/SomeService.js', ...}
context.keys():
()
,用于获取匹配的模块路径数组。context:
,是通过
require.context()
创建的上下文对象。返回:
keys:
string[]
,返回包含了所有与require.context()
创建的上下文对象匹配的模块相对路径字符串。- js
// 创建一个上下文来导入 ./src 目录下所有以 .js 结尾的文件 const context = require.context('./src', false, /\.js$/); // 获取所有匹配的文件路径 const keys = context.keys(); console.log(keys); // 输出:['./file1.js', './file2.js', './subdir/file3.js']
context():
(path)
,是require.context()
返回的一个函数,用于动态加载模块。path:
string
,模块的路径,相对于上下文的目录。路径必须匹配在require.context()
时指定的正则表达式。返回:
module:
Module
,返回值是匹配路径的模块对象。这允许你在运行时动态地导入模块,而不是在编译时静态地导入它们。- js
const context = require.context('./modules', false, /\.js$/); const moduleA = context('./moduleA.js'); // 加载 ./modules/moduleA.js console.log(moduleA);
说明文档
目前前端工程化开发过程中,我们会使用各种各样的脚手架,vue-cli、create-react-app,当然也包括 webpack、gulp、rollup、vite 等工具。
这些工具是怎么开发出来的呢?当我们执行一个命令时,它们做了什么事情?是怎么样完成的一系列操作?
这里我开发了一个 coderwhy 的脚手架:一个帮助你快速搭建和开发前端项目的 CLI。
文档内容分成两部分:
第一部分:coderwhy 使用说明;
第二部分:coderwhy 脚手架开发过程;
欢迎下载学习,如果对你有帮助,可以点一个 star~
coderwhy
想不起来其他名字,以这个命名吧~
coderwhy: 一个帮助你快速搭建和开发前端项目的 CLI
如何安装?
npm install coderwhy -g
创建项目
目前支持 Vue,后期会支持 React,Angular 考虑中~
1、功能
vue 项目模块已经帮你配置:
- 常用的目录结构(你可以在此基础上修改)
- vue.config.js(其中配置了别名,你可以自行修改和配置更多)
- axios(网络请求 axios 的安装以及二次封装)
- vue-router(router 的安装和配置,另外有路由的动态加载,后面详细说明)
- vuex(vuex 的安装和配置,另外有动态加载子模块,后面详细说明)
2、使用coderwhy创建项目
coderwhy create <your_project_name>
自动拉取项目模板、安装项目依赖、打开浏览器 http://localhost:8080/
、自动启动项目
项目开发
项目开发目前提供三个功能:
- 创建 Vue 组件
- 创建 Vue 页面,并配置路由
- 创建 Vuex 子模块
1、创建 Vue 组件
# 例如coderwhy addcpn NavBar,默认会存放到src/components文件夹中
coderwhy addcpn YourComponentName
# 也可以指定存放的具体文件夹
coderwhy addcpn YourComponentName -d src/pages/home
2、创建 Vue 页面,并配置路由
# 例如coderwhy addpage Home,默认会放到src/pages/home/Home.vue中,并且会创建src/page/home/router.js
coderwhy addpage YourPageName
# 也可以指定文件夹,但需要手动集成路由
coderwhy addpage YourPageName -d src/views
为什么会创建 router.js 文件:
router.js
文件是路由的其中一个配置;- 创建该文件中
src/router/index.js
中会自动加载到路由的routes
配置中,不需要手动配置了(如果是自己配置的文件夹需要手动配置)
src/router/index.js
中已经完成如下操作:
// 动态加载pages中所有的路由文件
const context = require.context('@/pages', true, /router\.js$/)
const routes = context.keys().map((key) => {
const page = require('@/pages' + key.replace('.', ''))
return page.default
})
3、创建 Vuex 子模块
# 例如coderwhy addstore home,默认会放到src/store/modules/home/index.js和types.js
coderwhy addstore YourVuexChildModuleName
# 也可以指定文件夹
coderwhy addstore YourVuexChildModuleName -d src/vuex/modules
创建完成后,不需要手动配置,已经动态将所有子模块集成进去:
// 动态加载modules
const modules = {}
const context = require.context('./', true, /index\.js$/)
context
.keys()
.filter((key) => {
if (key === './index.js') return false
return true
})
.map((key) => {
// 获取名字
const modulePath = key.replace('./modules/', '')
const moduleName = modulePath.replace('/index.js', '')
const module = require(`${key}`)
modules[`${moduleName}`] = module.default
})
开发过程
项目开始
1、初始化项目
pnpm init
2、创建 ./lib/index.js
文件
// ./lib/index.js
console.log('Hello Coderwhy')
创建 package.json
{
"name": "coderwhy",
"version": "1.1.0",
"description": "CLI front-end development tools",
"main": "index.js",
"bin": {
"coderwhy": "index.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": ["vue", "react", "CLI", "component"],
"author": "coderwhy",
"license": "MIT",
"homepage": "https://github.com/coderwhy/coderwhy",
"repository": {
"type": "git",
"url": "https://github.com/coderwhy/coderwhy"
},
"dependencies": {
"chalk": "^4.1.0",
"commander": "^6.1.0",
"download-git-repo": "^3.0.2",
"ejs": "^3.1.5",
"open": "^7.3.0"
}
}
最终的目录结构:
├── LICENSE
├── index.js
├── lib
│ ├── config
│ │ └── repo_config.js
│ ├── core
│ │ ├── actions.js
│ │ ├── create.js
│ │ └── help.js
│ ├── template
│ │ ├── component.vue.ejs
│ │ ├── vue-router.js.ejs
│ │ ├── vue-store.js.ejs
│ │ └── vue-types.js.ejs
│ └── utils
│ ├── file.js
│ ├── log.js
│ └── terminal.js
├── package-lock.json
├── package.json
└── readme.md
创建 coderwhy 的命令
1、创建bin文件bin/vite.js
#!/usr/bin/env node
:这是bash命令,会自动在环境变量中查找node,使用node运行vite.js代码
注意:必须放在第一行
#!/usr/bin/env node
2、在 package.json
中添加bin选项
当在命令行输入coderwhy
就会来到bin/vite.js
文件并执行其中的代码
"bin": {
"coderwhy": "index.js"
}
3、执行 npm link
,建立一个软链接。将coderwhy添加到环境变量中
解析命令行参数
node解析命令行参数
在node中内置了对命令行参数的解析:
1、在命令行输入命令并携带参数
coderwhy --version
2、在node代码中可以通过process.argv
获取到参数
commander解析命令行参数
依赖包: commander :完整的 node.js 命令行解决方案。
- 安装:
pnpm i commander
一、定义版本号
#!/usr/bin/env node
//
const { cmd } = require('commander') // 推荐该方式导入,vscode提示会更好
// const cmd = require('commander') // 此处2种方式都可以导入cmd
// 定义显示模块的版本号
cmd.version(require('./package.json').version, '-v --version')
// 解析终端指令
cmd.parse(process.argv)
二、给help增加其他选项
1、添加单个选项
program.option('-s --src <src>', 'a source folder')
program.option('-d --dest <dest>', 'a destination folder')
program.option('-f --framework <framework>', 'your framework name')
2、自定义 help 指令,添加额外的帮助信息
program.on('--help', function () {
console.log('')
console.log('Usage:')
console.log(' coderwhy -v')
console.log(' coderwhy --version')
})
3、封装option选项
封装
导入并使用
命令行使用
创建项目指令create
依赖包:
- download-git-repo:用于从 Git 仓库中下载项目或文件。只下载指定的版本或分支,而不涉及 Git 版本控制系统本身的操作。
- 安装:
pnpm i download-git-repo
创建项目:
1、在命令行使用create
命令
$ coderwhy create airbnb
2、创建命令
// 创建命令
program
.command('create <project> [otherArgs...]')
.description('clone a repository into a newly created directory')
+ .action(createProject)
3、在 actions 中封装创建过程:
- promisify():用于将遵循回调模式的异步函数转换为返回
Promise
的函数。
const downloadRepoAsync = promisify(require('download-git-repo'))
const createProject = async (project, otherArg) => {
// 1.提示信息
console.log('coderwhy helps you create your project, please wait a moment~')
// 2.从仓库clone项目
await downloadRepoAsync(repoConfig.vueGitRepo, project, { clone: true })
// 3.执行终端命令npm install
// terminal.exec('npm install', {cwd: `./${project}`});
const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'
await terminal.spawn(npm, ['install'], { cwd: `./${project}` })
// 4.打开浏览器
open('http://localhost:8080/')
// 5.运行项目
await terminal.spawn(npm, ['run', 'serve'], { cwd: `./${project}` })
}
4、配置的 Git 地址如下:
- 后续会开发一个设置自己地址的指令
// 注意:地址上需要添加`direct:`和`#main`,否则报错
const vueGitRepo = 'direct:https://github.com/coderwhy/hy-vue-temp.git#main'
module.exports = {
vueGitRepo
}
5、封装执行终端命令的过程:
const { spawn, exec } = require('child_process')
const execCommand = (...cmds) => {
return new Promise((resolve, reject) => {
// 1. 开启子进程执行命令
const childProcess = spawn(...cmds)
// 2. 获取子进程的输出和错误信息
childProcess.stdout.pipe(process.stdout)
childProcess.stderr.pipe(process.stderr)
// 3. 监听子进程执行结束
childProcess.on('close', () => {
resolve()
})
})
}
添加组件指令
创建添加指令
1、添加指令
program
.command('addcpn <name> [...others]')
.description('add vue component, 例如: coderwhy addcpn NavBar -d src/components')
+ .action((name) => addComponent(name, program.opts().dest || 'src/components'))
program
.command('addpage <name> [...others]')
.description('add vue page, 例如: coderwhy addpage Home -d dest')
.action((name) => {
+ addPage(name, program.opts().dest || `src/pages/${name.toLowerCase()}`)
})
program
.command('addstore <name> [...others]')
.description('add vue store, 例如: coderwhy addstore favor[-d dest')
.action((name) => {
+ addStore(name, program.opts().dest || `src/store/modules/${name.toLowerCase()}`)
})
2、封装对应的 action
const addComponent = async (name, dest) => {
+ handleEjsToFile(name, dest, '../template/component.vue.ejs', `${name}.vue`)
}
const addPage = async (name, dest) => {
addComponent(name, dest)
+ handleEjsToFile(name, dest, '../template/vue-router.js.ejs', 'router.js')
}
const addStore = async (name, dest) => {
+ handleEjsToFile(name, dest, '../template/vue-store.js.ejs', 'index.js')
+ handleEjsToFile(name, dest, '../template/vue-types.js.ejs', 'types.js')
}
封装 ejs 模板
依赖插件:
- EJS language support:vscode插件,EJS语法高亮显示。
补充知识: EJS
1、组件模板:component.vue.ejs
<%_ if(data) { _%>
<template>
<div class="<%= data.lowerName %>">
<h2>{{ message }}</h2>
</div>
</template>
<script>
export default {
name: "<%= data.name %>",
components: {
},
mixins: [],
props: {
},
data: function() {
return {
message: "Hello <%= data.name %>"
}
},
created: function() {
},
mounted: function() {
},
computed: {
},
methods: {
}
}
</script>
<style scoped>
.<%= data.lowerName %> {
}
</style>
<%_ } _%>
2、路由模板:component.vue.ejs
- 组件模板,直接使用上面的即可
- router.js 模板
<%_ if (data) { _%>
// 普通加载路由
// import <%= data.name %> from './<%= data.name %>.vue'
// 懒加载路由
const <%= data.name %> = () => import('./<%= data.name %>.vue')
export default {
path: '/<%= data.lowerName %>',
name: '<%= data.name %>',
component: <%= data.name %>,
children: [
]
}
<%_ } _%>
3、vuex 模块的模板:component.vue.ejs
- index.js 模板
- types.js 模板
index.js 模块
import * as types from './types.js'
export default {
namespaced: true,
state: {
},
mutations: {
},
actions: {
},
getters: {
}
}
types.js 模块
export {
}
封装 ejs 解析
依赖包:
- ejs:EJS语法的扩展API
- 安装:
pnpm i ejs
- 安装:
1、封装 ejs 到文件的转化过程:handleEjsToFile()
const handleEjsToFile = async (name, dest, template, filename) => {
// 1.获取模块引擎的路径
const templatePath = path.resolve(__dirname, template)
+ const result = await ejsCompile(templatePath, { name, lowerName: name.toLowerCase() })
// 判断文件不存在,那么就创建文件
+ mkdirSync(dest)
// 写入文件中
const targetPath = path.resolve(dest, filename)
+ await writeFile(targetPath, result)
console.log('创建组件成功:' + name)
}
2、封装 ejs 的编译过程:ejsCompile()
const ejsCompile = (templatePath, data = {}, options = {}) => {
return new Promise((resolve, reject) => {
ejs.renderFile(templatePath, { data }, options, (err, str) => {
if (err) {
reject(err)
return
}
resolve(str)
})
})
}
3、封装创建文件夹的过程:mkdirSync()
const mkdirSync = (dirname) => {
if (fs.existsSync(dirname)) {
return true
} else {
// 不存在,判断父文件夹是否存在?
if (mkdirSync(path.dirname(dirname))) {
// 存在父文件,就直接新建该文件
fs.mkdirSync(dirname)
return true
}
}
}
4、封装写入文件的过程:writeFile()
const writeFile = (path, content) => {
if (fs.existsSync(path)) {
log.error('the file already exists~')
return
}
return fs.promises.writeFile(path, content)
}
发布工具
注册 npm 账号:
- https://www.npmjs.com/
- 选择 sign up
sign up 注册
在命令行登录:
npm login
# 输入账号、密码、邮箱
修改好 package.json 文件:
+ "keywords": [
"vue",
"react",
"CLI",
"component"
],
+ "author": "coderwhy",
+ "license": "MIT",
+ "homepage": "https://github.com/coderwhy/coderwhy",
+ "repository": {
"type": "git",
"url": "https://github.com/coderwhy/coderwhy"
},
发布到 npm registry 中
npm publish
更新 registry
# 1.修改版本号(最好符合semver规范)
# 2.重新发布
删除发布的包:
npm unpublish
过期发布的包:
npm deprecate