|
| 1 | +import { loadStringConstants } from "./constants.js"; |
| 2 | +import { loadConfig } from "./config.js"; |
| 3 | +import { readTextFile, writeTextFile } from "./fs.js"; |
| 4 | +import { ProgressReporter } from "./progress.js"; |
| 5 | +import { discoverRoutes } from "./routes.js"; |
| 6 | +import { extractRouteData } from "./analysis.js"; |
| 7 | +import { buildSpec } from "./spec.js"; |
| 8 | +import { indexFunctions } from "./symbols.js"; |
| 9 | +import { resolveRawGithubUrl, resolveSourcePath } from "./source.js"; |
| 10 | +import type { ApiSpec, CliOptions } from "./types.js"; |
| 11 | + |
| 12 | +export async function runExtraction(cliOptions: CliOptions): Promise<ApiSpec> { |
| 13 | + const progress = new ProgressReporter(cliOptions.json); |
| 14 | + const configStep = progress.startStep("loading config"); |
| 15 | + const config = await loadConfig(cliOptions.configPath ?? "./config.json", cliOptions); |
| 16 | + configStep.done(); |
| 17 | + |
| 18 | + const routesStep = progress.startStep("discovering routes"); |
| 19 | + const routes = await discoverRoutes( |
| 20 | + resolveRawGithubUrl(config.sourceBranch, resolveSourcePath(config.rootDir, config.routeFile)), |
| 21 | + ); |
| 22 | + routesStep.done(`${routes.length} routes`); |
| 23 | + |
| 24 | + const constantsStep = progress.startStep("loading constants"); |
| 25 | + const constants = await loadStringConstants(config.rootDir, config.sourceBranch); |
| 26 | + constantsStep.done(`${constants.size} constants`); |
| 27 | + |
| 28 | + const symbolsStep = progress.startStep("indexing functions"); |
| 29 | + const functions = await indexFunctions(config.rootDir, config.includeHelpers, config.sourceBranch); |
| 30 | + symbolsStep.done(`${functions.size} functions`); |
| 31 | + |
| 32 | + const extractStep = progress.startStep("extracting route parameters"); |
| 33 | + const routeExtractions = routes.map((route) => |
| 34 | + extractRouteData(route, functions, constants, config.maxCallDepth, config.debug), |
| 35 | + ); |
| 36 | + extractStep.done(`${routeExtractions.length} route scans`); |
| 37 | + |
| 38 | + const specStep = progress.startStep("building output"); |
| 39 | + const diagnostics = routeExtractions.flatMap((extraction) => extraction.diagnostics); |
| 40 | + const spec = buildSpec(config, routeExtractions, diagnostics); |
| 41 | + const outputText = `${JSON.stringify(spec, null, 2)}\n`; |
| 42 | + await writeTextFile(config.outputPath, outputText); |
| 43 | + if (config.diagnosticsOutputPath !== undefined) { |
| 44 | + await writeTextFile(config.diagnosticsOutputPath, `${JSON.stringify(diagnostics, null, 2)}\n`); |
| 45 | + } |
| 46 | + specStep.done(config.outputPath); |
| 47 | + |
| 48 | + if (config.failOnDiagnostics && diagnostics.length > 0) { |
| 49 | + throw new Error("Extraction produced diagnostics."); |
| 50 | + } |
| 51 | + |
| 52 | + if ( |
| 53 | + config.failOnUnresolved && |
| 54 | + diagnostics.some((diagnostic) => diagnostic.level === "error") |
| 55 | + ) { |
| 56 | + throw new Error("Extraction produced unresolved handler errors."); |
| 57 | + } |
| 58 | + |
| 59 | + return spec; |
| 60 | +} |
| 61 | + |
| 62 | +export async function readSpec(filePath: string): Promise<ApiSpec> { |
| 63 | + const sourceText = await readTextFile(filePath); |
| 64 | + return JSON.parse(sourceText) as ApiSpec; |
| 65 | +} |
0 commit comments