()
| 542 | } |
| 543 | |
| 544 | function run() { |
| 545 | const { config, files, help, native, tsconfig } = parseArgs(process.argv.slice(2)) |
| 546 | |
| 547 | if (help) { |
| 548 | console.log("Usage: effect-app-codegen [--file <path>]... [--config <path>] [--tsconfig <path>] [--legacy]") |
| 549 | console.log("Runs codegen blocks in the given files, or scans the current working tree when omitted.") |
| 550 | console.log(`Loads ${CONFIG_FILENAMES.join(", ")} from cwd when present; --config overrides.`) |
| 551 | console.log( |
| 552 | "static/facade blocks resolve via the native tsgo fork by default; --legacy uses the classic typescript API." |
| 553 | ) |
| 554 | console.log("--tsconfig enables `static` model blocks (type-checker-backed literal Encoded/Type).") |
| 555 | return |
| 556 | } |
| 557 | |
| 558 | const defaults = loadConfig(process.cwd(), config) |
| 559 | const targetFiles = files.length > 0 ? files : defaultFiles() |
| 560 | const updated: Array<string> = [] |
| 561 | const untouched: Array<string> = [] |
| 562 | |
| 563 | // Build the type resolver lazily, only if some target file requests static model blocks. |
| 564 | const candidateFiles = targetFiles.filter((f) => fs.existsSync(f) && fs.statSync(f).isFile()) |
| 565 | const staticFiles = candidateFiles.filter((f) => staticModelBlockRe.test(fs.readFileSync(f, "utf8"))) |
| 566 | const preSynced = new Set<string>() |
| 567 | for (const filePath of staticFiles) { |
| 568 | const source = fs.readFileSync(filePath, "utf8") |
| 569 | const synced = syncModelSource(source) |
| 570 | if (synced !== source) { |
| 571 | fs.writeFileSync(filePath, synced) |
| 572 | preSynced.add(filePath) |
| 573 | } |
| 574 | } |
| 575 | const tsconfigPath = staticFiles.length > 0 |
| 576 | ? (tsconfig ?? findNearestTsconfig(path.dirname(staticFiles[0]!))) |
| 577 | : undefined |
| 578 | if (staticFiles.length > 0 && !tsconfigPath) { |
| 579 | throw new Error("static model blocks require a tsconfig; pass --tsconfig <path>") |
| 580 | } |
| 581 | |
| 582 | // Static/facade blocks resolve a model's generated `Encoded`/`Make`/services |
| 583 | // from its NESTED models' generated namespaces. A composite model whose nested |
| 584 | // models are generated in the same run only resolves correctly once those |
| 585 | // namespaces exist, so we iterate until the static files reach a fixed point |
| 586 | // (recreating the resolver each round to pick up freshly-written namespaces). |
| 587 | const updatedSet = new Set<string>() |
| 588 | const MAX_ROUNDS = 5 |
| 589 | let round = 0 |
| 590 | let changedStatic = true |
| 591 | while (round === 0 || (changedStatic && round < MAX_ROUNDS)) { |
| 592 | const firstRound = round === 0 |
| 593 | round++ |
| 594 | changedStatic = false |
| 595 | const resolver: ModelTypeResolver | undefined = tsconfigPath |
| 596 | ? native |
| 597 | ? createNativeModelTypeResolver({ tsconfigPath }) |
| 598 | : createModelTypeResolver({ tsconfigPath, files: staticFiles }) |
| 599 | : undefined |
| 600 | |
| 601 | // First round: process every target (incl. non-static presets). Later rounds: |
no test coverage detected