@atomic-ehr/codegen 0.0.1-canary.20250821160126.c552195 → 0.0.1-canary.20250822150706.c3b8669
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/builder.d.ts +3 -3
- package/dist/api/builder.d.ts.map +1 -1
- package/dist/api/builder.js +374 -0
- package/dist/api/generators/base/BaseGenerator.d.ts +4 -4
- package/dist/api/generators/base/BaseGenerator.d.ts.map +1 -1
- package/dist/api/generators/base/BaseGenerator.js +572 -0
- package/dist/api/generators/base/FileManager.d.ts +2 -2
- package/dist/api/generators/base/FileManager.d.ts.map +1 -1
- package/dist/api/generators/base/FileManager.js +204 -0
- package/dist/api/generators/base/PythonTypeMapper.d.ts +2 -2
- package/dist/api/generators/base/PythonTypeMapper.d.ts.map +1 -1
- package/dist/api/generators/base/PythonTypeMapper.js +71 -0
- package/dist/api/generators/base/TemplateEngine.d.ts +1 -1
- package/dist/api/generators/base/TemplateEngine.d.ts.map +1 -1
- package/dist/api/generators/base/TemplateEngine.js +133 -0
- package/dist/api/generators/base/TypeMapper.js +153 -0
- package/dist/api/generators/base/TypeScriptTypeMapper.d.ts +1 -1
- package/dist/api/generators/base/TypeScriptTypeMapper.d.ts.map +1 -1
- package/dist/api/generators/base/TypeScriptTypeMapper.js +232 -0
- package/dist/api/generators/base/builders/DirectoryBuilder.d.ts +4 -4
- package/dist/api/generators/base/builders/DirectoryBuilder.d.ts.map +1 -1
- package/dist/api/generators/base/builders/DirectoryBuilder.js +215 -0
- package/dist/api/generators/base/builders/FileBuilder.d.ts +2 -2
- package/dist/api/generators/base/builders/FileBuilder.d.ts.map +1 -1
- package/dist/api/generators/base/builders/FileBuilder.js +408 -0
- package/dist/api/generators/base/builders/IndexBuilder.d.ts +2 -2
- package/dist/api/generators/base/builders/IndexBuilder.d.ts.map +1 -1
- package/dist/api/generators/base/builders/IndexBuilder.js +290 -0
- package/dist/api/generators/base/enhanced-errors.d.ts +2 -2
- package/dist/api/generators/base/enhanced-errors.d.ts.map +1 -1
- package/dist/api/generators/base/enhanced-errors.js +259 -0
- package/dist/api/generators/base/error-handler.d.ts +1 -1
- package/dist/api/generators/base/error-handler.d.ts.map +1 -1
- package/dist/api/generators/base/error-handler.js +243 -0
- package/dist/api/generators/base/errors.d.ts +2 -2
- package/dist/api/generators/base/errors.d.ts.map +1 -1
- package/dist/api/generators/base/errors.js +694 -0
- package/dist/api/generators/base/index.d.ts +22 -22
- package/dist/api/generators/base/index.d.ts.map +1 -1
- package/dist/api/generators/base/index.js +161 -0
- package/dist/api/generators/base/types.d.ts +2 -2
- package/dist/api/generators/base/types.d.ts.map +1 -1
- package/dist/api/generators/base/types.js +12 -0
- package/dist/api/generators/rest-client.d.ts +2 -2
- package/dist/api/generators/rest-client.d.ts.map +1 -1
- package/dist/api/generators/rest-client.js +847 -0
- package/dist/api/generators/search-parameter-enhancer.d.ts +1 -1
- package/dist/api/generators/search-parameter-enhancer.d.ts.map +1 -1
- package/dist/api/generators/search-parameter-enhancer.js +801 -0
- package/dist/api/generators/types.js +4 -0
- package/dist/api/generators/typescript.d.ts +3 -3
- package/dist/api/generators/typescript.d.ts.map +1 -1
- package/dist/api/generators/typescript.js +537 -0
- package/dist/api/generators/validation-generator.js +632 -0
- package/dist/api/index.d.ts +10 -10
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +51 -0
- package/dist/cli/commands/generate/typescript.d.ts +1 -1
- package/dist/cli/commands/generate/typescript.d.ts.map +1 -1
- package/dist/cli/commands/generate/typescript.js +52 -0
- package/dist/cli/commands/generate.d.ts +5 -12
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +158 -0
- package/dist/cli/commands/index.d.ts +2 -1
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +100 -0
- package/dist/cli/commands/typeschema/generate.js +130 -0
- package/dist/cli/commands/typeschema.js +48 -0
- package/dist/cli/index.js +12 -8664
- package/dist/cli/utils/log.d.ts +2 -2
- package/dist/cli/utils/log.d.ts.map +1 -1
- package/dist/cli/utils/log.js +23 -0
- package/dist/cli/utils/prompts.js +224 -0
- package/dist/cli/utils/spinner.js +270 -0
- package/dist/config.d.ts +22 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +703 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +84 -38
- package/dist/logger.js +290 -0
- package/dist/typeschema/cache.d.ts +2 -2
- package/dist/typeschema/cache.d.ts.map +1 -1
- package/dist/typeschema/cache.js +285 -0
- package/dist/typeschema/core/binding.d.ts +1 -1
- package/dist/typeschema/core/binding.d.ts.map +1 -1
- package/dist/typeschema/core/binding.js +187 -0
- package/dist/typeschema/core/field-builder.d.ts +1 -1
- package/dist/typeschema/core/field-builder.d.ts.map +1 -1
- package/dist/typeschema/core/field-builder.js +259 -0
- package/dist/typeschema/core/identifier.js +117 -0
- package/dist/typeschema/core/nested-types.d.ts +1 -1
- package/dist/typeschema/core/nested-types.d.ts.map +1 -1
- package/dist/typeschema/core/nested-types.js +111 -0
- package/dist/typeschema/core/transformer.d.ts +2 -2
- package/dist/typeschema/core/transformer.d.ts.map +1 -1
- package/dist/typeschema/core/transformer.js +345 -0
- package/dist/typeschema/generator.d.ts +3 -3
- package/dist/typeschema/generator.d.ts.map +1 -1
- package/dist/typeschema/generator.js +352 -0
- package/dist/typeschema/index.d.ts +14 -14
- package/dist/typeschema/index.d.ts.map +1 -1
- package/dist/typeschema/index.js +92 -0
- package/dist/typeschema/parser.d.ts +2 -2
- package/dist/typeschema/parser.d.ts.map +1 -1
- package/dist/typeschema/parser.js +310 -0
- package/dist/typeschema/profile/processor.d.ts +1 -1
- package/dist/typeschema/profile/processor.d.ts.map +1 -1
- package/dist/typeschema/profile/processor.js +268 -0
- package/dist/typeschema/schema.js +456 -0
- package/dist/typeschema/type-schema.types.js +39 -0
- package/dist/typeschema/types.js +4 -0
- package/dist/typeschema/utils.d.ts +1 -1
- package/dist/typeschema/utils.d.ts.map +1 -1
- package/dist/typeschema/utils.js +13 -0
- package/dist/typeschema/value-set/processor.d.ts +1 -1
- package/dist/typeschema/value-set/processor.d.ts.map +1 -1
- package/dist/typeschema/value-set/processor.js +168 -0
- package/dist/utils/codegen-logger.js +204 -0
- package/dist/utils.js +42 -0
- package/package.json +15 -4
- package/dist/index-e7pfye24.js +0 -8532
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* Built using the BaseGenerator architecture with TypeMapper, TemplateEngine, and FileManager.
|
|
6
6
|
*/
|
|
7
7
|
import type { TypeSchema } from "../../typeschema/type-schema.types";
|
|
8
|
-
import { BaseGenerator } from "./base/BaseGenerator";
|
|
9
|
-
import { type TypeScriptTypeMapperOptions } from "./base/TypeScriptTypeMapper";
|
|
10
|
-
import type { BaseGeneratorOptions, GeneratedFile, TemplateContext, TypeMapper } from "./base/types";
|
|
8
|
+
import { BaseGenerator } from "./base/BaseGenerator.js";
|
|
9
|
+
import { type TypeScriptTypeMapperOptions } from "./base/TypeScriptTypeMapper.js";
|
|
10
|
+
import type { BaseGeneratorOptions, GeneratedFile, TemplateContext, TypeMapper } from "./base/types.js";
|
|
11
11
|
/**
|
|
12
12
|
* TypeScript-specific generator options
|
|
13
13
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typescript.d.ts","sourceRoot":"","sources":["../../../src/api/generators/typescript.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"typescript.d.ts","sourceRoot":"","sources":["../../../src/api/generators/typescript.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAEN,KAAK,2BAA2B,EAChC,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EACX,oBAAoB,EACpB,aAAa,EACb,eAAe,EACf,UAAU,EACV,MAAM,iBAAiB,CAAC;AAEzB;;GAEG;AACH,MAAM,WAAW,0BAA2B,SAAQ,oBAAoB;IACvE,wCAAwC;IACxC,YAAY,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IAE7B,sCAAsC;IACtC,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,kCAAkC;IAClC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B,kCAAkC;IAClC,gBAAgB,CAAC,EAAE,YAAY,GAAG,WAAW,CAAC;IAE9C,8BAA8B;IAC9B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B,4BAA4B;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,0BAA0B;IAC1B,iBAAiB,CAAC,EAAE,2BAA2B,CAAC;CAChD;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,aAAa,CACrD,0BAA0B,EAC1B,aAAa,EAAE,CACf;IACA,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAG9B;IACJ,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IAEnD,OAAO,KAAK,SAAS,GAEpB;IAED,SAAS,CAAC,eAAe,IAAI,MAAM;IAInC,SAAS,CAAC,gBAAgB,IAAI,MAAM;cAIjB,gBAAgB,IAAI,UAAU;cAajC,qBAAqB,CACpC,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,eAAe,GACtB,OAAO,CAAC,MAAM,CAAC;IA0DlB,SAAS,CAAC,oBAAoB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,UAAU,EAAE;cAInD,eAAe,CAC9B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,eAAe,GACtB,OAAO,CAAC,IAAI,CAAC;IAmBhB;;OAEG;IACG,gBAAgB,CACrB,OAAO,EAAE,UAAU,EAAE,GACnB,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAajC;;OAEG;IACG,eAAe,CACpB,MAAM,EAAE,UAAU,GAChB,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IAkC3C,OAAO,CAAC,gBAAgB;IAqBxB,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,yBAAyB;IAwBjC,OAAO,CAAC,yBAAyB;IAmBjC,OAAO,CAAC,mBAAmB;IAI3B;;OAEG;IACH,OAAO,CAAC,0BAA0B;IA0DlC;;OAEG;IACH,OAAO,CAAC,2BAA2B;IAsEnC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAgC3B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAqB7B;;OAEG;IACH,OAAO,CAAC,2BAA2B;IAoCnC;;OAEG;IACH,OAAO,CAAC,eAAe;IAIvB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAsDzB;;OAEG;cACgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IAoC5D;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrC;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,0BAA0B,CAAC,GAAG,IAAI;IAI9D;;OAEG;IACH,UAAU,IAAI,0BAA0B;IAIxC;;OAEG;cACsB,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAMhE;;OAEG;YACW,qBAAqB;CA2DnC"}
|
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modern TypeScript Generator built on BaseGenerator
|
|
3
|
+
*
|
|
4
|
+
* This is the new, clean implementation that replaces the monolithic typescript.ts generator.
|
|
5
|
+
* Built using the BaseGenerator architecture with TypeMapper, TemplateEngine, and FileManager.
|
|
6
|
+
*/
|
|
7
|
+
import { BaseGenerator } from "./base/BaseGenerator.js";
|
|
8
|
+
import { TypeScriptTypeMapper, } from "./base/TypeScriptTypeMapper.js";
|
|
9
|
+
/**
|
|
10
|
+
* Modern TypeScript Generator
|
|
11
|
+
*
|
|
12
|
+
* Generates clean, type-safe TypeScript interfaces from FHIR TypeSchema documents.
|
|
13
|
+
* Uses the new BaseGenerator architecture for maintainability and extensibility.
|
|
14
|
+
*/
|
|
15
|
+
export class TypeScriptGenerator extends BaseGenerator {
|
|
16
|
+
profilesByPackage = new Map();
|
|
17
|
+
resourceTypes = new Set();
|
|
18
|
+
get tsOptions() {
|
|
19
|
+
return this.options;
|
|
20
|
+
}
|
|
21
|
+
getLanguageName() {
|
|
22
|
+
return "TypeScript";
|
|
23
|
+
}
|
|
24
|
+
getFileExtension() {
|
|
25
|
+
return ".ts";
|
|
26
|
+
}
|
|
27
|
+
createTypeMapper() {
|
|
28
|
+
const options = this.options;
|
|
29
|
+
return new TypeScriptTypeMapper({
|
|
30
|
+
namingConvention: (options.namingConvention ?? "PascalCase") === "PascalCase"
|
|
31
|
+
? "PascalCase"
|
|
32
|
+
: "camelCase",
|
|
33
|
+
moduleFormat: options.moduleFormat === "cjs" ? "commonjs" : "esm",
|
|
34
|
+
preferUndefined: true,
|
|
35
|
+
...options.typeMapperOptions,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
async generateSchemaContent(schema, context) {
|
|
39
|
+
// Skip unsupported schema types
|
|
40
|
+
if (this.shouldSkipSchema(schema)) {
|
|
41
|
+
return "";
|
|
42
|
+
}
|
|
43
|
+
// Collect resource types for Reference generic
|
|
44
|
+
if (schema.identifier.kind === "resource") {
|
|
45
|
+
this.resourceTypes.add(this.typeMapper.formatTypeName(schema.identifier.name));
|
|
46
|
+
}
|
|
47
|
+
// Update filename for profiles to include proper directory structure
|
|
48
|
+
// if (false) {
|
|
49
|
+
// // Profile support removed - not in core schema
|
|
50
|
+
// const sanitizedPackage = this.sanitizePackageName(
|
|
51
|
+
// schema.identifier.package || "unknown",
|
|
52
|
+
// );
|
|
53
|
+
// const profileFileName = this.typeMapper.formatFileName(
|
|
54
|
+
// schema.identifier.name,
|
|
55
|
+
// );
|
|
56
|
+
// context.filename = `profiles/${sanitizedPackage}/${profileFileName}`;
|
|
57
|
+
// // Track profile for index generation
|
|
58
|
+
// if (!this.profilesByPackage.has(schema.identifier.package || "unknown")) {
|
|
59
|
+
// this.profilesByPackage.set(schema.identifier.package || "unknown", []);
|
|
60
|
+
// }
|
|
61
|
+
// this.profilesByPackage.get(schema.identifier.package || "unknown")?.push({
|
|
62
|
+
// filename: profileFileName,
|
|
63
|
+
// interfaceName: this.typeMapper.formatTypeName(schema.identifier.name),
|
|
64
|
+
// });
|
|
65
|
+
// }
|
|
66
|
+
// Handle Reference type specially
|
|
67
|
+
if (schema.identifier.name === "Reference") {
|
|
68
|
+
return this.generateReferenceInterface(schema);
|
|
69
|
+
}
|
|
70
|
+
// Generate TypeScript content directly (no templates for simplicity)
|
|
71
|
+
const mainInterface = this.generateTypeScriptInterface(schema);
|
|
72
|
+
// Generate nested types if present
|
|
73
|
+
let nestedInterfaces = "";
|
|
74
|
+
if ("nested" in schema && schema.nested && Array.isArray(schema.nested)) {
|
|
75
|
+
const nestedInterfaceStrings = schema.nested.map((nestedType) => this.generateNestedTypeInterface(schema.identifier.name, nestedType));
|
|
76
|
+
nestedInterfaces = nestedInterfaceStrings.join("\n\n");
|
|
77
|
+
}
|
|
78
|
+
// Combine main interface with nested interfaces
|
|
79
|
+
if (nestedInterfaces) {
|
|
80
|
+
return `${mainInterface}\n\n${nestedInterfaces}`;
|
|
81
|
+
}
|
|
82
|
+
return mainInterface;
|
|
83
|
+
}
|
|
84
|
+
filterAndSortSchemas(schemas) {
|
|
85
|
+
return schemas.filter((schema) => !this.shouldSkipSchema(schema));
|
|
86
|
+
}
|
|
87
|
+
async validateContent(content, context) {
|
|
88
|
+
const hasValidExport = /export\s+(interface|class|type|enum)\s+\w+/.test(content);
|
|
89
|
+
const hasValidSyntax = content.includes("{") && content.includes("}");
|
|
90
|
+
if (!hasValidExport) {
|
|
91
|
+
throw new Error(`Generated content for ${context.schema.identifier.name} does not contain valid export statements`);
|
|
92
|
+
}
|
|
93
|
+
if (!hasValidSyntax) {
|
|
94
|
+
throw new Error(`Generated content for ${context.schema.identifier.name} has invalid syntax (missing braces)`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Transform multiple schemas into TypeScript
|
|
99
|
+
*/
|
|
100
|
+
async transformSchemas(schemas) {
|
|
101
|
+
const results = [];
|
|
102
|
+
for (const schema of schemas) {
|
|
103
|
+
const result = await this.transformSchema(schema);
|
|
104
|
+
if (result) {
|
|
105
|
+
results.push(result);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return results;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Transform a single schema into TypeScript
|
|
112
|
+
*/
|
|
113
|
+
async transformSchema(schema) {
|
|
114
|
+
if (this.shouldSkipSchema(schema)) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
// Create template context
|
|
118
|
+
const context = {
|
|
119
|
+
schema,
|
|
120
|
+
typeMapper: this.typeMapper,
|
|
121
|
+
filename: this.getFilenameForSchema(schema),
|
|
122
|
+
language: "TypeScript",
|
|
123
|
+
timestamp: new Date().toISOString(),
|
|
124
|
+
};
|
|
125
|
+
// Generate content using template engine
|
|
126
|
+
const content = await this.generateSchemaContent(schema, context);
|
|
127
|
+
if (!content.trim()) {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
// Extract imports and exports from generated content
|
|
131
|
+
const imports = this.extractImportsFromContent(content, schema);
|
|
132
|
+
const exports = this.extractExportsFromContent(content, schema);
|
|
133
|
+
const filename = this.getFilenameForSchema(schema);
|
|
134
|
+
return {
|
|
135
|
+
content,
|
|
136
|
+
imports,
|
|
137
|
+
exports: Array.from(exports),
|
|
138
|
+
filename,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
shouldSkipSchema(schema) {
|
|
142
|
+
if (schema.identifier.kind === "value-set" ||
|
|
143
|
+
schema.identifier.kind === "binding" ||
|
|
144
|
+
schema.identifier.kind === "primitive-type") {
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
// Profile support removed - not in core schema specification
|
|
148
|
+
if (schema.identifier.url?.includes("/extension/") &&
|
|
149
|
+
!this.tsOptions.includeExtensions) {
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
getFilenameForSchema(schema) {
|
|
155
|
+
const baseName = this.typeMapper.formatFileName(schema.identifier.name);
|
|
156
|
+
return `${baseName}${this.getFileExtension()}`;
|
|
157
|
+
}
|
|
158
|
+
extractImportsFromContent(content, _schema) {
|
|
159
|
+
const imports = new Map();
|
|
160
|
+
const importRegex = /import\s+(?:type\s+)?{\s*([^}]+)\s*}\s+from\s+['"]([^'"]+)['"];?/g;
|
|
161
|
+
let match;
|
|
162
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
163
|
+
const symbolsStr = match[1];
|
|
164
|
+
const path = match[2];
|
|
165
|
+
if (!symbolsStr || !path)
|
|
166
|
+
continue;
|
|
167
|
+
const symbols = symbolsStr.split(",").map((s) => s.trim());
|
|
168
|
+
for (const symbol of symbols) {
|
|
169
|
+
imports.set(symbol, path);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return imports;
|
|
173
|
+
}
|
|
174
|
+
extractExportsFromContent(content, schema) {
|
|
175
|
+
const exports = new Set();
|
|
176
|
+
const exportRegex = /export\s+(?:interface|class|enum|type)\s+([A-Za-z_$][A-Za-z0-9_$]*)/g;
|
|
177
|
+
let match;
|
|
178
|
+
while ((match = exportRegex.exec(content)) !== null) {
|
|
179
|
+
if (match[1])
|
|
180
|
+
exports.add(match[1]);
|
|
181
|
+
}
|
|
182
|
+
exports.add(this.typeMapper.formatTypeName(schema.identifier.name));
|
|
183
|
+
return exports;
|
|
184
|
+
}
|
|
185
|
+
sanitizePackageName(packageName) {
|
|
186
|
+
return packageName.replace(/[^a-zA-Z0-9-_.]/g, "-");
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Generate special Reference interface with generics
|
|
190
|
+
*/
|
|
191
|
+
generateReferenceInterface(schema) {
|
|
192
|
+
const lines = [];
|
|
193
|
+
const imports = new Set();
|
|
194
|
+
if ("fields" in schema && schema.fields) {
|
|
195
|
+
for (const [, field] of Object.entries(schema.fields)) {
|
|
196
|
+
const importDeps = this.collectFieldImports(field);
|
|
197
|
+
importDeps.forEach((imp) => imports.add(imp));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
lines.push("import type { ResourceType } from './utilities.js';");
|
|
201
|
+
if (imports.size > 0) {
|
|
202
|
+
const sortedImports = Array.from(imports).sort();
|
|
203
|
+
for (const importName of sortedImports) {
|
|
204
|
+
lines.push(`import type { ${importName} } from './${importName}.js';`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
lines.push(""); // Add blank line after imports
|
|
208
|
+
// Add JSDoc comment
|
|
209
|
+
if (this.tsOptions.includeDocuments && schema.description) {
|
|
210
|
+
lines.push("/**");
|
|
211
|
+
lines.push(` * ${schema.description}`);
|
|
212
|
+
if (schema.identifier.url) {
|
|
213
|
+
lines.push(` * @see ${schema.identifier.url}`);
|
|
214
|
+
}
|
|
215
|
+
if (schema.identifier.package) {
|
|
216
|
+
lines.push(` * @package ${schema.identifier.package}`);
|
|
217
|
+
}
|
|
218
|
+
lines.push(" * @template T - The resource type being referenced");
|
|
219
|
+
lines.push(" */");
|
|
220
|
+
}
|
|
221
|
+
// Generate generic interface declaration
|
|
222
|
+
lines.push("export interface Reference<T extends ResourceType = ResourceType> {");
|
|
223
|
+
if ("fields" in schema && schema.fields) {
|
|
224
|
+
for (const [fieldName, field] of Object.entries(schema.fields)) {
|
|
225
|
+
if (fieldName === "type") {
|
|
226
|
+
// Special handling for the type field to use the generic parameter
|
|
227
|
+
lines.push(" type?: T;");
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
const fieldLine = this.generateFieldLine(fieldName, field);
|
|
231
|
+
if (fieldLine) {
|
|
232
|
+
lines.push(` ${fieldLine}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
lines.push("}");
|
|
238
|
+
return lines.join("\n");
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Generate TypeScript interface directly without templates
|
|
242
|
+
*/
|
|
243
|
+
generateTypeScriptInterface(schema) {
|
|
244
|
+
const lines = [];
|
|
245
|
+
const interfaceName = this.typeMapper.formatTypeName(schema.identifier.name);
|
|
246
|
+
const imports = new Set();
|
|
247
|
+
if ("fields" in schema && schema.fields) {
|
|
248
|
+
for (const [, field] of Object.entries(schema.fields)) {
|
|
249
|
+
const importDeps = this.collectFieldImports(field);
|
|
250
|
+
importDeps.forEach((imp) => imports.add(imp));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
// Collect imports from nested types
|
|
254
|
+
if ("nested" in schema && schema.nested && Array.isArray(schema.nested)) {
|
|
255
|
+
for (const nestedType of schema.nested) {
|
|
256
|
+
if (nestedType.fields) {
|
|
257
|
+
for (const [, field] of Object.entries(nestedType.fields)) {
|
|
258
|
+
const importDeps = this.collectFieldImports(field);
|
|
259
|
+
importDeps.forEach((imp) => imports.add(imp));
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// Generate import statements
|
|
265
|
+
if (imports.size > 0) {
|
|
266
|
+
const sortedImports = Array.from(imports).sort();
|
|
267
|
+
for (const importName of sortedImports) {
|
|
268
|
+
lines.push(`import type { ${importName} } from './${importName}.js';`);
|
|
269
|
+
}
|
|
270
|
+
lines.push(""); // Add blank line after imports
|
|
271
|
+
}
|
|
272
|
+
// Add JSDoc comment if enabled
|
|
273
|
+
if (this.tsOptions.includeDocuments && schema.description) {
|
|
274
|
+
lines.push("/**");
|
|
275
|
+
lines.push(` * ${schema.description}`);
|
|
276
|
+
if (schema.identifier.url) {
|
|
277
|
+
lines.push(` * @see ${schema.identifier.url}`);
|
|
278
|
+
}
|
|
279
|
+
if (schema.identifier.package) {
|
|
280
|
+
lines.push(` * @package ${schema.identifier.package}`);
|
|
281
|
+
}
|
|
282
|
+
lines.push(" */");
|
|
283
|
+
}
|
|
284
|
+
// Generate interface declaration
|
|
285
|
+
lines.push(`export interface ${interfaceName} {`);
|
|
286
|
+
// Add resourceType for FHIR resources
|
|
287
|
+
if (schema.identifier.kind === "resource") {
|
|
288
|
+
lines.push(` resourceType: '${interfaceName}';`);
|
|
289
|
+
}
|
|
290
|
+
// Generate fields (if any)
|
|
291
|
+
if ("fields" in schema && schema.fields) {
|
|
292
|
+
for (const [fieldName, field] of Object.entries(schema.fields)) {
|
|
293
|
+
const fieldLine = this.generateFieldLine(fieldName, field);
|
|
294
|
+
if (fieldLine) {
|
|
295
|
+
lines.push(` ${fieldLine}`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
lines.push("}");
|
|
300
|
+
return lines.join("\n");
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Collect import dependencies from a field
|
|
304
|
+
*/
|
|
305
|
+
collectFieldImports(field) {
|
|
306
|
+
const imports = [];
|
|
307
|
+
if ("type" in field && field.type) {
|
|
308
|
+
// Handle nested types - they don't need imports as they're in the same file
|
|
309
|
+
if (field.type.kind === "nested") {
|
|
310
|
+
// Nested types are generated in the same file, no import needed
|
|
311
|
+
return imports;
|
|
312
|
+
}
|
|
313
|
+
const languageType = this.typeMapper.mapType(field.type);
|
|
314
|
+
// Only import non-primitive types that are not built-in
|
|
315
|
+
if (!languageType.isPrimitive && languageType.name !== "any") {
|
|
316
|
+
const builtInTypes = [
|
|
317
|
+
"string",
|
|
318
|
+
"number",
|
|
319
|
+
"boolean",
|
|
320
|
+
"Date",
|
|
321
|
+
"object",
|
|
322
|
+
"unknown",
|
|
323
|
+
"any",
|
|
324
|
+
];
|
|
325
|
+
if (!builtInTypes.includes(languageType.name)) {
|
|
326
|
+
imports.push(languageType.name);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return imports;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Extract resource types from reference field constraints
|
|
334
|
+
*/
|
|
335
|
+
extractReferenceTypes(referenceConstraints) {
|
|
336
|
+
const resourceTypes = [];
|
|
337
|
+
if (!Array.isArray(referenceConstraints)) {
|
|
338
|
+
return resourceTypes;
|
|
339
|
+
}
|
|
340
|
+
for (const constraint of referenceConstraints) {
|
|
341
|
+
if (!constraint || typeof constraint !== "object") {
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
if (constraint.kind === "resource" && constraint.name) {
|
|
345
|
+
const resourceType = this.typeMapper.formatTypeName(constraint.name);
|
|
346
|
+
resourceTypes.push(resourceType);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return [...new Set(resourceTypes)]; // Remove duplicates
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Generate nested type interface
|
|
353
|
+
*/
|
|
354
|
+
generateNestedTypeInterface(parentTypeName, nestedType) {
|
|
355
|
+
const lines = [];
|
|
356
|
+
const nestedTypeName = this.typeMapper.formatTypeName(`${parentTypeName}${this.capitalizeFirst(nestedType.identifier.name)}`);
|
|
357
|
+
// Add JSDoc comment if enabled
|
|
358
|
+
if (this.tsOptions.includeDocuments && nestedType.description) {
|
|
359
|
+
lines.push("/**");
|
|
360
|
+
lines.push(` * ${nestedType.description}`);
|
|
361
|
+
if (nestedType.identifier.url) {
|
|
362
|
+
lines.push(` * @see ${nestedType.identifier.url}`);
|
|
363
|
+
}
|
|
364
|
+
lines.push(" */");
|
|
365
|
+
}
|
|
366
|
+
// Generate interface declaration
|
|
367
|
+
lines.push(`export interface ${nestedTypeName} {`);
|
|
368
|
+
// Generate fields
|
|
369
|
+
if (nestedType.fields) {
|
|
370
|
+
for (const [fieldName, field] of Object.entries(nestedType.fields)) {
|
|
371
|
+
const fieldLine = this.generateFieldLine(fieldName, field);
|
|
372
|
+
if (fieldLine) {
|
|
373
|
+
lines.push(` ${fieldLine}`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
lines.push("}");
|
|
378
|
+
return lines.join("\n");
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Capitalize first letter of string
|
|
382
|
+
*/
|
|
383
|
+
capitalizeFirst(str) {
|
|
384
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Generate a single field line
|
|
388
|
+
*/
|
|
389
|
+
generateFieldLine(fieldName, field) {
|
|
390
|
+
let typeString = "any";
|
|
391
|
+
let required = false;
|
|
392
|
+
let isArray = false;
|
|
393
|
+
if ("type" in field && field.type) {
|
|
394
|
+
const languageType = this.typeMapper.mapType(field.type);
|
|
395
|
+
typeString = languageType.name;
|
|
396
|
+
// Handle nested types specially
|
|
397
|
+
if (field.type.kind === "nested") {
|
|
398
|
+
// Extract parent name from URL like "http://hl7.org/fhir/StructureDefinition/Patient#contact"
|
|
399
|
+
const urlParts = field.type.url?.split("#") || [];
|
|
400
|
+
if (urlParts.length === 2) {
|
|
401
|
+
const parentName = urlParts[0].split("/").pop() || "";
|
|
402
|
+
const nestedName = field.type.name;
|
|
403
|
+
typeString = this.typeMapper.formatTypeName(`${parentName}${this.capitalizeFirst(nestedName)}`);
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
typeString = this.typeMapper.formatTypeName(field.type.name);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
else if (typeString === "Reference" &&
|
|
410
|
+
field.reference &&
|
|
411
|
+
Array.isArray(field.reference)) {
|
|
412
|
+
const referenceTypes = this.extractReferenceTypes(field.reference);
|
|
413
|
+
if (referenceTypes.length > 0) {
|
|
414
|
+
referenceTypes.forEach((type) => this.resourceTypes.add(type));
|
|
415
|
+
const unionType = referenceTypes
|
|
416
|
+
.map((type) => `'${type}'`)
|
|
417
|
+
.join(" | ");
|
|
418
|
+
typeString = `Reference<${unionType}>`;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
if ("required" in field) {
|
|
423
|
+
required = field.required;
|
|
424
|
+
}
|
|
425
|
+
if ("array" in field) {
|
|
426
|
+
isArray = field.array;
|
|
427
|
+
}
|
|
428
|
+
const optional = required ? "" : "?";
|
|
429
|
+
const arrayType = isArray ? "[]" : "";
|
|
430
|
+
return `${fieldName}${optional}: ${typeString}${arrayType};`;
|
|
431
|
+
}
|
|
432
|
+
// ==========================================
|
|
433
|
+
/**
|
|
434
|
+
* Extract exported symbols from TypeScript content
|
|
435
|
+
*/
|
|
436
|
+
extractExports(content) {
|
|
437
|
+
const exports = [];
|
|
438
|
+
const exportListPattern = /export\s*\{\s*([^}]+)\s*\}/g;
|
|
439
|
+
let match;
|
|
440
|
+
while ((match = exportListPattern.exec(content)) !== null) {
|
|
441
|
+
if (match[1]) {
|
|
442
|
+
const names = match[1]
|
|
443
|
+
.split(",")
|
|
444
|
+
.map((name) => name.trim())
|
|
445
|
+
.filter(Boolean);
|
|
446
|
+
exports.push(...names);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
const directExportPatterns = [
|
|
450
|
+
/export\s+interface\s+(\w+)/g, // export interface Name
|
|
451
|
+
/export\s+type\s+(\w+)/g, // export type Name
|
|
452
|
+
/export\s+class\s+(\w+)/g, // export class Name
|
|
453
|
+
/export\s+enum\s+(\w+)/g, // export enum Name
|
|
454
|
+
/export\s+const\s+(\w+)/g, // export const name
|
|
455
|
+
/export\s+function\s+(\w+)/g, // export function name
|
|
456
|
+
];
|
|
457
|
+
for (const pattern of directExportPatterns) {
|
|
458
|
+
let match;
|
|
459
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
460
|
+
if (match[1]) {
|
|
461
|
+
exports.push(match[1]);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return [...new Set(exports)];
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Set output directory for compatibility with API builder
|
|
469
|
+
*/
|
|
470
|
+
setOutputDir(directory) {
|
|
471
|
+
this.options.outputDir = directory;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Update generator options for compatibility with API builder
|
|
475
|
+
*/
|
|
476
|
+
setOptions(options) {
|
|
477
|
+
this.options = { ...this.options, ...options };
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Get current options for compatibility with API builder
|
|
481
|
+
*/
|
|
482
|
+
getOptions() {
|
|
483
|
+
return { ...this.options };
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Run post-generation hooks - generate utility files
|
|
487
|
+
*/
|
|
488
|
+
async runPostGenerationHooks() {
|
|
489
|
+
await super.runPostGenerationHooks();
|
|
490
|
+
await this.generateUtilitiesFile();
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Generate utilities.ts file with ResourceType union
|
|
494
|
+
*/
|
|
495
|
+
async generateUtilitiesFile() {
|
|
496
|
+
if (this.resourceTypes.size === 0) {
|
|
497
|
+
this.logger.warn("No resource types found, skipping utilities.ts generation");
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
const lines = [];
|
|
501
|
+
// Add file header comment
|
|
502
|
+
lines.push("/**");
|
|
503
|
+
lines.push(" * FHIR Resource Type Utilities");
|
|
504
|
+
lines.push(" * This file contains utility types for FHIR resources.");
|
|
505
|
+
lines.push(" * ");
|
|
506
|
+
lines.push(" * @generated This file is auto-generated. Do not edit manually.");
|
|
507
|
+
lines.push(" */");
|
|
508
|
+
lines.push("");
|
|
509
|
+
// Generate ResourceType union
|
|
510
|
+
const sortedResourceTypes = Array.from(this.resourceTypes).sort();
|
|
511
|
+
lines.push("/**");
|
|
512
|
+
lines.push(" * Union of all FHIR resource types in this package");
|
|
513
|
+
lines.push(" */");
|
|
514
|
+
lines.push("export type ResourceType =");
|
|
515
|
+
for (let i = 0; i < sortedResourceTypes.length; i++) {
|
|
516
|
+
const isLast = i === sortedResourceTypes.length - 1;
|
|
517
|
+
const separator = isLast ? ";" : "";
|
|
518
|
+
lines.push(` | '${sortedResourceTypes[i]}'${separator}`);
|
|
519
|
+
}
|
|
520
|
+
lines.push("");
|
|
521
|
+
// Generate helper type for Resource references
|
|
522
|
+
lines.push("/**");
|
|
523
|
+
lines.push(" * Helper type for creating typed References");
|
|
524
|
+
lines.push(" * @example Reference<'Patient' | 'Practitioner'> - Reference that can point to Patient or Practitioner");
|
|
525
|
+
lines.push(" */");
|
|
526
|
+
lines.push("export type TypedReference<T extends ResourceType> = {");
|
|
527
|
+
lines.push(" reference?: string;");
|
|
528
|
+
lines.push(" type?: T;");
|
|
529
|
+
lines.push(" identifier?: any; // Simplified for utility");
|
|
530
|
+
lines.push(" display?: string;");
|
|
531
|
+
lines.push("};");
|
|
532
|
+
const content = lines.join("\n");
|
|
533
|
+
// Write the utilities file
|
|
534
|
+
await this.fileManager.writeFile("utilities.ts", content);
|
|
535
|
+
this.logger.info(`Generated utilities.ts with ${this.resourceTypes.size} resource types`);
|
|
536
|
+
}
|
|
537
|
+
}
|