babelfhir-ts 1.6.3 → 1.6.5
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/README.md +113 -113
- package/out/src/generator/emitters/class/classGenerator.js +2 -2
- package/out/src/generator/emitters/class/classGeneratorHelpers.js +8 -13
- package/out/src/generator/emitters/class/classTemplate.js +8 -5
- package/out/src/generator/index.js +31 -41
- package/out/src/generator/sdProcessor.js +5 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
<a href="https://www.npmjs.com/package/@babelfhir-ts/dicomweb"><img src="https://img.shields.io/npm/v/@babelfhir-ts/dicomweb.svg?label=%40babelfhir-ts%2Fdicomweb" alt="dicomweb"></a>
|
|
37
37
|
</p>
|
|
38
38
|
|
|
39
|
-
**BabelFHIR-TS** transforms FHIR® StructureDefinitions into production-ready TypeScript code with full type safety and built-in validation.
|
|
39
|
+
**BabelFHIR-TS** transforms FHIR® StructureDefinitions into production-ready TypeScript code with full type safety and built-in validation. BabelFHIR-TS generates **profile-aware** interfaces that understand your Implementation Guide's constraints, extensions, and slicing rules.
|
|
40
40
|
|
|
41
41
|
### What you get
|
|
42
42
|
|
|
@@ -48,118 +48,7 @@
|
|
|
48
48
|
- **Zero manual mapping**—consume any FHIR package or Implementation Guide directly from registries
|
|
49
49
|
- **Fast and lightweight**—the CLI pulls no FHIRPath engine of its own; generated packages declare `fhirpath` as a *peer* dependency (`>=4.9.1 <6`), so the host app owns the version
|
|
50
50
|
- **Type-safe FHIR client** — generated client extends [`@babelfhir-ts/client-r4`](https://www.npmjs.com/package/@babelfhir-ts/client-r4) / [`client-r4b`](https://www.npmjs.com/package/@babelfhir-ts/client-r4b) / [`client-r5`](https://www.npmjs.com/package/@babelfhir-ts/client-r5) with profile-specific methods (e.g., `.uSCorePatientProfile()`, `.pASClaim()`) on top of base resource accessors
|
|
51
|
-
- **Install any FHIR profile as a node module**—use `babelfhir-ts install` to add Implementation Guides directly to your project
|
|
52
|
-
|
|
53
|
-
<!-- PARITY-BADGES:START - Do not remove or modify this section -->
|
|
54
|
-
## Continuous Validation
|
|
55
|
-
|
|
56
|
-
Every push to `develop` and `main` runs the parity pipeline, which validates generated code against real-world FHIR Implementation Guides. (Pull requests run the faster `CI` workflow: typecheck, unit tests, and generated-artifact drift checks.) For each IG, the pipeline:
|
|
57
|
-
|
|
58
|
-
1. Downloads the FHIR package from a registry
|
|
59
|
-
2. Generates TypeScript interfaces, validators, and classes
|
|
60
|
-
3. Compiles the output with `tsc` (zero errors required)
|
|
61
|
-
4. Generates `empty()` and `random()` test resources for every profile
|
|
62
|
-
5. Validates those resources against two independent external FHIR validators
|
|
63
|
-
|
|
64
|
-
### Validated Implementation Guides (31)
|
|
65
|
-
|
|
66
|
-
Every parity run validates **31 real-world IGs** (29 R4, 2 R5). The list is not duplicated here — it is generated from the parity suite's own source of truth into [`parity-matrix.json`](./parity-matrix.json).
|
|
67
|
-
|
|
68
|
-
Each validated IG is also published as a ready-to-install package — `@max-health-inc/fhir-<name>` — so you do not have to generate it yourself:
|
|
69
|
-
|
|
70
|
-
**[→ Browse the FHIR IG registry](https://github.com/orgs/Max-Health-Inc/packages?ecosystem=npm&q=fhir-)**
|
|
71
|
-
|
|
72
|
-
```bash
|
|
73
|
-
# GitHub Packages needs the scope mapped, and a token with read:packages
|
|
74
|
-
echo "@max-health-inc:registry=https://npm.pkg.github.com" >> .npmrc
|
|
75
|
-
npm install @max-health-inc/fhir-us-core
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
Published by [Max-Health-Inc/fhir-igs](https://github.com/Max-Health-Inc/fhir-igs), which pins a babelfhir-ts version and republishes an IG only when the IG version, the pinned generator, or the generation flags actually change.
|
|
79
|
-
|
|
80
|
-
### Validation with the Firely SDK Validator
|
|
81
|
-
|
|
82
|
-
The `firely` job validates generated resources using the [Firely SDK Validator](https://docs.fire.ly/projects/Firely-NET-SDK/validation.html) (`Firely.Fhir.Validation`, v3.x) running on the Firely .NET SDK (`Hl7.Fhir`, v6.x) — two separately versioned packages. Results are published as live badges:
|
|
83
|
-
|
|
84
|
-
<details><summary>Per-IG results</summary>
|
|
85
|
-
|
|
86
|
-

|
|
87
|
-

|
|
88
|
-

|
|
89
|
-

|
|
90
|
-

|
|
91
|
-

|
|
92
|
-

|
|
93
|
-

|
|
94
|
-

|
|
95
|
-

|
|
96
|
-

|
|
97
|
-

|
|
98
|
-

|
|
99
|
-

|
|
100
|
-

|
|
101
|
-

|
|
102
|
-

|
|
103
|
-

|
|
104
|
-

|
|
105
|
-

|
|
106
|
-

|
|
107
|
-

|
|
108
|
-

|
|
109
|
-

|
|
110
|
-

|
|
111
|
-

|
|
112
|
-

|
|
113
|
-

|
|
114
|
-

|
|
115
|
-
|
|
116
|
-
</details>
|
|
117
|
-
|
|
118
|
-
### Validation with HL7 Java Validator
|
|
119
|
-
|
|
120
|
-
The `hl7` job validates the same artifacts using the [official HL7 FHIR Validator](https://confluence.hl7.org/display/FHIR/Using+the+FHIR+Validator), the reference implementation for FHIR conformance checking:
|
|
121
|
-
|
|
122
|
-
<details><summary>Per-IG results</summary>
|
|
123
|
-
|
|
124
|
-

|
|
125
|
-

|
|
126
|
-

|
|
127
|
-

|
|
128
|
-

|
|
129
|
-

|
|
130
|
-

|
|
131
|
-

|
|
132
|
-

|
|
133
|
-

|
|
134
|
-

|
|
135
|
-

|
|
136
|
-

|
|
137
|
-

|
|
138
|
-

|
|
139
|
-

|
|
140
|
-

|
|
141
|
-

|
|
142
|
-

|
|
143
|
-

|
|
144
|
-

|
|
145
|
-

|
|
146
|
-

|
|
147
|
-

|
|
148
|
-

|
|
149
|
-

|
|
150
|
-

|
|
151
|
-

|
|
152
|
-

|
|
153
|
-
|
|
154
|
-
</details>
|
|
155
|
-
|
|
156
|
-
> External-validator badges cover the 29 R4/R4B IGs. R5 packages are generated and typechecked, but neither external validator publishes R5 badges yet.
|
|
157
|
-
|
|
158
|
-
> Terminology validation requires a tx server. The pipeline uses `--tx-server https://tx.fhir.org/{r4|r5}` (matching the package's FHIR version) during generation to expand ValueSets and produce valid codes.
|
|
159
|
-
|
|
160
|
-
📊 **[Full Report](https://max-health-inc.github.io/BabelFHIR-TS/)**
|
|
161
|
-
<!-- HL7-PARITY-BADGES:END -->
|
|
162
|
-
<!-- PARITY-BADGES:END -->
|
|
51
|
+
- **Install any FHIR profile as a node module**—use `babelfhir-ts install` to add Implementation Guides (FHIR Packages) directly to your project
|
|
163
52
|
|
|
164
53
|
## Installation
|
|
165
54
|
|
|
@@ -274,6 +163,117 @@ Full documentation is available at **[max-health-inc.github.io/BabelFHIR-TS/docs
|
|
|
274
163
|
- [Limitations](https://max-health-inc.github.io/BabelFHIR-TS/docs/limitations) — edge cases, random() caveats
|
|
275
164
|
- [Contributing](https://max-health-inc.github.io/BabelFHIR-TS/docs/contributing) — dev setup, scripts, project structure
|
|
276
165
|
|
|
166
|
+
<!-- PARITY-BADGES:START - Do not remove or modify this section -->
|
|
167
|
+
## Continuous Validation
|
|
168
|
+
|
|
169
|
+
Every push to `develop` and `main` runs the parity pipeline, which validates generated code against real-world FHIR Implementation Guides. (Pull requests run the faster `CI` workflow: typecheck, unit tests, and generated-artifact drift checks.) For each IG, the pipeline:
|
|
170
|
+
|
|
171
|
+
1. Downloads the FHIR package from a registry
|
|
172
|
+
2. Generates TypeScript interfaces, validators, and classes
|
|
173
|
+
3. Compiles the output with `tsc` (zero errors required)
|
|
174
|
+
4. Generates `empty()` and `random()` test resources for every profile
|
|
175
|
+
5. Validates those resources against two independent external FHIR validators
|
|
176
|
+
|
|
177
|
+
### Validated Implementation Guides (31)
|
|
178
|
+
|
|
179
|
+
Every parity run validates **31 real-world IGs** (29 R4, 2 R5). The list is not duplicated here — it is generated from the parity suite's own source of truth into [`parity-matrix.json`](./parity-matrix.json).
|
|
180
|
+
|
|
181
|
+
Each validated IG is also published as a ready-to-install package — `@max-health-inc/fhir-<name>` — so you do not have to generate it yourself:
|
|
182
|
+
|
|
183
|
+
**[→ Browse the FHIR IG registry](https://github.com/orgs/Max-Health-Inc/packages?ecosystem=npm&q=fhir-)**
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# GitHub Packages needs the scope mapped, and a token with read:packages
|
|
187
|
+
echo "@max-health-inc:registry=https://npm.pkg.github.com" >> .npmrc
|
|
188
|
+
npm install @max-health-inc/fhir-us-core
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Published by [Max-Health-Inc/fhir-igs](https://github.com/Max-Health-Inc/fhir-igs), which pins a babelfhir-ts version and republishes an IG only when the IG version, the pinned generator, or the generation flags actually change.
|
|
192
|
+
|
|
193
|
+
### Validation with the Firely SDK Validator
|
|
194
|
+
|
|
195
|
+
The `firely` job validates generated resources using the [Firely SDK Validator](https://docs.fire.ly/projects/Firely-NET-SDK/validation.html) (`Firely.Fhir.Validation`, v3.x) running on the Firely .NET SDK (`Hl7.Fhir`, v6.x) — two separately versioned packages. Results are published as live badges:
|
|
196
|
+
|
|
197
|
+
<details><summary>Per-IG results</summary>
|
|
198
|
+
|
|
199
|
+

|
|
200
|
+

|
|
201
|
+

|
|
202
|
+

|
|
203
|
+

|
|
204
|
+

|
|
205
|
+

|
|
206
|
+

|
|
207
|
+

|
|
208
|
+

|
|
209
|
+

|
|
210
|
+

|
|
211
|
+

|
|
212
|
+

|
|
213
|
+

|
|
214
|
+

|
|
215
|
+

|
|
216
|
+

|
|
217
|
+

|
|
218
|
+

|
|
219
|
+

|
|
220
|
+

|
|
221
|
+

|
|
222
|
+

|
|
223
|
+

|
|
224
|
+

|
|
225
|
+

|
|
226
|
+

|
|
227
|
+

|
|
228
|
+
|
|
229
|
+
</details>
|
|
230
|
+
|
|
231
|
+
### Validation with HL7 Java Validator
|
|
232
|
+
|
|
233
|
+
The `hl7` job validates the same artifacts using the [official HL7 FHIR Validator](https://confluence.hl7.org/display/FHIR/Using+the+FHIR+Validator), the reference implementation for FHIR conformance checking:
|
|
234
|
+
|
|
235
|
+
<details><summary>Per-IG results</summary>
|
|
236
|
+
|
|
237
|
+

|
|
238
|
+

|
|
239
|
+

|
|
240
|
+

|
|
241
|
+

|
|
242
|
+

|
|
243
|
+

|
|
244
|
+

|
|
245
|
+

|
|
246
|
+

|
|
247
|
+

|
|
248
|
+

|
|
249
|
+

|
|
250
|
+

|
|
251
|
+

|
|
252
|
+

|
|
253
|
+

|
|
254
|
+

|
|
255
|
+

|
|
256
|
+

|
|
257
|
+

|
|
258
|
+

|
|
259
|
+

|
|
260
|
+

|
|
261
|
+

|
|
262
|
+

|
|
263
|
+

|
|
264
|
+

|
|
265
|
+

|
|
266
|
+
|
|
267
|
+
</details>
|
|
268
|
+
|
|
269
|
+
> External-validator badges cover the 29 R4/R4B IGs. R5 packages are generated and typechecked, but neither external validator publishes R5 badges yet.
|
|
270
|
+
|
|
271
|
+
> Terminology validation requires a tx server. The pipeline uses `--tx-server https://tx.fhir.org/{r4|r5}` (matching the package's FHIR version) during generation to expand ValueSets and produce valid codes.
|
|
272
|
+
|
|
273
|
+
📊 **[Full Report](https://max-health-inc.github.io/BabelFHIR-TS/)**
|
|
274
|
+
<!-- HL7-PARITY-BADGES:END -->
|
|
275
|
+
<!-- PARITY-BADGES:END -->
|
|
276
|
+
|
|
277
277
|
## License
|
|
278
278
|
|
|
279
279
|
ISC © Maximilian Nussbaumer
|
|
@@ -5,7 +5,7 @@ import { primitivePlaceholder, generateCodeDefault, makeCodeableConcept, chooseC
|
|
|
5
5
|
import { generateSliceElement, buildNestedRequirementsObject } from './sliceElementGenerator.js';
|
|
6
6
|
import { COMPLEX_SKELETON_MAP, propKey, buildValueExpression, buildCrossReferencedEntryArray, } from './classGeneratorHelpers.js';
|
|
7
7
|
import { renderClassTemplate } from './classTemplate.js';
|
|
8
|
-
export function generateClass(className, interfaceName, baseResource, requiredFields = [], profileUrl, fieldPatterns = [], forbiddenFields = [], valueSetBindings = {}, fieldOrder = [], codeableConceptBindings = []) {
|
|
8
|
+
export function generateClass(className, interfaceName, baseResource, requiredFields = [], profileUrl, fieldPatterns = [], forbiddenFields = [], valueSetBindings = {}, fieldOrder = [], codeableConceptBindings = [], hasValueSetRegistry = false) {
|
|
9
9
|
// Decide whether to emit resourceType. If baseResource points at a known resource or ends with 'Resource'.
|
|
10
10
|
const CORE_RESOURCE_BASES = ctx().coreResourceBases;
|
|
11
11
|
const addResourceType = !!baseResource && CORE_RESOURCE_BASES.has(baseResource);
|
|
@@ -578,6 +578,6 @@ export function generateClass(className, interfaceName, baseResource, requiredFi
|
|
|
578
578
|
profileUrl, constructorMetaProfile, setterMetaProfile,
|
|
579
579
|
randomMetaProfileInit, resourceTypeLine, baseResource,
|
|
580
580
|
fieldPatterns, forbiddenFields, valueSetBindings, fieldOrder,
|
|
581
|
-
codeableConceptBindings,
|
|
581
|
+
codeableConceptBindings, hasValueSetRegistry,
|
|
582
582
|
});
|
|
583
583
|
}
|
|
@@ -93,12 +93,13 @@ export function buildCrossReferencedEntryArray(sliceElements) {
|
|
|
93
93
|
return `(() => { ${decls.join(' ')} return [${rewritten.join(', ')}]; })()`;
|
|
94
94
|
}
|
|
95
95
|
/** Generate the static pattern/forbidden/order/binding metadata properties for a class. */
|
|
96
|
-
export function generatePatternMetadata(fieldPatterns, forbiddenFields, valueSetBindings, fieldOrder = [], codeableConceptBindings = []
|
|
96
|
+
export function generatePatternMetadata(fieldPatterns, forbiddenFields, valueSetBindings, fieldOrder = [], codeableConceptBindings = [],
|
|
97
|
+
/** Whether the class file imports `getRandomConceptByUrl` — see renderClassTemplate. */
|
|
98
|
+
emitCodeResolver = false) {
|
|
97
99
|
const patternMap = {};
|
|
98
100
|
for (const fp of fieldPatterns) {
|
|
99
101
|
patternMap[fp.fieldName] = fp.pattern;
|
|
100
102
|
}
|
|
101
|
-
const hasBindings = Object.keys(valueSetBindings).length > 0;
|
|
102
103
|
return `
|
|
103
104
|
/**
|
|
104
105
|
* Pattern constraints this profile fixes on its fields, keyed by field path
|
|
@@ -127,16 +128,10 @@ export function generatePatternMetadata(fieldPatterns, forbiddenFields, valueSet
|
|
|
127
128
|
*/
|
|
128
129
|
protected static readonly _codeableConceptBindings: string[] = ${JSON.stringify(codeableConceptBindings)};
|
|
129
130
|
|
|
130
|
-
/**
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (registry && typeof registry.getRandomConceptByUrl === 'function') {
|
|
136
|
-
return registry.getRandomConceptByUrl as (url: string) => { code: string; system: string; display?: string } | undefined;
|
|
137
|
-
}
|
|
138
|
-
} catch { /* ValueSetRegistry not available */ }
|
|
139
|
-
return undefined;
|
|
140
|
-
})()` : 'undefined'};
|
|
131
|
+
/**
|
|
132
|
+
* Random-example code resolver for ValueSet bindings. \`undefined\` when this package emitted no
|
|
133
|
+
* ValueSet registry; enrichResource and resolveSliceCode both fall back to a placeholder.
|
|
134
|
+
*/
|
|
135
|
+
protected static readonly _codeResolver: ((url: string) => { code: string; system: string; display?: string } | undefined) | undefined = ${emitCodeResolver ? 'getRandomConceptByUrl' : 'undefined'};
|
|
141
136
|
`;
|
|
142
137
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { COMPLEX_SKELETON_MAP, generatePatternMetadata } from './classGeneratorHelpers.js';
|
|
2
2
|
/** Render the final class file content from pre-computed template parameters. */
|
|
3
3
|
export function renderClassTemplate(params) {
|
|
4
|
-
const { interfaceName, className, requiredInitBlock, enableMetaProfile, profileUrl, constructorMetaProfile, setterMetaProfile, randomMetaProfileInit, resourceTypeLine, baseResource, fieldPatterns, forbiddenFields, valueSetBindings, fieldOrder, codeableConceptBindings, } = params;
|
|
4
|
+
const { interfaceName, className, requiredInitBlock, enableMetaProfile, profileUrl, constructorMetaProfile, setterMetaProfile, randomMetaProfileInit, resourceTypeLine, baseResource, fieldPatterns, forbiddenFields, valueSetBindings, fieldOrder, codeableConceptBindings, hasValueSetRegistry, } = params;
|
|
5
5
|
// Determine which skeleton helpers are actually used in requiredInitBlock
|
|
6
6
|
const usedSkeletons = new Set();
|
|
7
7
|
for (const helper of Object.values(COMPLEX_SKELETON_MAP)) {
|
|
@@ -24,14 +24,17 @@ export function renderClassTemplate(params) {
|
|
|
24
24
|
helperImports.add(helper);
|
|
25
25
|
}
|
|
26
26
|
const helperImportList = Array.from(helperImports).join(', ');
|
|
27
|
-
|
|
27
|
+
// Both halves needed: bindings can all point at ValueSets this package does not emit, and a
|
|
28
|
+
// registry with no bindings is dead weight.
|
|
29
|
+
const emitCodeResolver = Object.keys(valueSetBindings).length > 0 && hasValueSetRegistry;
|
|
28
30
|
return `
|
|
29
31
|
import { validate${interfaceName}, type ${interfaceName} } from "./${interfaceName}.js";
|
|
30
32
|
import type { ValidatorOptions } from "./ValidatorOptions.js";
|
|
31
33
|
// Only import helpers actually referenced below (dynamic based on required fields)
|
|
32
34
|
import { ${helperImportList} } from "./random/index.js";
|
|
33
|
-
${
|
|
34
|
-
|
|
35
|
+
${emitCodeResolver ? `// Static on purpose: a Node-only runtime require here dies at module init under any bundler
|
|
36
|
+
// that strips module metadata, which kept this package off Workers and Deno.
|
|
37
|
+
import { getRandomConceptByUrl } from "./valuesets/index.js";` : ''}
|
|
35
38
|
|
|
36
39
|
// Helper emitted only when meta.profile enforcement is applicable (resource profiles)
|
|
37
40
|
${enableMetaProfile ? `type WithMeta = { meta?: { profile?: string[] } };
|
|
@@ -54,7 +57,7 @@ function safeClone<T>(value: T): T {
|
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
export class ${className} {
|
|
57
|
-
${generatePatternMetadata(fieldPatterns, forbiddenFields, valueSetBindings, fieldOrder, codeableConceptBindings)}
|
|
60
|
+
${generatePatternMetadata(fieldPatterns, forbiddenFields, valueSetBindings, fieldOrder, codeableConceptBindings, emitCodeResolver)}
|
|
58
61
|
private resource: ${interfaceName};
|
|
59
62
|
|
|
60
63
|
// Declared and assigned rather than written as a parameter property, which
|
|
@@ -128,33 +128,41 @@ function resolveGeneratorTypeRoots() {
|
|
|
128
128
|
}
|
|
129
129
|
return [];
|
|
130
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* The tsconfig generated output is compiled with.
|
|
133
|
+
*
|
|
134
|
+
* `types: []` is load-bearing and pairs with the emitters: generated code imports no node
|
|
135
|
+
* builtins, so naming a type package here is a promise to ship one. `['node']` was that promise,
|
|
136
|
+
* kept by writing an @types/node stub into the output; both go together or neither does.
|
|
137
|
+
*/
|
|
138
|
+
export function buildOutputTsconfig() {
|
|
139
|
+
return {
|
|
140
|
+
compilerOptions: {
|
|
141
|
+
target: 'ES2020',
|
|
142
|
+
module: 'ESNext',
|
|
143
|
+
moduleResolution: 'Bundler',
|
|
144
|
+
declaration: true,
|
|
145
|
+
outDir: '.',
|
|
146
|
+
rootDir: '.',
|
|
147
|
+
skipLibCheck: true,
|
|
148
|
+
esModuleInterop: true,
|
|
149
|
+
allowSyntheticDefaultImports: true,
|
|
150
|
+
strict: true,
|
|
151
|
+
resolveJsonModule: true,
|
|
152
|
+
// Relative to the output dir; the generator's own @types is appended absolute so
|
|
153
|
+
// `@types/fhir` resolves even when the output's deps were skipped.
|
|
154
|
+
typeRoots: ['./node_modules/@types', ...resolveGeneratorTypeRoots()],
|
|
155
|
+
types: []
|
|
156
|
+
},
|
|
157
|
+
include: ['./**/*.ts'],
|
|
158
|
+
exclude: ['node_modules']
|
|
159
|
+
};
|
|
160
|
+
}
|
|
131
161
|
/** Compile TypeScript files to JavaScript with declaration files */
|
|
132
162
|
async function compileTypeScriptToJS(dir) {
|
|
133
163
|
return new Promise((resolve, reject) => {
|
|
134
164
|
const tscCmd = process.platform === 'win32' ? 'tsc.cmd' : 'tsc';
|
|
135
|
-
|
|
136
|
-
// Paths are relative to tsconfig location (which is `dir` itself); the
|
|
137
|
-
// generator's own @types (absolute) is appended so `@types/fhir` resolves
|
|
138
|
-
// even when the output's deps were skipped (see resolveGeneratorTypeRoots).
|
|
139
|
-
const tsconfig = {
|
|
140
|
-
compilerOptions: {
|
|
141
|
-
target: 'ES2020',
|
|
142
|
-
module: 'ESNext',
|
|
143
|
-
moduleResolution: 'Bundler',
|
|
144
|
-
declaration: true,
|
|
145
|
-
outDir: '.',
|
|
146
|
-
rootDir: '.',
|
|
147
|
-
skipLibCheck: true,
|
|
148
|
-
esModuleInterop: true,
|
|
149
|
-
allowSyntheticDefaultImports: true,
|
|
150
|
-
strict: true,
|
|
151
|
-
resolveJsonModule: true,
|
|
152
|
-
typeRoots: ['./node_modules/@types', ...resolveGeneratorTypeRoots()],
|
|
153
|
-
types: ['node']
|
|
154
|
-
},
|
|
155
|
-
include: ['./**/*.ts'],
|
|
156
|
-
exclude: ['node_modules']
|
|
157
|
-
};
|
|
165
|
+
const tsconfig = buildOutputTsconfig();
|
|
158
166
|
const tsconfigPath = path.join(dir, 'tsconfig.temp.json');
|
|
159
167
|
fs.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2));
|
|
160
168
|
// shell required on Windows for .cmd wrappers; disabled on Unix to prevent injection
|
|
@@ -611,11 +619,6 @@ export async function generateIntoPackage(packageArchivePath, outArchivePath, fl
|
|
|
611
619
|
// Install fhirpath type stub so tsc can resolve validator imports
|
|
612
620
|
const { installFhirpathStub, removeFhirpathStub } = await import('./emitters/validator/fhirpathStubInstaller.js');
|
|
613
621
|
installFhirpathStub(outputDir);
|
|
614
|
-
// Install minimal @types/node stub so tsc can resolve `import { createRequire } from 'module'`
|
|
615
|
-
const nodeTypesDir = path.join(outputDir, 'node_modules', '@types', 'node');
|
|
616
|
-
fs.mkdirSync(nodeTypesDir, { recursive: true });
|
|
617
|
-
fs.writeFileSync(path.join(nodeTypesDir, 'index.d.ts'), `declare module 'module' {\n export function createRequire(filename: string | URL): NodeRequire;\n}\n`);
|
|
618
|
-
fs.writeFileSync(path.join(nodeTypesDir, 'package.json'), JSON.stringify({ name: '@types/node', version: '0.0.0-stub', types: 'index.d.ts' }));
|
|
619
622
|
// Compile TypeScript to JavaScript
|
|
620
623
|
endPhase = startPhase('compile');
|
|
621
624
|
logger.log('Compiling TypeScript to JavaScript...');
|
|
@@ -623,11 +626,6 @@ export async function generateIntoPackage(packageArchivePath, outArchivePath, fl
|
|
|
623
626
|
endPhase(); // compile
|
|
624
627
|
// Remove fhirpath stub — the real package is a peer dependency
|
|
625
628
|
removeFhirpathStub(outputDir);
|
|
626
|
-
// Remove @types/node stub — only needed for compilation
|
|
627
|
-
try {
|
|
628
|
-
fs.rmSync(path.join(outputDir, 'node_modules', '@types'), { recursive: true, force: true });
|
|
629
|
-
}
|
|
630
|
-
catch { /* ignore */ }
|
|
631
629
|
// Remove base client type stubs — they were only needed for tsc to resolve
|
|
632
630
|
// @babelfhir-ts/client-<version> imports during compilation. The real package is
|
|
633
631
|
// installed by the consumer via npm.
|
|
@@ -843,19 +841,11 @@ export async function generateIntoPackageDirect(packageArchivePath, flags) {
|
|
|
843
841
|
}
|
|
844
842
|
const { installFhirpathStub, removeFhirpathStub } = await import('./emitters/validator/fhirpathStubInstaller.js');
|
|
845
843
|
installFhirpathStub(outputDir);
|
|
846
|
-
const nodeTypesDir = path.join(outputDir, 'node_modules', '@types', 'node');
|
|
847
|
-
fs.mkdirSync(nodeTypesDir, { recursive: true });
|
|
848
|
-
fs.writeFileSync(path.join(nodeTypesDir, 'index.d.ts'), `declare module 'module' {\n export function createRequire(filename: string | URL): NodeRequire;\n}\n`);
|
|
849
|
-
fs.writeFileSync(path.join(nodeTypesDir, 'package.json'), JSON.stringify({ name: '@types/node', version: '0.0.0-stub', types: 'index.d.ts' }));
|
|
850
844
|
endPhase = startPhase('compile');
|
|
851
845
|
logger.log('Compiling TypeScript to JavaScript...');
|
|
852
846
|
await compileTypeScriptToJS(outputDir);
|
|
853
847
|
endPhase(); // compile
|
|
854
848
|
removeFhirpathStub(outputDir);
|
|
855
|
-
try {
|
|
856
|
-
fs.rmSync(path.join(outputDir, 'node_modules', '@types'), { recursive: true, force: true });
|
|
857
|
-
}
|
|
858
|
-
catch { /* ignore */ }
|
|
859
849
|
const babelfhirDir = path.join(outputDir, 'node_modules', '@babelfhir-ts');
|
|
860
850
|
if (fs.existsSync(babelfhirDir)) {
|
|
861
851
|
for (const entry of fs.readdirSync(babelfhirDir)) {
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* Helpers and types live in sdProcessorHelpers.ts.
|
|
7
7
|
* Required-field dedup lives in sdProcessorRequiredFields.ts.
|
|
8
8
|
*/
|
|
9
|
+
import fs from 'fs';
|
|
9
10
|
import path from 'path';
|
|
10
11
|
import { generateClass } from './emitters/class/classGenerator.js';
|
|
11
12
|
import { generateInterfaces } from './emitters/interface/interfaceGenerator.js';
|
|
@@ -733,7 +734,10 @@ export async function processStructureDefinition(sd, ctx) {
|
|
|
733
734
|
}
|
|
734
735
|
// ── Emit artifacts ───────────────────────────────────────────────
|
|
735
736
|
const { fieldPatterns, forbiddenFields, valueSetBindings, codeableConceptBindings, fieldOrder } = await extractFieldPatternsAndMetadata([parsed.newFields || [], aligned.alignedNewFields || [], parsed.oldFields || []], baseResource, sd, requiredFields, candidateRequiredFields, nestedRequirementsMap, resolvedProfilePatterns);
|
|
736
|
-
|
|
737
|
+
// Read off disk, not threaded through ctx: emitValueSetFiles has already run by now, and ten
|
|
738
|
+
// call sites would otherwise carry a flag most of them always pass false.
|
|
739
|
+
const hasValueSetRegistry = fs.existsSync(path.join(outputDir, 'valuesets', 'index.ts'));
|
|
740
|
+
const classContent = generateClass(`${interfaceName}Class`, interfaceName, baseResource, requiredFields, sd.url, fieldPatterns, forbiddenFields, valueSetBindings, fieldOrder, codeableConceptBindings, hasValueSetRegistry);
|
|
737
741
|
ensureRandomSupportFile(outputDir);
|
|
738
742
|
ensureValidatorOptionsFile(outputDir);
|
|
739
743
|
writeInterfaceAndValidatorToFile(outputDir, interfaceName, finalInterfaceContent, validateFn);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "babelfhir-ts",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.5",
|
|
4
4
|
"description": "BabelFHIR-TS: generate TypeScript interfaces, validators, and helper classes from FHIR R4/R4B/R5 StructureDefinitions (profiles) directly inside package archives.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "out/src/main.js",
|