fork of hey-api/openapi-ts because I need some additional things
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}