Skip to content

Commit ca5fb86

Browse files
authored
Merge pull request #15 from Fldicoahkiin/feat/firefox-support
feat: 支持 Firefox 浏览器
2 parents 35efc5c + 8530dbf commit ca5fb86

10 files changed

Lines changed: 751 additions & 26 deletions

File tree

.github/workflows/release-extension.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ jobs:
6666
echo "tag=$tag" >> "$GITHUB_OUTPUT"
6767
echo "zip_name=stackprism-v${version}.zip" >> "$GITHUB_OUTPUT"
6868
echo "crx_name=stackprism-v${version}.crx" >> "$GITHUB_OUTPUT"
69+
echo "xpi_name=stackprism-v${version}.xpi" >> "$GITHUB_OUTPUT"
6970
7071
- name: 打包 zip
7172
shell: bash
@@ -78,6 +79,13 @@ jobs:
7879
)
7980
sha256sum "release/${{ steps.meta.outputs.zip_name }}" > "release/${{ steps.meta.outputs.zip_name }}.sha256"
8081
82+
- name: 打包 Firefox .xpi
83+
shell: bash
84+
run: |
85+
set -euo pipefail
86+
node build-scripts/package-firefox.mjs
87+
sha256sum "release/${{ steps.meta.outputs.xpi_name }}" > "release/${{ steps.meta.outputs.xpi_name }}.sha256"
88+
8189
- name: 签名 crx
8290
id: crx
8391
shell: bash
@@ -105,6 +113,8 @@ jobs:
105113
assets=(
106114
"release/${{ steps.meta.outputs.zip_name }}"
107115
"release/${{ steps.meta.outputs.zip_name }}.sha256"
116+
"release/${{ steps.meta.outputs.xpi_name }}"
117+
"release/${{ steps.meta.outputs.xpi_name }}.sha256"
108118
)
109119
if [ -f "release/${{ steps.meta.outputs.crx_name }}" ]; then
110120
assets+=(

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ docs/public/icon.svg
99

1010
# Build and packaged extension output
1111
dist/
12+
dist-firefox/
1213
build/
1314
release/
1415
releases/
1516
artifacts/
1617
public/injected/
1718
*.zip
1819
*.crx
20+
*.xpi
1921
*.pem
2022

2123
# Test output and local reports

build-scripts/package-firefox.mjs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { cpSync, rmSync, readFileSync, writeFileSync, mkdirSync, existsSync, createWriteStream } from 'node:fs'
2+
import { resolve, dirname } from 'node:path'
3+
import { fileURLToPath } from 'node:url'
4+
import { createRequire } from 'node:module'
5+
import archiver from 'archiver'
6+
7+
const require = createRequire(import.meta.url)
8+
const esbuild = require('esbuild')
9+
10+
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..')
11+
const distDir = resolve(root, 'dist')
12+
const firefoxDir = resolve(root, 'dist-firefox')
13+
14+
if (!existsSync(distDir)) {
15+
console.error('[package-firefox] dist/ not found, run `pnpm build` first')
16+
process.exit(1)
17+
}
18+
19+
rmSync(firefoxDir, { recursive: true, force: true })
20+
cpSync(distDir, firefoxDir, { recursive: true })
21+
22+
// --- Bundle background script as IIFE ---
23+
// CRXJS outputs background as ES modules with code-split shared chunks.
24+
// Firefox background scripts don't support ES modules, so we rebundle
25+
// the entry point into a single IIFE via esbuild.
26+
27+
const loaderPath = resolve(firefoxDir, 'service-worker-loader.js')
28+
const loaderCode = readFileSync(loaderPath, 'utf8')
29+
const entryMatch = loaderCode.match(/import\s+'\.\/(assets\/[^']+)'/)
30+
if (!entryMatch) {
31+
console.error('[package-firefox] could not resolve service-worker-loader entry')
32+
process.exit(1)
33+
}
34+
35+
const entryPath = resolve(firefoxDir, entryMatch[1])
36+
const backgroundPath = resolve(firefoxDir, 'background.js')
37+
38+
await esbuild.build({
39+
entryPoints: [entryPath],
40+
bundle: true,
41+
format: 'iife',
42+
outfile: backgroundPath,
43+
target: 'es2022',
44+
platform: 'browser',
45+
logLevel: 'warning'
46+
})
47+
48+
console.log('[package-firefox] bundled background.js as IIFE')
49+
50+
// --- Transform manifest.json ---
51+
52+
const manifestPath = resolve(firefoxDir, 'manifest.json')
53+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'))
54+
55+
if (manifest.background?.service_worker) {
56+
manifest.background = { scripts: ['background.js'] }
57+
}
58+
59+
manifest.browser_specific_settings = {
60+
gecko: {
61+
id: 'stackprism@setube.github.io',
62+
strict_min_version: '128.0'
63+
}
64+
}
65+
66+
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2))
67+
console.log('[package-firefox] manifest.json transformed')
68+
69+
// --- Package .xpi ---
70+
71+
const releaseDir = resolve(root, 'release')
72+
if (!existsSync(releaseDir)) mkdirSync(releaseDir)
73+
74+
const version = manifest.version
75+
const xpiName = `stackprism-v${version}.xpi`
76+
const xpiPath = resolve(releaseDir, xpiName)
77+
78+
await new Promise((ok, reject) => {
79+
const output = createWriteStream(xpiPath)
80+
const archive = archiver('zip', { zlib: { level: 9 } })
81+
output.on('close', ok)
82+
archive.on('error', reject)
83+
archive.pipe(output)
84+
archive.glob('**', { cwd: firefoxDir, dot: true })
85+
archive.finalize()
86+
})
87+
88+
console.log(`[package-firefox] created release/${xpiName}`)

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"lint": "eslint src",
1010
"build:injected": "node build-scripts/build-injected.mjs",
1111
"build": "pnpm run build:injected && vite build",
12+
"build:firefox": "pnpm run build && node build-scripts/package-firefox.mjs",
1213
"test:unit": "node --test tests/*.test.mjs",
1314
"check:links": "node build-scripts/check-tech-links.mjs",
1415
"extract:icons": "node build-scripts/extract-wappalyzer-icons.mjs",
@@ -24,6 +25,8 @@
2425
"@types/node": "^20.12.7",
2526
"@vitejs/plugin-vue": "^5.0.4",
2627
"@vue/eslint-config-typescript": "^14.7.0",
28+
"archiver": "^7.0.1",
29+
"esbuild": "^0.21.5",
2730
"eslint": "^10.3.0",
2831
"eslint-plugin-vue": "^10.9.1",
2932
"lucide-vue-next": "^1.0.0",

0 commit comments

Comments
 (0)