( externalHTTPRoutes = [], externalWebSocketRoutes = [], )
| 54 | * @param {string[]} externalWebSocketRoutes Additional WS routes to parse |
| 55 | */ |
| 56 | const buildSchemas = async ( |
| 57 | externalHTTPRoutes = [], |
| 58 | externalWebSocketRoutes = [], |
| 59 | ) => { |
| 60 | const { getRouteFiles, tsExtension } = await import('../build/utils.js'); |
| 61 | |
| 62 | const schemas = ['BodySchema', 'QuerySchema', 'ResponseSchema']; |
| 63 | const settings = { |
| 64 | ignoreErrors: true, |
| 65 | noExtraProps: true, |
| 66 | required: true, |
| 67 | }; |
| 68 | |
| 69 | const { compilerOptions } = JSON.parse( |
| 70 | await fs.readFile('tsconfig.json', 'utf-8'), |
| 71 | ); |
| 72 | |
| 73 | const { Config } = await import('../build/config.js'); |
| 74 | const [httpRoutes, wsRoutes] = await getRouteFiles(new Config()); |
| 75 | |
| 76 | // Depending on if we're parsing an external projects routes, |
| 77 | // skip the prebuilt ones. This makes it much faster to build |
| 78 | const routesToParse = ( |
| 79 | moduleMain |
| 80 | ? [...httpRoutes, ...wsRoutes] |
| 81 | : [...externalHTTPRoutes, ...externalWebSocketRoutes] |
| 82 | ).filter((r) => r.endsWith(tsExtension)); |
| 83 | |
| 84 | if (routesToParse.length === 0) { |
| 85 | return; |
| 86 | } |
| 87 | |
| 88 | // Build a SINGLE TypeScript program for every route up front. Previously a |
| 89 | // fresh program was created per route, which re-parsed the entire standard |
| 90 | // lib and the large transitive `.d.ts` graph (puppeteer-core, playwright, |
| 91 | // @types/node) once per file — the dominant cost of this script. One shared |
| 92 | // program does that work a single time. |
| 93 | // |
| 94 | // Schema generation must be deterministic and independent of the consumer's |
| 95 | // runtime module settings. Under moduleResolution "nodenext"/"node16", types |
| 96 | // pulled from dual CJS/ESM packages (e.g. puppeteer-core) serialize as |
| 97 | // absolute-path `import("...",{with:{"resolution-mode":"import"}}).Type` |
| 98 | // $ref names that the ajv-backed validator cannot resolve at request time. |
| 99 | // Forcing the canonical es2022/bundler resolution yields stable, named $refs |
| 100 | // for every consumer regardless of how their own tsconfig is configured. |
| 101 | const program = TJS.getProgramFromFiles( |
| 102 | routesToParse, |
| 103 | { ...compilerOptions, module: 'es2022', moduleResolution: 'bundler' }, |
| 104 | './', |
| 105 | ); |
| 106 | |
| 107 | // `uniqueNames` lets identically-named interfaces (e.g. every route's |
| 108 | // `BodySchema`) coexist in one program by suffixing each definition with a |
| 109 | // hash. We use it only to address the specific symbol a given route exports; |
| 110 | // the suffixes are stripped from the emitted files (see stripHashSuffixes). |
| 111 | const generator = TJS.buildGenerator(program, { |
| 112 | ...settings, |
| 113 | uniqueNames: true, |
no test coverage detected