()
| 1630 | function countBy<T>(xs: ReadonlyArray<T>, key: (x: T) => string): Record<string, number> { |
| 1631 | const out: Record<string, number> = {}; |
| 1632 | for (const x of xs) out[key(x)] = (out[key(x)] ?? 0) + 1; |
| 1633 | return Object.fromEntries(Object.entries(out).sort(([a], [b]) => a.localeCompare(b))); |
| 1634 | } |
| 1635 | |
| 1636 | export function emitArtifact( |
| 1637 | manifest: unknown, |
| 1638 | declarations: CompileResult['declarations'], |
| 1639 | rules: ReadonlyArray<CompiledFungrimRule> |
| 1640 | ): string { |
| 1641 | const lines: string[] = ['{']; |
| 1642 | lines.push(`"manifest": ${JSON.stringify(manifest, undefined, 2)},`); |
| 1643 | lines.push('"declarations": {'); |
| 1644 | const declLines = Object.entries(declarations).map( |
| 1645 | ([name, rec]) => `${JSON.stringify(name)}: ${JSON.stringify(rec)}` |
| 1646 | ); |
| 1647 | lines.push(declLines.join(',\n')); |
| 1648 | lines.push('},'); |
| 1649 | lines.push('"rules": ['); |
| 1650 | lines.push(rules.map((r) => JSON.stringify(r)).join(',\n')); |
| 1651 | lines.push(']'); |
| 1652 | lines.push('}'); |
| 1653 | return lines.join('\n') + '\n'; |
| 1654 | } |
| 1655 | |
| 1656 | // --------------------------------------------------------------------------- |
| 1657 | // Main |
| 1658 | // --------------------------------------------------------------------------- |
| 1659 | |
| 1660 | function main(): void { |
| 1661 | // argv[1] is this script (guaranteed by the guard below) |
| 1662 | const scriptDir = path.dirname(path.resolve(process.argv[1])); |
| 1663 | const rootDir = path.resolve(scriptDir, '../..'); |
| 1664 | const corpusDir = path.join(rootDir, 'data/fungrim'); |
| 1665 | const overridesPath = path.join(scriptDir, 'curation-overrides.json'); |
| 1666 | const artifactPath = path.join( |
| 1667 | rootDir, |
| 1668 | 'src/compute-engine/fungrim/fungrim-core-data.json' |
| 1669 | ); |
| 1670 | const reportPath = path.join(scriptDir, 'rule-compile-report.json'); |
| 1671 | |
| 1672 | const corpus = loadCorpus(corpusDir); |
| 1673 | const overrides: CurationOverrides = fs.existsSync(overridesPath) |
| 1674 | ? JSON.parse(fs.readFileSync(overridesPath, 'utf8')) |
| 1675 | : {}; |
| 1676 | // Curated injected entries are appended to the slice and compiled through |
| 1677 | // the full pipeline (guards, orientation, self-test) like corpus entries. |
| 1678 | const slice = [ |
| 1679 | ...corpus.entries.filter(isSliceEntry), |
| 1680 | ...(overrides.inject ?? []), |
| 1681 | ]; |
| 1682 | |
| 1683 | const started = Date.now(); |
| 1684 | const result = compileEntries(slice, corpus.declarations, overrides); |
| 1685 | const elapsed = Date.now() - started; |
| 1686 | |
| 1687 | const upstream = JSON.parse( |
| 1688 | fs.readFileSync(path.join(corpusDir, 'MANIFEST.json'), 'utf8') |
| 1689 | ); |
no test coverage detected