import { describe, expect, it } from 'vitest';
import {
collectReferencedJsxNames,
extractBabelScriptSrcs,
findHtmlEntriesReferencing,
htmlLoadsJsxModule,
isJsxModule,
} from '../../src/runtime/jsx-module-refs';
const MULTI_FILE_HTML = `
Backups Panel
`;
describe('extractBabelScriptSrcs', () => {
it('returns [] for empty / nullish input', () => {
expect(extractBabelScriptSrcs('')).toEqual([]);
expect(extractBabelScriptSrcs(null)).toEqual([]);
expect(extractBabelScriptSrcs(undefined)).toEqual([]);
});
it('lists only text/babel module srcs, in document order', () => {
expect(extractBabelScriptSrcs(MULTI_FILE_HTML)).toEqual([
'tweaks-panel.jsx',
'icons.jsx',
'chrome.jsx',
'app.jsx',
]);
});
it('ignores CDN/library ';
expect(extractBabelScriptSrcs(html)).toEqual([]);
});
it('handles src-before-type attribute order', () => {
const html = '';
expect(extractBabelScriptSrcs(html)).toEqual(['app.jsx']);
});
it('normalizes a leading ./ and strips query/hash', () => {
const html =
'' +
'';
expect(extractBabelScriptSrcs(html)).toEqual(['icons.jsx', 'chrome.jsx']);
});
it('ignores babel scripts commented out in HTML', () => {
const html =
'' +
'';
expect(extractBabelScriptSrcs(html)).toEqual(['app.jsx']);
expect(extractBabelScriptSrcs(html)).not.toContain('legacy.jsx');
});
});
describe('htmlLoadsJsxModule', () => {
it('matches an exact src reference', () => {
expect(htmlLoadsJsxModule(MULTI_FILE_HTML, 'icons.jsx')).toBe(true);
});
it('matches by basename when the project name has no slash', () => {
const html = '';
expect(htmlLoadsJsxModule(html, 'icons.jsx')).toBe(true);
});
it('is false when the module is not referenced', () => {
expect(htmlLoadsJsxModule(MULTI_FILE_HTML, 'unused.jsx')).toBe(false);
});
it('is false for an empty module name', () => {
expect(htmlLoadsJsxModule(MULTI_FILE_HTML, '')).toBe(false);
});
});
describe('findHtmlEntriesReferencing', () => {
it('returns every HTML entry that loads the module, in map order', () => {
const sources = new Map([
['Backups Panel.html', MULTI_FILE_HTML],
['Overview Panel.html', ''],
['Unrelated.html', ''],
]);
expect(findHtmlEntriesReferencing('icons.jsx', sources)).toEqual([
'Backups Panel.html',
'Overview Panel.html',
]);
});
it('returns [] when no HTML references the module (standalone artifact)', () => {
const sources = new Map([['Page.html', 'no scripts
']]);
expect(findHtmlEntriesReferencing('icons.jsx', sources)).toEqual([]);
});
});
describe('isJsxModule', () => {
it('is true when at least one HTML entry loads the file', () => {
const sources = new Map([['Backups Panel.html', MULTI_FILE_HTML]]);
expect(isJsxModule('app.jsx', sources)).toBe(true);
});
it('is false when nothing references the file', () => {
const sources = new Map([['Backups Panel.html', MULTI_FILE_HTML]]);
expect(isJsxModule('standalone-component.jsx', sources)).toBe(false);
});
});
describe('collectReferencedJsxNames', () => {
const files = [
{ name: 'Backups Panel.html' },
{ name: 'tweaks-panel.jsx' },
{ name: 'icons.jsx' },
{ name: 'chrome.jsx' },
{ name: 'app.jsx' },
{ name: 'standalone.jsx' },
{ name: 'styles.css' },
];
it('returns project file names loaded by an HTML entry, excluding unreferenced ones', async () => {
const read = async (name: string) =>
name === 'Backups Panel.html' ? MULTI_FILE_HTML : null;
const result = await collectReferencedJsxNames(files, read);
expect(result).toEqual(new Set(['tweaks-panel.jsx', 'icons.jsx', 'chrome.jsx', 'app.jsx']));
// A .jsx that no HTML loads stays a normal standalone artifact.
expect(result.has('standalone.jsx')).toBe(false);
});
it('ignores ';
const result = await collectReferencedJsxNames(files, read);
expect(result.size).toBe(0);
});
it('returns an empty set when no HTML entries exist', async () => {
const read = async () => null;
const result = await collectReferencedJsxNames([{ name: 'only.jsx' }], read);
expect(result).toEqual(new Set());
});
});