refactor: configure Hachidori in the SubMiner build

This commit is contained in:
2026-09-22 12:26:37 -07:00
parent 5354fdf7b9
commit d7e1d7edc6
10 changed files with 113 additions and 21 deletions
+14
View File
@@ -30,6 +30,20 @@ for (const file of [
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 overlayPath = path.join(output, 'overlay-mode.js');
let overlay = fs.readFileSync(overlayPath, 'utf8');
for (const [original, replacement] of [
['export const OVERLAY_MODE = false;', 'export const OVERLAY_MODE = true;'],
['customJavaScript: !IS_FIREFOX,', 'customJavaScript: false,'],
]) {
if (!overlay.includes(original))
throw new Error(`Hachidori host configuration changed upstream: ${original}`);
overlay = overlay.replace(original, replacement);
}
fs.writeFileSync(overlayPath, overlay);
manifest.permissions = manifest.permissions.filter((permission) => permission !== 'userScripts');
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));
}
+37
View File
@@ -0,0 +1,37 @@
import assert from 'node:assert/strict';
import { execFileSync } from 'node:child_process';
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import test from 'node:test';
test('Hachidori staging configures Electron without changing the fork source', async () => {
const files = ['overlay-mode.js', 'manifest.json'];
const source = (file: string) =>
new URL(`../vendor/hachidori/extension/${file}`, import.meta.url);
const before = files.map((file) => readFileSync(source(file), 'utf8'));
execFileSync(process.execPath, [
fileURLToPath(new URL('./build-hachidori.mjs', import.meta.url)),
]);
assert.deepEqual(
files.map((file) => readFileSync(source(file), 'utf8')),
before,
);
const staged = await import(new URL('../build/hachidori/overlay-mode.js', import.meta.url).href);
assert.equal(staged.OVERLAY_MODE, true);
assert.equal(staged.HOST_CAPABILITIES.customJavaScript, false);
const original = await import(source('overlay-mode.js').href);
assert.equal(original.OVERLAY_MODE, false);
assert.equal(original.HOST_CAPABILITIES.customJavaScript, true);
const manifest = JSON.parse(
readFileSync(new URL('../build/hachidori/manifest.json', import.meta.url), 'utf8'),
);
const originalManifest = JSON.parse(readFileSync(source('manifest.json'), 'utf8'));
assert.deepEqual(manifest, {
...originalManifest,
permissions: originalManifest.permissions.filter(
(permission: string) => permission !== 'userScripts',
),
});
});
+5 -5
View File
@@ -25,7 +25,7 @@ function run(code: string) {
});
}
test('Hachidori publishes popup state independently from successful lookups', () => {
test('Hachidori marks popup state independently from successful lookups', () => {
run(`
const events = [];
for (const name of ['yomitan-popup-shown', 'yomitan-popup-hidden', 'subminer-yomitan-lookup']) {
@@ -33,13 +33,13 @@ test('Hachidori publishes popup state independently from successful lookups', ()
}
const attributes = new Map();
const host = { setAttribute: (name, value) => attributes.set(name, value) };
SubMinerHachidori.attention(host, true);
SubMinerHachidori.markHost(host, true);
assert.equal(attributes.get('data-subminer-yomitan-popup-visible'), 'true');
assert.equal(events.join(','), 'yomitan-popup-shown');
assert.equal(events.join(','), '');
SubMinerHachidori.lookup();
SubMinerHachidori.attention(host, false);
SubMinerHachidori.markHost(host, false);
assert.equal(attributes.get('data-subminer-yomitan-popup-visible'), 'false');
assert.equal(events.join(','), 'yomitan-popup-shown,subminer-yomitan-lookup,yomitan-popup-hidden');
assert.equal(events.join(','), 'subminer-yomitan-lookup');
`);
});