Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/release-extension.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ jobs:
echo "tag=$tag" >> "$GITHUB_OUTPUT"
echo "zip_name=stackprism-v${version}.zip" >> "$GITHUB_OUTPUT"
echo "crx_name=stackprism-v${version}.crx" >> "$GITHUB_OUTPUT"
echo "xpi_name=stackprism-v${version}.xpi" >> "$GITHUB_OUTPUT"

- name: 打包 zip
shell: bash
Expand All @@ -78,6 +79,13 @@ jobs:
)
sha256sum "release/${{ steps.meta.outputs.zip_name }}" > "release/${{ steps.meta.outputs.zip_name }}.sha256"

- name: 打包 Firefox .xpi
shell: bash
run: |
set -euo pipefail
node build-scripts/package-firefox.mjs
sha256sum "release/${{ steps.meta.outputs.xpi_name }}" > "release/${{ steps.meta.outputs.xpi_name }}.sha256"

- name: 签名 crx
id: crx
shell: bash
Expand Down Expand Up @@ -105,6 +113,8 @@ jobs:
assets=(
"release/${{ steps.meta.outputs.zip_name }}"
"release/${{ steps.meta.outputs.zip_name }}.sha256"
"release/${{ steps.meta.outputs.xpi_name }}"
"release/${{ steps.meta.outputs.xpi_name }}.sha256"
)
if [ -f "release/${{ steps.meta.outputs.crx_name }}" ]; then
assets+=(
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ docs/public/icon.svg

# Build and packaged extension output
dist/
dist-firefox/
build/
release/
releases/
artifacts/
public/injected/
*.zip
*.crx
*.xpi
*.pem

# Test output and local reports
Expand Down
88 changes: 88 additions & 0 deletions build-scripts/package-firefox.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { cpSync, rmSync, readFileSync, writeFileSync, mkdirSync, existsSync, createWriteStream } from 'node:fs'
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import { createRequire } from 'node:module'
import archiver from 'archiver'

const require = createRequire(import.meta.url)
const esbuild = require('esbuild')

const root = resolve(dirname(fileURLToPath(import.meta.url)), '..')
const distDir = resolve(root, 'dist')
const firefoxDir = resolve(root, 'dist-firefox')

if (!existsSync(distDir)) {
console.error('[package-firefox] dist/ not found, run `pnpm build` first')
process.exit(1)
}

rmSync(firefoxDir, { recursive: true, force: true })
cpSync(distDir, firefoxDir, { recursive: true })

// --- Bundle background script as IIFE ---
// CRXJS outputs background as ES modules with code-split shared chunks.
// Firefox background scripts don't support ES modules, so we rebundle
// the entry point into a single IIFE via esbuild.

const loaderPath = resolve(firefoxDir, 'service-worker-loader.js')
const loaderCode = readFileSync(loaderPath, 'utf8')
const entryMatch = loaderCode.match(/import\s+'\.\/(assets\/[^']+)'/)
if (!entryMatch) {
console.error('[package-firefox] could not resolve service-worker-loader entry')
process.exit(1)
}

const entryPath = resolve(firefoxDir, entryMatch[1])
const backgroundPath = resolve(firefoxDir, 'background.js')

await esbuild.build({
entryPoints: [entryPath],
bundle: true,
format: 'iife',
outfile: backgroundPath,
target: 'es2022',
platform: 'browser',
logLevel: 'warning'
})

console.log('[package-firefox] bundled background.js as IIFE')

// --- Transform manifest.json ---

const manifestPath = resolve(firefoxDir, 'manifest.json')
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'))

if (manifest.background?.service_worker) {
manifest.background = { scripts: ['background.js'] }
}

manifest.browser_specific_settings = {
gecko: {
id: 'stackprism@setube.github.io',
strict_min_version: '128.0'
}
}

writeFileSync(manifestPath, JSON.stringify(manifest, null, 2))
console.log('[package-firefox] manifest.json transformed')

// --- Package .xpi ---

const releaseDir = resolve(root, 'release')
if (!existsSync(releaseDir)) mkdirSync(releaseDir)

const version = manifest.version
const xpiName = `stackprism-v${version}.xpi`
const xpiPath = resolve(releaseDir, xpiName)

await new Promise((ok, reject) => {
const output = createWriteStream(xpiPath)
const archive = archiver('zip', { zlib: { level: 9 } })
output.on('close', ok)
archive.on('error', reject)
archive.pipe(output)
archive.glob('**', { cwd: firefoxDir, dot: true })
archive.finalize()
})

console.log(`[package-firefox] created release/${xpiName}`)
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"lint": "eslint src",
"build:injected": "node build-scripts/build-injected.mjs",
"build": "pnpm run build:injected && vite build",
"build:firefox": "pnpm run build && node build-scripts/package-firefox.mjs",
"test:unit": "node --test tests/*.test.mjs",
"check:links": "node build-scripts/check-tech-links.mjs",
"extract:icons": "node build-scripts/extract-wappalyzer-icons.mjs",
Expand All @@ -24,6 +25,8 @@
"@types/node": "^20.12.7",
"@vitejs/plugin-vue": "^5.0.4",
"@vue/eslint-config-typescript": "^14.7.0",
"archiver": "^7.0.1",
"esbuild": "^0.21.5",
"eslint": "^10.3.0",
"eslint-plugin-vue": "^10.9.1",
"lucide-vue-next": "^1.0.0",
Expand Down
Loading