44 * Core transformation logic for converting FHIRSchema to TypeSchema format
55 */
66
7+ import assert from "node:assert" ;
78import type { FHIRSchemaElement } from "@atomic-ehr/fhirschema" ;
89import { shouldSkipCanonical } from "@root/typeschema/skip-hack" ;
910import type { CodegenLog } from "@root/utils/log" ;
@@ -12,12 +13,15 @@ import {
1213 concatIdentifiers ,
1314 extractExtensionDeps ,
1415 type Field ,
16+ type Identifier ,
1517 isNestedIdentifier ,
1618 isProfileIdentifier ,
1719 type NestedTypeSchema ,
20+ type ProfileIdentifier ,
1821 packageMetaToFhir ,
1922 type RichFHIRSchema ,
2023 type RichValueSet ,
24+ type SpecializationTypeSchema ,
2125 type TypeIdentifier ,
2226 type TypeSchema ,
2327 type ValueSetTypeSchema ,
@@ -92,33 +96,50 @@ export async function transformValueSet(
9296 } ;
9397}
9498
95- export function extractDependencies (
96- identifier : TypeIdentifier ,
99+ const collectRawDeps = (
97100 base : TypeIdentifier | undefined ,
98101 fields : Record < string , Field > | undefined ,
99102 nestedTypes : NestedTypeSchema [ ] | undefined ,
100- ) : TypeIdentifier [ ] | undefined {
101- const deps = [ ] ;
103+ ) : TypeIdentifier [ ] => {
104+ const deps : TypeIdentifier [ ] = [ ] ;
102105 if ( base ) deps . push ( base ) ;
103106 if ( fields ) deps . push ( ...extractFieldDependencies ( fields ) ) ;
104107 if ( nestedTypes ) deps . push ( ...extractNestedDependencies ( nestedTypes ) ) ;
108+ return deps ;
109+ } ;
105110
106- const localNestedTypeUrls = new Set ( nestedTypes ?. map ( ( nt ) => nt . identifier . url ) ) ;
111+ export const extractDependencies = (
112+ identifier : Identifier ,
113+ base : TypeIdentifier | undefined ,
114+ fields : Record < string , Field > | undefined ,
115+ nestedTypes : NestedTypeSchema [ ] | undefined ,
116+ ) : Identifier [ ] | undefined => {
117+ const deps = collectRawDeps ( base , fields , nestedTypes ) ;
107118
108- const filtered = deps . filter ( ( dep ) => {
119+ const filtered = deps . filter ( ( dep ) : dep is Identifier => {
109120 if ( dep . url === identifier . url ) return false ;
110- if ( isProfileIdentifier ( identifier ) ) return true ;
111- if ( ! isNestedIdentifier ( dep ) ) return true ;
112- return ! localNestedTypeUrls . has ( dep . url ) ;
121+ if ( isNestedIdentifier ( dep ) ) return false ;
122+ return true ;
113123 } ) ;
114124
115125 return concatIdentifiers ( filtered ) ;
116- }
126+ } ;
127+
128+ export const extractProfileDependencies = (
129+ identifier : ProfileIdentifier ,
130+ base : TypeIdentifier | undefined ,
131+ fields : Record < string , Field > | undefined ,
132+ nestedTypes : NestedTypeSchema [ ] | undefined ,
133+ ) : TypeIdentifier [ ] | undefined => {
134+ const deps = collectRawDeps ( base , fields , nestedTypes ) ;
135+ const filtered = deps . filter ( ( dep ) => dep . url !== identifier . url ) ;
136+ return concatIdentifiers ( filtered ) ;
137+ } ;
117138
118139export function transformFhirSchema ( register : Register , fhirSchema : RichFHIRSchema , logger ?: CodegenLog ) : TypeSchema [ ] {
119140 const identifier = mkIdentifier ( fhirSchema ) ;
120141
121- let base : TypeIdentifier | undefined ;
142+ let base : Identifier | undefined ;
122143 if ( fhirSchema . base ) {
123144 const baseFs = register . resolveFs (
124145 fhirSchema . package_meta ,
@@ -128,27 +149,44 @@ export function transformFhirSchema(register: Register, fhirSchema: RichFHIRSche
128149 throw new Error (
129150 `Base resource not found '${ fhirSchema . base } ' for <${ fhirSchema . url } > from ${ packageMetaToFhir ( fhirSchema . package_meta ) } ` ,
130151 ) ;
131- base = mkIdentifier ( baseFs ) ;
152+ const baseId = mkIdentifier ( baseFs ) ;
153+ assert ( ! isNestedIdentifier ( baseId ) , `Unexpected nested base for ${ fhirSchema . url } ` ) ;
154+ base = baseId ;
132155 }
133156
134157 const fields = mkFields ( register , fhirSchema , [ ] , fhirSchema . elements , logger ) ;
135158 const nested = mkNestedTypes ( register , fhirSchema , logger ) ;
136159
137- const extensions =
138- fhirSchema . derivation === "constraint" ? extractProfileExtensions ( register , fhirSchema , logger ) : undefined ;
139- const extensionDeps = extensions ?. flatMap ( extractExtensionDeps ) ;
140- const dependencies = concatIdentifiers ( extractDependencies ( identifier , base , fields , nested ) , extensionDeps ) ;
141-
142- const typeSchema : TypeSchema = {
143- identifier,
144- base,
145- fields,
146- nested,
147- description : fhirSchema . description ,
148- dependencies,
149- extensions,
150- typeFamily : undefined , // NOTE: should be populateTypeFamily later.
151- } ;
160+ let typeSchema : TypeSchema ;
161+ if ( fhirSchema . derivation === "constraint" ) {
162+ if ( ! base ) throw new Error ( `Profile ${ fhirSchema . url } must have a base type` ) ;
163+ assert ( isProfileIdentifier ( identifier ) ) ;
164+ const extensions = extractProfileExtensions ( register , fhirSchema , logger ) ;
165+ const extensionDeps = extensions ?. flatMap ( extractExtensionDeps ) ;
166+ const rawDeps = extractProfileDependencies ( identifier , base , fields , nested ) ;
167+ typeSchema = {
168+ identifier,
169+ base,
170+ fields,
171+ nested,
172+ description : fhirSchema . description ,
173+ dependencies : concatIdentifiers ( rawDeps , extensionDeps ) ,
174+ extensions,
175+ } ;
176+ } else {
177+ assert ( ! isNestedIdentifier ( identifier ) , `Unexpected nested identifier for ${ fhirSchema . url } ` ) ;
178+ const rawDeps = extractDependencies ( identifier , base , fields , nested ) ;
179+ const specialization : SpecializationTypeSchema = {
180+ identifier,
181+ base,
182+ fields,
183+ nested,
184+ description : fhirSchema . description ,
185+ dependencies : rawDeps ,
186+ typeFamily : undefined , // NOTE: should be populateTypeFamily later.
187+ } ;
188+ typeSchema = specialization ;
189+ }
152190
153191 const bindingSchemas = collectBindingSchemas ( register , fhirSchema , logger ) ;
154192 return [ typeSchema , ...bindingSchemas ] ;
0 commit comments