likes
comments
collection
share

前端实现一个PPT功能模块脚手架

作者站长头像
站长
· 阅读数 46

需求背景

公司有个使用 javaScript 来编写PPT的需求,通过 pptxgenjs 这个库可以实现,但是有一个问题,那就是当我为A需求编写了一套PPT代码之后,然后此时B需求又需要一个PPT,样式及内容上和A需求大差不差,此时还觉得可以让这两个PPT共用同一份代码,然后通过判断区分一下即可,直到C需求来了,C又跟B大差不差,最后得知,公司还打算搞个D/E/F等等出来。

其实在做B需求的时候,我就发现,这玩意不可以像 vue 组件那样复用,必须把它们相互独立起来,当然,一些小的静态组合还是可以抽离出来做组件的,但是页面必须独立,哪怕样式/逻辑一模一样。

也就是说,假设A需求有一个A1页面,然后这个A1页面在B需求中也是需要的,并且没有任何改动,我们不能直接在B中导入A1,而是要复制一份A1,作为B1,然后在B中导入B1。

后来做到C需求的时候,因为这些PPT页面都挺多的,而我又把一个页面拆分成一个 .js 文件,也就是说,我需要复制十几个文件,然后还要重命名,太麻烦了,可不可以像NextJS那样,在终端输入 nest g controller cats,就可以自动生成对应的目录以及代码文件呢?

因此自己写了个功能模块脚手架 bluewhale-ppt-cli 来实现这个需求。

实现过程

一、初始化项目

新建一个 bluewhale-ppt-cli 目录,然后进入目录,执行 npm init -y,生成 package,json 文件。

打开 package,json 文件,将 author 字段填写完整。

然后,在目录中新建 bin 目录,并且在 bin 目录下新建 index.js 文件,在 index.js 文件写下如下代码

#!/usr/bin/env node
// 注意,上面这行代码必须在开头第一行
require('../src/index.js')

#!/usr/bin/env node 是为了解决不同用户 node 安装路径不同的问题,/usr/bin/env 就是告诉系统可以在PATH目录中查找,让系统动态的去查找 node 来执行脚本文件。

然后,我们要在 package.json 文件中,添加如下代码

"bin":{
    "bw-ppt":"bin/index.js"
}

这里的 bw-ppt 就是我们要在终端输入的自定义命令,当我们输入 bw-ppt 的时候,它就会执行 bin/index.js 中的代码。

前端实现一个PPT功能模块脚手架

此时,项目目录结构如下

前端实现一个PPT功能模块脚手架

根据上面的代码,不难知道,我们还需要创建一个 src 目录以及 src 目录下的 index.js 文件,之所以这样做,是为了将代码分模块,更方便管理,如果你狠一点,可以把全部代码都写在 bin/index.js 里面,创建完成后,我们在 src/index.js 文件中写入一行代码 console.log('hello cli'),保存。

然后在项目根目录打开终端,执行命令 npm link,进行软连接,然后在终端中输入 bw-ppt,回车,出现 hello cli 表示第一步基本完成了。

前端实现一个PPT功能模块脚手架

二、安装依赖

这一步需要使用 npm install 命令进行一些依赖包的安装。

commander

可以自定一些终端命令,当我们在终端中输入自定义命令的时候就会去执行相应的操作。

我们最后希望做到的是,在终端输入 bw-ppt create pptName 的时候,想要代码理解 create pptName 这一部分,就要用到这个包了。

安装命令: npm i commander

chalk

可以美化终端的输出,但是要注意的是,chalk5.0 版本不再支持 require() 导入,即下面的代码会报错

const chalk = require('chalk')

前端实现一个PPT功能模块脚手架

有两种解决方法,一种是安装 chalk4.0

# 先卸载当前版本
npm uninstall chalk 
# 安装4.0版本
npm install chalk@4

另一种方法则是修改整个项目的 EMS 规范,在 package.json 文件中,将 type 字段的值指定为 module

{
  "name": "ppt-cli",
  "type":"module"
   ...
}

inquirer

这个包可以让我们在终端中对用户进行询问,并且记录用户选择的结果,用它是因为考虑到,到时候会根据终端输入 pptName 在相应目录下创建同名文件夹,那必然会遇到存在同名文件夹的情况,这个时候就可以使用这个包来询问用户是否需要覆盖这个目录了。

安装命令: npm i inquirer@8

该包8.0后面的版本不再支持require导入

figlet

该包可以在终端中输出logo,用上它主要是为了排面

安装命令: npm i figlet

ora

该包的作用是在终端输出加载动画,后面我们会把模板代码放到代码仓库,然后下载的过程中就需要用到这个加载动画了。

安装命令: npm i ora@5

该包5.0之后的版本不支持require导入

fs-extra

该包是 fs 模块的一个拓展,提供了很多API,继承了 fs 所有方法

安装命令: npm i fs-extra

download-git-repo

该包的作用是下载远程模板,就是从仓库中拉取代码的意思

安装命令: npm i download-git-repo

命令合集

npm install commander chalk@4 inquirer@8 figlet ora@5 fs-extra download-git-repo

三、代码实现

前面的准备工作完成之后,就开始具体实现了,首先是 src/index.js 文件中的代码

index.js

const { program } = require('commander');
const chalk = require('chalk')
const figlet = require('figlet')
​
program
    .command('create <name>')
    .description('创建一个新的PPT')
    .action((PPTName) => {
        console.log('\r\n' + chalk.white.bgBlueBright.bold(figlet.textSync('bluewhale-ppt', {
            font: 'Standard',
            horizontalLayout: 'full',
            verticalLayout: 'default',
            width: 180,
            whitespaceBreak: true,
        })) + '\r\n');
    })
​
program.parse();

上面的代码实现的功能就是,当我们在终端中输入 bw-ppt create pptName 的时候,控制台出现一个大大的logo,如下

前端实现一个PPT功能模块脚手架

此处没什么实际作用,主要是好看。

然后我们在 index.js 的同级新建一个 create.js 文件,这个文件默认会导出一个函数,因此我们在 index.js 中引入它,如下

const create = require('./create.js')
​
program
    .command('create <name>')
    .description('创建一个新的PPT')
    .action((PPTName) => {
        // ....
        create(PPTName);
    })

到这里,就没有 index.js 这个文件的事情了,我们来到 crate,js 文件

create.js

这个文件默认导出一个匿名函数,函数刚开始执行的时候,会调用 isInstallPptxGenJS 函数来检查用户是否安装了 pptxgenjs 这个依赖库,如果没有安装,则提醒用户安装,然后退出执行。

如果用户安装了所需要的依赖库,就继续判断是否存在同名目录,存在的话则询问用户是否覆盖,不存在的话则直接拉取代码。

const path = require('path')
const fse = require('fs-extra')
const inquirer = require('inquirer')
const chalk = require('chalk')
const download = require('./download.js')
​
module.exports = function (name) {
    // 判断是否已经安装pptxgenjs库
    if (isInstallPptxGenJS() !== 'installed') {
        // 未安装时执行的逻辑
        return
    }
    // 当前命令行执行的目录
    const cwd = process.cwd();
    // 需要创建的目录
    const targetPath = path.join(cwd, '/src/blueWhalePPT', name)
    // 目录是否存在
    if (fse.existsSync(targetPath)) {
        inquirer.prompt([{
            name: 'action',
            type: 'list',
            message: '目录已存在,请选择:',
            choices: [
                { name: '覆盖', value: 'overwrite' },
                { name: '取消', value: false }
            ]
        }]).then(({ action }) => {
            // 用户选择了取消-即不覆盖,所以终止运行
            if (!action) return
            // 用户选择了覆盖
            if (action === 'overwrite') {
                // 移除已经存在的目录
                fse.remove(targetPath).then(() => {
                    download(targetPath)
                })
            }
        })
    } else {
        download(targetPath)
    }
    return
}

通过上面的代码,我们可以知道,我们还需要实现一个 isInstallPptxGenJS 函数以及在同级目录下新增一个 download.js 文件,这样 create.js 文件才能正常的执行。

isInstallPptxGenJS
/**
 * 判断当前用户是否已经安装了PptxGenJS库
 * 未安装则询问是否安装,不安装则退出
 * 已安装则输出版本号
 */
function isInstallPptxGenJS() {
    const cwd = process.cwd();
    const packageDir = cwd + '/package.json'
    if (fse.existsSync(packageDir)) {
        const { dependencies } = require(packageDir)
        // 判断是否安装PptxGenJS
        if ('pptxgenjs' in dependencies) {
            const version = dependencies['pptxgenjs']
            console.log(chalk.green.bold(`已安装pptxgenjs:${version}`))
            return 'installed'
        } else {
            const str1 = `未安装pptxgenjs`
            const str2 = `请执行 ${chalk.cyan(`npm install pptxgenjs --save`)} or ${chalk.cyan(`yarn add pptxgenjs`)} 进行安装`
            const str3 = `更多详情请查阅PptxGenJS官方文档:https://gitbrent.github.io/PptxGenJS/`
            console.log(chalk.redBright.bold(`${str1}\r\n${str2}\r\n${str3}`))
            return `nonexistence pptxgenjs` // 未安装pptxgenjs库
        }
    } else {
        const str1 = `未在 ${chalk.green.bold(cwd)} 目录下找到package.json文件`
        const str2 = `[] 请检查执行路径是否正确`
        const str3 = `[] 请执行 ${chalk.cyan(`npm init`)} 命令生成package.json文件`
        console.log(chalk.redBright.bold(`${str1}\r\n${str2}\r\n${str3}`))
        return 'package.json not found' // 未找到package.json文件
    }
}

上面这个函数,会先检查用户当前执行命令的路径下是否存在 package.json 文件,存在导入 package.json 文件,然后判断里面的 dependencies 字段中是否存在 pptxgenjs 这个字段,存在则输入版本号,不存在则提醒用户安装。

前端实现一个PPT功能模块脚手架

download.js

这个文件默认导出一个匿名函数,用于从远端仓库中下载模板。

const download = require("download-git-repo")
const ora = require("ora")
​
module.exports = function (targetDir) {
    const spinner = ora('远端仓库开始下载...')
    spinner.start()
    // 下载远端模板
    const repoUrl = 'direct:https://gitee.com/XXX/YYY.git'
    const downloadConfig = {
        clone: true
    }
    download(repoUrl, targetDir, downloadConfig, (err) => {
        if (err) {
            spinner.fail('[ 远端仓库下载失败 ]')
            console.log(err)
        } else {
            spinner.succeed('[ 远端仓库下载成功 ]')
        }
​
    })
}

我这里选择的是把模板文件存在码云上,可以根据实际需求更改拉取模板的仓库地址 repoUrl 即可。

前端实现一个PPT功能模块脚手架

上面是模板代码的目录结构。

四、本地测试

在终端中输入命令 bw-ppt create myPPT,回车执行

前端实现一个PPT功能模块脚手架

然后可以看到,在 src 目录下多出了一个 bluewhalePPT 目录,然后该目录下还有一个 myPPT 目录,且里面的文件跟我们上面在码云中看到的一模一样,说明我们这个功能脚手架是可以的了。

五、发布到npm

接下来我们要把这个东西发布到 npm 上去,这样别人执行 npm i bluewhale-ppt-cli 后,在终端输入 bw-ppt create <PPTName> 就可以达到同样的效果了。

首先,终端执行 npm login,输入用户名,密码,登录到 npm

然后登录成功后,输入 npm publish,执行发布操作

前端实现一个PPT功能模块脚手架

看到 + 包名@版本号 出现,说明发布成功。正常来说,还会收到邮件的通知。

前端实现一个PPT功能模块脚手架

发布的时候要注意

  1. 出现问题直接百度
  2. 每次修改代码之后记得修改版本号,然后重新发布 npm publish

六、项目实测

这次新建一个项目,来测试一下这个脚手架能不能用

过程跟简单,新建一个目录,执行 npm init -y ,然后执行 npm i bluewhale-ppt-cli -Dnpm i pptxgenjs,然后在终端输入 bw-ppt create myPPT

前端实现一个PPT功能模块脚手架

很棒,完成。

后续计划

上面只是简单的实现了这个功能脚手架的基本功能,后续应该要加上模板选择,然后还有文件的名称应该跟终端输入的一样,然后还有 common.js 这种文件是每个PPT公用的文件,要想办法提取出来,而不是每个目录创建一个,然后还有代码内容也要控制起来。

反正就是给这个脚手架加入更多的参数和更多的功能。

转载自:https://juejin.cn/post/7212164132795039800
评论
请登录