likes
comments
collection
share

用 unplugin-starter 写一个系统推送打包结果通知的插件,顺便蹭了个PR等待打包的过程总是漫长又无聊,如果

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

前言

等待打包的过程总是漫长又无聊,如果你也想在打包的时候摸摸鱼又不想一直盯着CLI惦记着打包好了没,那你可以试试unplugin-build-notifier (github.com) 这款跨平台的插件, 它能提供系统推送通知,让你第一时间知道打包结果,在一定程度缓解了等待打包过程的无聊和焦虑,以下是Windows系统的通知推送截图

用 unplugin-starter 写一个系统推送打包结果通知的插件,顺便蹭了个PR等待打包的过程总是漫长又无聊,如果

特点

  • 💚 跨平台的系统通知,由 node-notifier 提供支持
  • ✨ 自动获取项目名称
  • 📂 自动打开文件资源管理器
  • 🔧 可配置的消息和点击行为
  • 🚀 获取构建时间
  • ⚡️ 支持 Vite、Webpack、Rspack、Vue CLI、Rollup、esbuild 等,由 unplugin 提供支持
  • 🦾 完全支持 TypeScript

安装

npm i unplugin-build-notifier -D
yarn add unplugin-build-notifier -D
pnpm add unplugin-build-notifier -D

使用示例

Vite
// vite.config.ts
import BuildNotifier from 'unplugin-build-notifier/vite'

export default defineConfig({
  plugins: [
    BuildNotifier({ /* options */ }),
  ],
})

Example: playground/

Rollup
// rollup.config.js
import BuildNotifier from 'unplugin-build-notifier/rollup'

export default {
  plugins: [
    BuildNotifier({ /* options */ }),
  ],
}

Webpack
// webpack.config.js
module.exports = {
  /* ... */
  plugins: [
    require('unplugin-build-notifier/webpack')({ /* options */ })
  ]
}

Nuxt
// nuxt.config.js
export default defineNuxtConfig({
  modules: [
    ['unplugin-build-notifier/nuxt', { /* options */ }],
  ],
})

This module works for both Nuxt 2 and Nuxt Vite

Vue CLI
// vue.config.js
module.exports = {
  configureWebpack: {
    plugins: [
      require('unplugin-build-notifier/webpack')({ /* options */ }),
    ],
  },
}

esbuild
// esbuild.config.js
import { build } from 'esbuild'
import BuildNotifier from 'unplugin-build-notifier/esbuild'

build({
  plugins: [BuildNotifier()],
})

核心代码

该项目基于unplugin的起始模板 unplugin-starter(github.com) 进行开发,已经开源 github 并发布npm包,核心代码都在src/index.ts中,下面简单介绍一下代码实现

1.路径处理

首先需要获取到项目根路径,用于展示项目名称和项目路径(用于区分同名项目),以及启动该目录下的文件资源管理器。

项目根路径主要通过import.meta.url获取到安装依赖后的路径,然后向上查找node_modules目录来确定。

这里有个小细节是如果使用pnpm安装依赖,那么依赖会被安装在.pnpm目录下,再通过软连接的方式放到node_modules主目录中,所以获取到的实际目录是在.pnpm下,这里可能会有很多层node_modules,我们可以直接返回到.pnpm目录后再向上查找到真正的项目根目录

import { dirname, join, sep } from 'node:path'
import { fileURLToPath } from 'node:url'

// 依赖包当前路径
const __dirname = dirname(fileURLToPath(import.meta.url))
// 项目根路径
const rootPath = findNodeModules(__dirname)

function findNodeModules(dir: string): string {
  const pathArr = dir.split(sep)
  const pnpmIndex = pathArr.lastIndexOf('.pnpm')
  const nodeModuleIndex = pathArr.lastIndexOf('node_modules', pnpmIndex)
  return join(...pathArr.slice(0, nodeModuleIndex))
}

2.跨平台打开项目文件夹

根据不同的操作系统,使用相关命令来打开根目录文件夹

  • 对于 macOS('darwin'),使用 'open'

  • 对于 Windows('win32'),使用 'start'

  • 对于其他平台(主要是 Linux),使用 'xdg-open'

import { exec } from 'node:child_process'
import { platform } from 'node:os'

// 获取当前系统指令
function getOpenCommand() {
  switch (platform()) {
    case 'darwin':
      return 'open'
    case 'win32':
      return 'start'
    default:
      return 'xdg-open'
  }
}

// 打开项目根目录
function startdir() {
  exec(`${getOpenCommand()} "" "${rootPath}"`, (error, stdout, stderr) => {
    if (error) {
      console.error(
        `exec error: ${error}\nstdout: ${stdout}\nstderr: ${stderr}`,
      )
    }
  })
}

3.编写推送通知功能

推送通知功能主要由node-notifier实现,该库本身具有跨平台简单易用的特点所以非常合适,这里我们对Options进行定义和一些默认配置。

types.ts

export interface Options {
  message?: string
  iconPath?: string
  click?: () => void
  timeout?: () => void
}

index.ts

import { basename } from 'node:path'
import notifier from 'node-notifier'
import type { Options } from './types'

function nodeNotifier({
  message = '',
  iconPath = '',
  click = startdir,
  timeout = startdir,
}: Options = {}) {
  notifier.notify({
    title: `✨ ${basename(rootPath)}`,
    message: `${message}\n${rootPath}`,
    icon: iconPath || join(__dirname, './SpongeBob.jpg'),
  })
  notifier.on('click', click)

  notifier.on('timeout', timeout)
}

tsup.config.ts

引入图片目录需要在tsup.config中声明,否则不会打包进去,本示例将图片放置于/src/public

export default <Options>{
  publicDir: './src/public',
}

4.插件定义

这里展示的消息会默认显示打包时长,当然,你可以自定义你的消息和交互行为

export const unpluginFactory: UnpluginFactory<Options | undefined> = (
  options,
) => {
  let buildStart: number

  return {
    name: 'unplugin-build-notifier',
    buildStart() {
      buildStart = performance.now()
    },
    writeBundle() {
      const buildTime = ((performance.now() - buildStart) / 1000).toFixed(2)
      nodeNotifier({
        message: `🎉 build success!\n🚀 built in ${buildTime}s`,
        ...options,
      })
    },
  }
}

5.打包测试(蹭PR)

直接 pnpm build 打包后在playground中引入测试即可,测试完vite项目后以为一切顺利,可是当测试vue-cli也就是webpack构建的cjs项目时出现以下报错,也可以直接查看Pull Request #28(github.com)的相关描述

const { defineConfig } = require('@vue/cli-service')
const Unplugin = require('../../dist/webpack.cjs')

console.log({ Unplugin })
module.exports = defineConfig({
  transpileDependencies: true,
  configureWebpack: {
    plugins: [
      Unplugin(),
    ],
  },
})

用 unplugin-starter 写一个系统推送打包结果通知的插件,顺便蹭了个PR等待打包的过程总是漫长又无聊,如果

看了console.log和ERROR后,发现我导入的Unplugin是一个具有default属性的对象而不是一个函数,于是我就去看了打包产物webpack.cjs

var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};

var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

var webpack_exports = {};
__export(webpack_exports, {
  default: () => webpack_default
});
module.exports = __toCommonJS(webpack_exports);
...
var webpack_default = (0, import_unplugin2.createWebpackPlugin)(unpluginFactory);

可以看到webpack_default是我需要默认导出的函数,而这里的默认导出实际就等于module.exports = { default: () => webpack_default },所以我需要require('../../dist/webpack.cjs').default才能拿到我正在的Unplugin函数,可这不是我想要的,于是就去tsup的github中翻到这条issues#572(github.com),解决方案就是在tsup.config中添加以下配置,于是就顺便的蹭了unplugin-starter 一个 PR

export default <Options>{ 
+ cjsInterop: true,
+ splitting: true,
  ...,
}
  1. 发布npm包

直接使用 unplugin-starter 默认的指令 pnpm release 即可自动发布更新npm包,该项目集成了CI且使用bumpp进行交互式的自动版本号更新,生成git提交等,体验相当好

"release": "bumpp && npm publish"

总结

unplugin-build-notifier (github.com)是一个增加开发体验的简单插件,能够通过系统推送打包结果通知,在一定程度缓解了等待打包过程的无聊和焦虑,如果对你有帮助的话,麻烦点个赞或stars吧~

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