speckl/compiler/dist/index.js
Scott Scoble a2f7b7b2e9
Some checks failed
Deploy Landing Page to Storj / deploy (push) Failing after 2s
fix: add dist-check guard + List<T> type parsing + state machine examples
- check-dist.sh: Prevents DIST DESYNC by verifying all dist/*.js files
  are newer than their src/*.ts sources. Fails build if stale.
- Makefile: Standard build targets (build/check-dist/test/recompile/clean)
- src/index.ts: Integrated dist freshness check before compilation
- src/parser.ts: Added List<T> type parsing alongside existing Set<T>/Map<K,V>
  (e.g., 'log: List<LogEntry>' now parses as { type: 'list', elementType })
- examples/: Restored v0.3 state machine specs (TigerBeetleLedger,
  AccountLedger, ToggleSwitch, TwoPhaseCommit)
- DIST DESYNC is a known recurring anti-pattern — this guard prevents it

Tests: 39/41 pass (2 pre-existing E2E WASM failures due to missing wabt)
2026-05-05 03:11:53 -07:00

88 lines
No EOL
3.4 KiB
JavaScript

#!/usr/bin/env node
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import { parseSpeckFile } from './parser.js';
import { generateProvenanceGraph } from './generators/provenance.js';
import { generateCycloneDX } from './generators/cyclonedx.js';
import { generateSPDX } from './generators/spdx.js';
import { generateTypeScriptStateMachine } from './generators/typescript-state-machine.js';
import { generateWASM } from './generators/wasm.js';
import fs from 'fs';
import path from 'path';
async function main() {
// Guard: check that dist is not stale before compiling
const checkScript = path.join(path.dirname(new URL(import.meta.url).pathname), '..', 'check-dist.sh');
if (fs.existsSync(checkScript)) {
try {
const { execSync } = await import('child_process');
execSync(`bash "${checkScript}"`, { stdio: 'inherit' });
}
catch {
console.error('\n❌ Cannot compile with stale dist. Rebuild first.');
process.exit(1);
}
}
const argv = await yargs(hideBin(process.argv))
.command('$0 <file>', 'Compile a SpeckDL specification', (yargsCmd) => {
return yargsCmd
.positional('file', {
describe: 'SpeckDL file to compile',
type: 'string',
demandOption: true,
})
.option('output-dir', {
alias: 'o',
describe: 'Output directory for compiled artifacts',
type: 'string',
default: './out',
})
.option('bom-format', {
alias: 'b',
describe: 'BOM output format',
type: 'string',
choices: ['cdx', 'spdx', 'both'],
default: 'both',
});
})
.help()
.argv;
const file = argv.file;
const options = {
outputDir: argv.outputDir,
bomFormat: argv.bomFormat,
};
console.log(`Compiling ${file}...`);
// Parse the Speck file
const ast = parseSpeckFile(file);
const speckCount = ast.specks.length;
const memberCount = ast.specks.reduce((sum, s) => sum + s.members.length, 0);
console.log(`AST parsed successfully: ${speckCount} speck(s), ${memberCount} member(s)`);
// Ensure output directory exists
if (!fs.existsSync(options.outputDir)) {
fs.mkdirSync(options.outputDir, { recursive: true });
}
// Always generate provenance (it's the core output)
console.log('\nGenerating provenance graph (PROV-O JSON-LD)...');
generateProvenanceGraph(ast, options.outputDir);
// Generate TypeScript state machine
console.log('\nGenerating TypeScript state machine...');
generateTypeScriptStateMachine(ast, options.outputDir);
// Generate WASM state machine
console.log('\nGenerating WebAssembly (WAT)...');
generateWASM(ast, options.outputDir);
// Generate BOM based on format selection
if (options.bomFormat === 'cdx' || options.bomFormat === 'both') {
console.log('\nGenerating CycloneDX BOM...');
generateCycloneDX(ast, options.outputDir);
}
if (options.bomFormat === 'spdx' || options.bomFormat === 'both') {
console.log('\nGenerating SPDX BOM...');
generateSPDX(ast, options.outputDir);
}
console.log(`\nCompilation complete. Output: ${options.outputDir}/`);
}
main().catch((err) => {
console.error('Compilation failed:', err);
process.exit(1);
});
//# sourceMappingURL=index.js.map