chore: build utools plugin (#1037)

This commit is contained in:
Libin YANG
2025-10-01 12:25:50 +08:00
committed by GitHub
parent 06ea65f500
commit f6efebb48e
11 changed files with 251 additions and 45 deletions
+4
View File
@@ -52,3 +52,7 @@ web-ext.config.ts
# vite-plugin-pwa dev output
dev-dist
# uTools build artifacts
apps/utools/dist
apps/utools/release
+3
View File
@@ -121,6 +121,9 @@ pnpm web ext:zip
# Firefox 扩展打包(how to build Firefox addon)
pnpm web firefox:zip # output zip file at in .output/md-{version}-firefox.zip
# uTools 插件打包
pnpm utools:package # output zip file at apps/utools/release/md-utools-v{version}.zip
```
## 🚀 快速搭建私有服务
+31
View File
@@ -0,0 +1,31 @@
# uTools 插件打包指引
该目录包含将微信 Markdown 编辑器打包为 [uTools](https://u.tools) 插件所需的脚本与配置。
## 快速开始
```sh
pnpm utools:package
```
该命令将完成以下动作:
1. 调用 `pnpm --filter @md/web run build:utools` 构建前端资源至 `apps/utools/dist`,该构建将自动使用相对路径,确保在 uTools 的 `file://` 协议下能够正常加载。
2. 将仓库根目录的版本号写入 `apps/utools/plugin.json`,保持与主项目同步。
3.`public/mpmd/icon-256.png` 拷贝插件图标至 `apps/utools/logo.png`
4. 生成形如 `apps/utools/release/md-utools-vX.Y.Z.zip` 的安装包,可直接导入到 uTools。
> 注意:命令执行前请确认已安装 pnpm 8+ 与 Node.js 20+,并在仓库根目录执行 `pnpm install` 安装依赖。
## 手动导入调试
1. 运行 `pnpm --filter @md/web run build:utools`
2. 打开 uTools,进入插件面板中的「开发者工具」。
3. 选择「载入本地插件」,指向 `apps/utools` 目录即可。
## 目录说明
- `plugin.json`uTools 插件的清单文件。
- `preload.js`:在 uTools 渲染进程和 Web 前端之间建立通信的脚本,用于处理插件唤起事件。
- `dist/`:由 Vite 构建输出的静态资源目录。
- `release/`:运行打包命令后生成的插件安装包。
Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB

+27
View File
@@ -0,0 +1,27 @@
{
"pluginName": "微信 Markdown 编辑器",
"description": "Markdown 文档自动排版为微信图文,随时在 uTools 中打开使用",
"version": "2.0.4",
"author": "doocs",
"homepage": "https://github.com/doocs/md",
"pluginType": "tool",
"platform": [
"win32",
"darwin",
"linux"
],
"logo": "logo.png",
"main": "dist/index.html",
"preload": "preload.js",
"features": [
{
"code": "wechat-md",
"explain": "打开微信 Markdown 编辑器",
"cmds": [
"微信排版",
"Markdown 编辑器",
"doocs md"
]
}
]
}
+36
View File
@@ -0,0 +1,36 @@
(() => {
if (typeof window === `undefined`)
return
window.__MD_UTOOLS__ = true
const enter = (action) => {
try {
window.utools?.hideMainWindowWhenBlur(false)
window.utools?.showMainWindow()
window.utools?.setExpendHeight(680)
}
catch (error) {
console.warn(`[md][utools] enter failed`, error)
}
window.postMessage({ type: `utools:enter`, payload: action })
}
const leave = () => {
window.postMessage({ type: `utools:leave` })
}
window.utools?.onPluginEnter(enter)
window.utools?.onPluginOut(leave)
window.exports = {
'wechat-md': {
mode: `none`,
args: {
enter,
leave,
},
},
}
})()
+1
View File
@@ -9,6 +9,7 @@
"build:only": "vite build",
"build:h5-netlify": "run-p type-check \"build:h5-netlify:only {@}\" --",
"build:h5-netlify:only": "cross-env SERVER_ENV=NETLIFY vite build",
"build:utools": "cross-env SERVER_ENV=UTOOLS vite build --outDir ../utools/dist --emptyOutDir",
"build:cli": "pnpm run build && npx shx rm -rf packages/md-cli/dist && npx shx rm -rf dist/**/*.map && npx shx cp -r dist packages/md-cli/ && cd packages/md-cli && npm pack",
"build:analyze": "cross-env ANALYZE=true vite build",
"compile:extension": "pnpm --prefix ./src/extension run compile",
+44 -34
View File
@@ -12,7 +12,13 @@ import { VitePWA } from 'vite-plugin-pwa'
import { VitePluginRadar } from 'vite-plugin-radar'
import vueDevTools from 'vite-plugin-vue-devtools'
const base = process.env.SERVER_ENV === `NETLIFY` ? `/` : `/md/`
const isUTools = process.env.SERVER_ENV === `UTOOLS`
const base
= process.env.SERVER_ENV === `NETLIFY`
? `/`
: isUTools
? `./`
: `/md/`
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
@@ -27,39 +33,43 @@ export default defineConfig(({ mode }) => {
vueDevTools({
launchEditor: env.VITE_LAUNCH_EDITOR ?? `code`,
}),
VitePWA({
registerType: `autoUpdate`,
includeAssets: [`favicon.ico`],
manifest: {
name: `@doocs-md`,
short_name: `@doocs-md`,
theme_color: `#ffffff`,
icons: [
{
src: `${base}pwa-192x192.png`,
sizes: `192x192`,
type: `image/png`,
},
{
src: `${base}pwa-512x512.png`,
sizes: `512x512`,
type: `image/png`,
},
{
src: `${base}pwa-512x512.png`,
sizes: `512x512`,
type: `image/png`,
purpose: `any maskable`,
},
],
},
workbox: {
maximumFileSizeToCacheInBytes: 6 * 1024 * 1024,
},
devOptions: {
enabled: true,
},
}),
...(!isUTools
? [
VitePWA({
registerType: `autoUpdate`,
includeAssets: [`favicon.ico`],
manifest: {
name: `@doocs-md`,
short_name: `@doocs-md`,
theme_color: `#ffffff`,
icons: [
{
src: `${base}pwa-192x192.png`,
sizes: `192x192`,
type: `image/png`,
},
{
src: `${base}pwa-512x512.png`,
sizes: `512x512`,
type: `image/png`,
},
{
src: `${base}pwa-512x512.png`,
sizes: `512x512`,
type: `image/png`,
purpose: `any maskable`,
},
],
},
workbox: {
maximumFileSizeToCacheInBytes: 6 * 1024 * 1024,
},
devOptions: {
enabled: true,
},
}),
]
: []),
nodePolyfills({
include: [`path`, `util`, `timers`, `stream`, `fs`],
overrides: {
+3 -1
View File
@@ -21,11 +21,13 @@
"lint": "eslint . --fix",
"type-check": "vue-tsc --build --force",
"postinstall": "simple-git-hooks",
"inspector": "pnpx node-modules-inspector"
"inspector": "pnpx node-modules-inspector",
"utools:package": "node ./scripts/package-utools.mjs"
},
"devDependencies": {
"@antfu/eslint-config": "5.4.1",
"@types/node": "^24.6.0",
"archiver": "^7.0.1",
"cross-env": "^10.1.0",
"eslint": "^9.36.0",
"eslint-plugin-format": "^1.0.2",
+7 -10
View File
@@ -14,6 +14,9 @@ importers:
'@types/node':
specifier: ^24.6.0
version: 24.6.0
archiver:
specifier: ^7.0.1
version: 7.0.1
cross-env:
specifier: ^10.1.0
version: 10.1.0
@@ -6928,10 +6931,6 @@ packages:
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
lru-cache@11.2.1:
resolution: {integrity: sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==}
engines: {node: 20 || >=22}
lru-cache@11.2.2:
resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==}
engines: {node: 20 || >=22}
@@ -10145,7 +10144,7 @@ snapshots:
bidi-js: 1.0.3
css-tree: 3.1.0
is-potential-custom-element-name: 1.0.1
lru-cache: 11.2.1
lru-cache: 11.2.2
'@asamuzakjp/nwsapi@2.3.9': {}
@@ -17580,15 +17579,13 @@ snapshots:
ansi-escapes: 7.0.0
cli-cursor: 5.0.0
slice-ansi: 7.1.0
strip-ansi: 7.1.0
strip-ansi: 7.1.2
wrap-ansi: 9.0.0
longest-streak@3.1.0: {}
lru-cache@10.4.3: {}
lru-cache@11.2.1: {}
lru-cache@11.2.2: {}
lru-cache@5.1.1:
@@ -20861,7 +20858,7 @@ snapshots:
npm-run-path: 6.0.0
picocolors: 1.1.1
picomatch: 4.0.3
strip-ansi: 7.1.0
strip-ansi: 7.1.2
tiny-invariant: 1.3.3
tinyglobby: 0.2.14
vite: 7.1.7(@types/node@24.6.0)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(terser@5.43.1)(yaml@2.8.1)
@@ -21485,7 +21482,7 @@ snapshots:
dependencies:
ansi-styles: 6.2.3
string-width: 7.2.0
strip-ansi: 7.1.0
strip-ansi: 7.1.2
wrappy@1.0.2: {}
+95
View File
@@ -0,0 +1,95 @@
#!/usr/bin/env node
import { spawn } from 'node:child_process'
import fs from 'node:fs'
import { cp, mkdir, readFile, rm, writeFile, access } from 'node:fs/promises'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import archiver from 'archiver'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const rootDir = path.resolve(__dirname, `..`)
const utoolsDir = path.join(rootDir, `apps`, `utools`)
const distDir = path.join(utoolsDir, `dist`)
const releaseDir = path.join(utoolsDir, `release`)
const iconSource = path.join(rootDir, `public`, `mpmd`, `icon-256.png`)
const iconTarget = path.join(utoolsDir, `logo.png`)
const manifestPath = path.join(utoolsDir, `plugin.json`)
const command = process.platform === `win32` ? `pnpm.cmd` : `pnpm`
function run(command, args, options = {}) {
return new Promise((resolve, reject) => {
const child = spawn(command, args, { stdio: `inherit`, ...options })
child.on(`close`, (code) => {
if (code === 0)
resolve(code)
else
reject(new Error(`${[command, ...args].join(` `)} exited with code ${code}`))
})
child.on(`error`, reject)
})
}
async function ensureFileExists(filePath, friendlyName) {
try {
await access(filePath, fs.constants.F_OK)
}
catch (error) {
throw new Error(`${friendlyName ?? filePath} 不存在,请确认路径是否正确。`)
}
}
async function main() {
const pkg = JSON.parse(await readFile(path.join(rootDir, `package.json`), `utf8`))
const version = pkg.version
console.log(`> 构建 uTools 前端资源(version: ${version}`)
await run(command, [`--filter`, `@md/web`, `run`, `build:utools`], { cwd: rootDir })
await ensureFileExists(distDir, `apps/utools/dist`)
await ensureFileExists(manifestPath, `apps/utools/plugin.json`)
await ensureFileExists(iconSource, `public/mpmd/icon-256.png`)
const manifest = JSON.parse(await readFile(manifestPath, `utf8`))
manifest.version = version
await writeFile(manifestPath, JSON.stringify(manifest, null, 2) + `\n`, `utf8`)
await cp(iconSource, iconTarget)
await rm(releaseDir, { recursive: true, force: true })
await mkdir(releaseDir, { recursive: true })
const packageName = `md-utools-v${version}`
const packageRoot = path.join(releaseDir, packageName)
await rm(packageRoot, { recursive: true, force: true })
await mkdir(packageRoot, { recursive: true })
await cp(distDir, path.join(packageRoot, `dist`), { recursive: true })
for (const file of [`plugin.json`, `preload.js`, `logo.png`, `README.md`]) {
const source = path.join(utoolsDir, file)
await ensureFileExists(source, `apps/utools/${file}`)
await cp(source, path.join(packageRoot, file), { recursive: true })
}
const zipPath = path.join(releaseDir, `${packageName}.zip`)
await new Promise((resolve, reject) => {
const output = fs.createWriteStream(zipPath)
const archive = archiver(`zip`, { zlib: { level: 9 } })
output.on(`close`, resolve)
archive.on(`error`, reject)
archive.pipe(output)
archive.directory(packageRoot, false)
archive.finalize()
})
console.log(`✔ uTools 插件打包完成 => ${path.relative(rootDir, zipPath)}`)
}
main().catch((error) => {
console.error(error)
process.exitCode = 1
})