Files
core2026/tools/typography-audit/load-fixtures.mjs
T

76 lines
3.3 KiB
JavaScript

import fs from 'node:fs';
import path from 'node:path';
import { createRequire } from 'node:module';
import { pathToFileURL, fileURLToPath } from 'node:url';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
const require = createRequire(path.join(root, 'app/game-frontend/package.json'));
const ts = require('typescript');
const out = path.join(process.env.TYPOGRAPHY_OUTPUT_DIR ?? path.join(root, 'test-results/typography-audit'), 'modules');
fs.mkdirSync(out, { recursive: true });
const cache = new Map();
export async function fixture(relative) {
const url = compile(path.join(root, relative));
return import(url);
}
function compile(filename) {
if (cache.has(filename)) return cache.get(filename);
const target = path.join(
out,
path
.relative(root, filename)
.replaceAll('/', '__')
.replace(/\.tsx?$/, '.mjs')
);
const url = pathToFileURL(target).href;
cache.set(filename, url);
let source = fs
.readFileSync(filename, 'utf8')
.replaceAll('import.meta.url', JSON.stringify(pathToFileURL(filename).href))
.replaceAll('import.meta.dirname', JSON.stringify(path.dirname(filename)));
const ast = ts.createSourceFile(filename, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
const pieces = [],
names = [];
for (const node of ast.statements) {
// Reuse only original fixture definitions, never register/execute the tests.
if (
ts.isImportDeclaration(node) ||
ts.isTypeAliasDeclaration(node) ||
ts.isInterfaceDeclaration(node) ||
ts.isFunctionDeclaration(node) ||
ts.isClassDeclaration(node) ||
ts.isVariableStatement(node)
) {
pieces.push(node.getFullText(ast));
if (ts.isVariableStatement(node))
for (const dec of node.declarationList.declarations)
if (ts.isIdentifier(dec.name)) names.push(dec.name.text);
if (ts.isFunctionDeclaration(node) && node.name) names.push(node.name.text);
}
}
const isSpec = filename.endsWith('.spec.ts');
const input = isSpec ? pieces.join('\n') + '\nexport { ' + names.join(', ') + ' };' : source;
let js = ts.transpileModule(input, {
compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ESNext },
}).outputText;
js = js.replace(/(from\s*|import\s*)['"]([^'"]+)['"]/g, (match, prefix, spec) => {
if (spec.startsWith('node:')) return match;
let dest;
if (spec.startsWith('.')) {
dest = path.resolve(path.dirname(filename), spec);
if (!fs.existsSync(dest) && dest.endsWith('.js')) dest = dest.replace(/\.js$/, '.ts');
if (!fs.existsSync(dest)) {
if (fs.existsSync(dest + '.ts')) dest += '.ts';
else if (fs.existsSync(dest + '.js')) dest += '.js';
}
dest = dest.endsWith('.ts') ? compile(dest) : pathToFileURL(dest).href;
} else {
let resolved = createRequire(filename).resolve(spec);
if (spec === '@playwright/test') resolved = resolved.replace(/index\.js$/, 'index.mjs');
dest = pathToFileURL(resolved).href;
}
return prefix + JSON.stringify(dest);
});
fs.writeFileSync(target, js);
return url;
}