mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-09-30 17:40:53 -07:00
Upstream dropped Firefox, so the overlay copy now turns off custom JavaScript from its plain capability flag. The Anki audio fixture follows the removal of media capture options.
70 lines
3.5 KiB
JavaScript
70 lines
3.5 KiB
JavaScript
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { createHash } from 'node:crypto';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
const source = path.join(repoRoot, 'vendor', 'hachidori');
|
|
const output = path.join(repoRoot, 'build', 'hachidori');
|
|
const provenance = JSON.parse(fs.readFileSync(path.join(source, 'SOURCE.json'), 'utf8'));
|
|
|
|
// Upstream commits the engine binaries. Verify the pinned bytes before staging
|
|
// so ordinary app builds need neither Emscripten nor network access.
|
|
for (const [file, expected] of Object.entries(provenance.artifacts)) {
|
|
const actual = createHash('sha256')
|
|
.update(fs.readFileSync(path.join(source, file)))
|
|
.digest('hex');
|
|
if (actual !== expected) throw new Error(`Hachidori artifact checksum mismatch: ${file}`);
|
|
}
|
|
const extension = path.join(source, 'extension');
|
|
const manifest = JSON.parse(fs.readFileSync(path.join(extension, 'manifest.json'), 'utf8'));
|
|
for (const file of [
|
|
manifest.options_page,
|
|
manifest.background.service_worker,
|
|
'offscreen.html',
|
|
...manifest.content_scripts.flatMap(({ js, css }) => [...js, ...css]),
|
|
]) {
|
|
if (!fs.existsSync(path.join(extension, file)))
|
|
throw new Error(`Missing Hachidori asset: ${file}`);
|
|
}
|
|
fs.rmSync(output, { recursive: true, force: true });
|
|
fs.mkdirSync(output, { recursive: true });
|
|
fs.cpSync(extension, output, { recursive: true });
|
|
// Host configuration belongs in the staged copy, leaving the fork usable in Chrome.
|
|
const hostConfiguration = {
|
|
'overlay-mode.js': [
|
|
['export const OVERLAY_MODE = false;', 'export const OVERLAY_MODE = true;'],
|
|
['customJavaScript: true,', 'customJavaScript: false,'],
|
|
],
|
|
// Overlay hosts seed the lookup highlight off because their Anki screenshot is
|
|
// the see-through viewport. SubMiner captures media from mpv, and without the
|
|
// highlight the sidebar shows nothing for the word being looked up.
|
|
'setup-state.js': [
|
|
[
|
|
'lookupMode: "hover",\n sourceHighlightEnabled: false,',
|
|
'lookupMode: "hover",\n sourceHighlightEnabled: true,',
|
|
],
|
|
],
|
|
};
|
|
for (const [file, replacements] of Object.entries(hostConfiguration)) {
|
|
const filePath = path.join(output, file);
|
|
let text = fs.readFileSync(filePath, 'utf8');
|
|
for (const [original, replacement] of replacements) {
|
|
if (!text.includes(original))
|
|
throw new Error(`Hachidori host configuration changed upstream: ${original}`);
|
|
text = text.replace(original, replacement);
|
|
}
|
|
fs.writeFileSync(filePath, text);
|
|
}
|
|
manifest.permissions = manifest.permissions.filter((permission) => permission !== 'userScripts');
|
|
// A fixed key pins the extension ID, so Hachidori's storage origin (dictionaries,
|
|
// settings, setup state) survives userData path changes. Electron otherwise
|
|
// derives the ID from the unpacked directory path.
|
|
manifest.key =
|
|
'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA40zTTbhCbrKnDOqj17tosDChP60VXuuMjR0tFFR/kAet21jrYn0JKPzqqNpXMGaJsx1chnuMOgaP0zuOT+gv9DNVV83wGLhQL7cC4LCoGN648WqnE2jYqBPj6hdD5A/N3uPeKFRAHkiC9HKoj2Et6sYZFTqag79QqQkkUz17X2VpjuTGmTP3lvhyEwhJWQKzOU0k6dTn6fKB2ZSqTebpw/JS2G8jf58vHlQOcfLhIiolKCG9DfMuo11aoG1s3AsFmQmw9VVyp4wgyQ80UI4lyBdqboanJxEiZ211+HKWPEgtG6VML1+Uo59qWS3uBU1apxE1+7E51UBEHaTsxajFIQIDAQAB';
|
|
fs.writeFileSync(path.join(output, 'manifest.json'), JSON.stringify(manifest, null, 2) + '\n');
|
|
for (const file of ['LICENSE', 'SOURCE.json', 'README.md']) {
|
|
fs.copyFileSync(path.join(source, file), path.join(output, file));
|
|
}
|
|
process.stdout.write(`Hachidori ${provenance.revision} staged in ${output}\n`);
|