tsdoc.ts103 lines · main
| 1 | import fs from 'fs' |
| 2 | import { strict as assert } from 'node:assert' |
| 3 | import { parseArgs } from 'node:util' |
| 4 | import stringify from 'json-stringify-safe' |
| 5 | |
| 6 | import { writeToDisk } from './helpers' |
| 7 | |
| 8 | const args = parseArgs({ |
| 9 | options: { |
| 10 | input: { |
| 11 | type: 'string', |
| 12 | }, |
| 13 | output: { |
| 14 | type: 'string', |
| 15 | short: 'n', |
| 16 | }, |
| 17 | }, |
| 18 | }) |
| 19 | |
| 20 | assert(args.values.input, 'input is required') |
| 21 | assert(args.values.output, 'output is required') |
| 22 | |
| 23 | dereference({ input: args.values.input!, output: args.values.output! }) |
| 24 | interface KV { |
| 25 | [key: string]: any |
| 26 | } |
| 27 | |
| 28 | async function dereference({ input, output }: { input: string; output: string }) { |
| 29 | console.log('input', input) |
| 30 | |
| 31 | const specRaw = fs.readFileSync(input, 'utf8') |
| 32 | const spec = JSON.parse(specRaw) |
| 33 | const kv = chilrenReducer({}, spec) |
| 34 | |
| 35 | // console.log('kv', kv) |
| 36 | const dereferenced = dereferenceReducer(spec, kv) |
| 37 | // console.log('dereferenced', dereferenced) |
| 38 | await writeToDisk(output, stringify(dereferenced, null, 2)) |
| 39 | // console.log('JSON.stringify(dereferenced)', JSON.stringify(spec)) |
| 40 | } |
| 41 | |
| 42 | function chilrenReducer(acc: KV, child: any): KV { |
| 43 | if (!!child.children) { |
| 44 | child.children.forEach((x: any) => chilrenReducer(acc, x)) |
| 45 | } |
| 46 | |
| 47 | const { id }: { id: string } = child |
| 48 | acc[id] = { ...child } |
| 49 | return acc |
| 50 | } |
| 51 | |
| 52 | // Recurse through all children, and if the `type.type` == 'reference' |
| 53 | // then it will add a key "dereferecnced" to the object. |
| 54 | function dereferenceReducer(child: any, kv: KV) { |
| 55 | if (!!child.children) { |
| 56 | child.children.forEach((x: any) => dereferenceReducer(x, kv)) |
| 57 | } |
| 58 | if (!!child.signatures) { |
| 59 | child.signatures.forEach((x: any) => dereferenceReducer(x, kv)) |
| 60 | } |
| 61 | if (!!child.parameters) { |
| 62 | child.parameters.forEach((x: any) => dereferenceReducer(x, kv)) |
| 63 | } |
| 64 | if ( |
| 65 | !!child.type && |
| 66 | !!child.type.declaration && |
| 67 | !!child.type.declaration.children && |
| 68 | child.type.type === 'reflection' |
| 69 | ) { |
| 70 | child.type.declaration.children.forEach((x: any) => dereferenceReducer(x, kv)) |
| 71 | } |
| 72 | |
| 73 | const final = { ...child } |
| 74 | if ( |
| 75 | // For now I can only dereference parameters |
| 76 | // because anything else is producing an error when saving to file: |
| 77 | // TypeError: Converting circular structure to JSON |
| 78 | final.kindString === 'Parameter' && |
| 79 | final.type?.type === 'reference' && |
| 80 | final.type?.id |
| 81 | ) { |
| 82 | const dereferenced = kv[final.type.id] |
| 83 | final.type.dereferenced = dereferenced || {} |
| 84 | return final |
| 85 | } else if ( |
| 86 | final.kindString === 'Property' && |
| 87 | final.type?.type === 'reference' && |
| 88 | final.type?.id |
| 89 | ) { |
| 90 | const dereferenced = kv[final.type.id] |
| 91 | final.type.dereferenced = dereferenced || {} |
| 92 | return final |
| 93 | } else if (final.kindString === 'Type alias' && final.type?.type === 'union') { |
| 94 | // handles union types that contain nested references |
| 95 | // by replacing the reference in-place |
| 96 | final.type.types = final.type.types.map((item: any) => { |
| 97 | return item.type === 'reference' ? kv[item.id] : item |
| 98 | }) |
| 99 | return final |
| 100 | } else { |
| 101 | return final |
| 102 | } |
| 103 | } |