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); });