fork of hey-api/openapi-ts because I need some additional things
at feat/skip-token 86 lines 3.0 kB view raw
1import path from 'node:path'; 2 3import type { IProjectRenderMeta } from '../extensions'; 4import { FileRegistry } from '../files/registry'; 5import { defaultExtensions } from '../languages/extensions'; 6import { defaultModuleEntryNames } from '../languages/modules'; 7import { defaultNameConflictResolvers } from '../languages/resolvers'; 8import type { Extensions, ModuleEntryNames, NameConflictResolvers } from '../languages/types'; 9import { NodeRegistry } from '../nodes/registry'; 10import type { IOutput } from '../output'; 11import { Planner } from '../planner/planner'; 12import { simpleNameConflictResolver } from '../planner/resolvers'; 13import type { NameConflictResolver } from '../planner/types'; 14import type { Renderer } from '../renderer'; 15import { SymbolRegistry } from '../symbols/registry'; 16import type { IProject } from './types'; 17 18export class Project implements IProject { 19 private _isPlanned = false; 20 21 readonly files: FileRegistry; 22 readonly nodes = new NodeRegistry(); 23 readonly symbols = new SymbolRegistry(); 24 25 readonly defaultFileName: string; 26 readonly defaultNameConflictResolver: NameConflictResolver; 27 readonly extensions: Extensions; 28 readonly fileName?: (name: string) => string; 29 readonly moduleEntryNames: ModuleEntryNames; 30 readonly nameConflictResolvers: NameConflictResolvers; 31 readonly renderers: ReadonlyArray<Renderer>; 32 readonly root: string; 33 34 constructor( 35 args: Pick< 36 Partial<IProject>, 37 | 'defaultFileName' 38 | 'defaultNameConflictResolver' 39 | 'extensions' 40 | 'fileName' 41 | 'moduleEntryNames' 42 | 'nameConflictResolvers' 43 | 'renderers' 44 > & 45 Pick<IProject, 'root'>, 46 ) { 47 const fileName = args.fileName; 48 this.defaultFileName = args.defaultFileName ?? 'main'; 49 this.defaultNameConflictResolver = 50 args.defaultNameConflictResolver ?? simpleNameConflictResolver; 51 this.extensions = { 52 ...defaultExtensions, 53 ...args.extensions, 54 }; 55 this.fileName = typeof fileName === 'string' ? () => fileName : fileName; 56 this.files = new FileRegistry(this); 57 this.moduleEntryNames = { 58 ...defaultModuleEntryNames, 59 ...args.moduleEntryNames, 60 }; 61 this.nameConflictResolvers = { 62 ...defaultNameConflictResolvers, 63 ...args.nameConflictResolvers, 64 }; 65 this.renderers = args.renderers ?? []; 66 this.root = path.resolve(args.root).replace(/[/\\]+$/, ''); 67 } 68 69 plan(meta?: IProjectRenderMeta): void { 70 if (this._isPlanned) return; 71 new Planner(this).plan(meta); 72 this._isPlanned = true; 73 } 74 75 render(meta?: IProjectRenderMeta): ReadonlyArray<IOutput> { 76 if (!this._isPlanned) this.plan(meta); 77 const files: Array<IOutput> = []; 78 for (const file of this.files.registered()) { 79 if (!file.external && file.finalPath && file.renderer) { 80 const content = file.renderer.render({ file, meta, project: this }); 81 files.push({ content, path: file.finalPath }); 82 } 83 } 84 return files; 85 } 86}