fix: bundle the content script to stop 'Unsafe attempt to load URL' errors
Dynamic import() of extension modules from a content script makes Chrome log 'Unsafe attempt to load URL chrome-extension://...' on YouTube pages, which fills the extension's error list even though the modules load (HTTP 200). The content script is now one classic file built by 'npm run build'; web_accessible_resources is no longer needed and is removed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
38dabd9fa2
commit
b137776381
@@ -0,0 +1,68 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { bundle } from '../tools/build-content.js';
|
||||
|
||||
const root = fileURLToPath(new URL('..', import.meta.url));
|
||||
|
||||
function project(files) {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'outlier-saver-bundle-'));
|
||||
for (const [name, text] of Object.entries(files)) writeFileSync(join(dir, name), text);
|
||||
return dir;
|
||||
}
|
||||
|
||||
test('bundle puts dependencies before their users and leaves no import/export syntax', () => {
|
||||
const dir = project({
|
||||
'a.js': 'export const A = 1;\n',
|
||||
'b.js': "import { A } from './a.js';\nexport function b() { return A + 1; }\n",
|
||||
'main.js': "import { b } from './b.js';\nglobalThis.result = b();\n",
|
||||
});
|
||||
const output = bundle(join(dir, 'main.js'));
|
||||
assert.ok(!/^\s*(import|export)\b/m.test(output), output);
|
||||
const context = {};
|
||||
new Function('globalThis', output)(context);
|
||||
assert.equal(context.result, 2);
|
||||
});
|
||||
|
||||
test('bundle includes a shared dependency only once', () => {
|
||||
const dir = project({
|
||||
'shared.js': 'export const SHARED = 7;\n',
|
||||
'left.js': "import { SHARED } from './shared.js';\nexport const left = () => SHARED;\n",
|
||||
'right.js': "import { SHARED } from './shared.js';\nexport const right = () => SHARED * 2;\n",
|
||||
'main.js': "import { left } from './left.js';\nimport { right } from './right.js';\nglobalThis.sum = left() + right();\n",
|
||||
});
|
||||
const output = bundle(join(dir, 'main.js'));
|
||||
assert.equal(output.match(/const SHARED/g).length, 1);
|
||||
const context = {};
|
||||
new Function('globalThis', output)(context);
|
||||
assert.equal(context.sum, 21);
|
||||
});
|
||||
|
||||
test('bundle refuses two modules that declare the same top-level name', () => {
|
||||
const dir = project({
|
||||
'a.js': 'export const X = 1;\n',
|
||||
'b.js': 'export const X = 2;\n',
|
||||
'main.js': "import { X } from './a.js';\nimport './b.js';\n",
|
||||
});
|
||||
assert.throws(() => bundle(join(dir, 'main.js')), /Duplicate top-level name "X"/);
|
||||
});
|
||||
|
||||
test('bundle refuses export forms it cannot rewrite', () => {
|
||||
for (const line of ['export default 1;', 'const a = 1;\nexport { a };', "export * from './x.js';"]) {
|
||||
const dir = project({ 'main.js': `${line}\n` });
|
||||
assert.throws(() => bundle(join(dir, 'main.js')), /only "export const/, line);
|
||||
}
|
||||
});
|
||||
|
||||
test('the committed src/content.js is up to date (run `npm run build` if this fails)', () => {
|
||||
const built = bundle(join(root, 'src/content/main.js'));
|
||||
assert.equal(readFileSync(join(root, 'src/content.js'), 'utf8'), built);
|
||||
});
|
||||
|
||||
test('the content script has no dynamic import and the manifest has no web_accessible_resources', () => {
|
||||
assert.ok(!/\bimport\s*\(/.test(readFileSync(join(root, 'src/content.js'), 'utf8')));
|
||||
assert.equal('web_accessible_resources' in JSON.parse(readFileSync(join(root, 'manifest.json'), 'utf8')), false);
|
||||
});
|
||||
Reference in New Issue
Block a user