From 2733f4e10e0fb6234ef622397ff847fc402db0e2 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 17 Dec 2025 10:26:50 +0100 Subject: [PATCH 1/4] Add `selectFields` to treeShake rule to specify only required fields. --- src/typeschema/utils.ts | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/typeschema/utils.ts b/src/typeschema/utils.ts index e38b5b24d..2b7896520 100644 --- a/src/typeschema/utils.ts +++ b/src/typeschema/utils.ts @@ -44,7 +44,7 @@ export const groupByPackages = (typeSchemas: TypeSchema[]) => { return grouped; }; -export type TypeSchemaShakeRule = { ignoreFields?: string[] }; +export type TypeSchemaShakeRule = { ignoreFields?: string[]; selectFields?: string[] }; export type TreeShake = Record>; @@ -56,10 +56,24 @@ export const treeShakeTypeSchema = ( schema = structuredClone(schema); if (isPrimitiveTypeSchema(schema) || isValueSetTypeSchema(schema) || isBindingSchema(schema)) return schema; - for (const fieldName of rule.ignoreFields ?? []) { - if (schema.fields && !schema.fields[fieldName]) throw new Error(`Field ${fieldName} not found`); - if (schema.fields) { - delete schema.fields[fieldName]; + if (rule.selectFields) { + if (rule.ignoreFields) throw new Error("Cannot use both ignoreFields and selectFields in the same rule"); + + const selectedFields: Record = {}; + for (const fieldName of rule.selectFields) { + if (!schema.fields || !schema.fields[fieldName]) throw new Error(`Field ${fieldName} not found`); + selectedFields[fieldName] = schema.fields[fieldName]; + } + schema.fields = selectedFields; + } + + if (rule.ignoreFields) { + if (rule.selectFields) throw new Error("Cannot use both ignoreFields and selectFields in the same rule"); + for (const fieldName of rule.ignoreFields) { + if (schema.fields && !schema.fields[fieldName]) throw new Error(`Field ${fieldName} not found`); + if (schema.fields) { + delete schema.fields[fieldName]; + } } } From 2b8993018c8615596cc6b833f046f194ae57cb63 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 17 Dec 2025 11:20:56 +0100 Subject: [PATCH 2/4] Fix: treeShake also shake out not used nested types. --- src/typeschema/utils.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/typeschema/utils.ts b/src/typeschema/utils.ts index 2b7896520..44d7cf520 100644 --- a/src/typeschema/utils.ts +++ b/src/typeschema/utils.ts @@ -12,11 +12,13 @@ import { isComplexTypeTypeSchema, isLogicalTypeSchema, isNestedIdentifier, + isNotChoiceDeclarationField, isPrimitiveTypeSchema, isProfileTypeSchema, isResourceTypeSchema, isSpecializationTypeSchema, isValueSetTypeSchema, + type NestedType, type ProfileTypeSchema, type RegularTypeSchema, type TypeSchema, @@ -77,8 +79,16 @@ export const treeShakeTypeSchema = ( } } - schema.dependencies = extractDependencies(schema.identifier, schema.base, schema.fields, schema.nested); + if (schema.nested) { + const usedTypes = new Set( + Object.values(schema.fields ?? {}) + .filter(isNotChoiceDeclarationField) + .map((f) => f.type.url), + ); + schema.nested = schema.nested.filter((n) => usedTypes.has(n.identifier.url)); + } + schema.dependencies = extractDependencies(schema.identifier, schema.base, schema.fields, schema.nested); return schema; }; From 63b2256cb38baa71e387ec7900740011b75204cc Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Wed, 17 Dec 2025 13:20:40 +0100 Subject: [PATCH 3/4] Fix: treeshake and polimorphic fields. Add tests. Refactoring. --- src/api/builder.ts | 3 +- src/typeschema/tree-shake.ts | 172 ++ src/typeschema/types.ts | 5 + src/typeschema/utils.ts | 100 -- .../__snapshots__/tree-shake.test.ts.snap | 1570 +++++++++++++++++ test/unit/typeschema/tree-shake.test.ts | 210 +++ test/unit/typeschema/utils.ts | 6 + 7 files changed, 1965 insertions(+), 101 deletions(-) create mode 100644 src/typeschema/tree-shake.ts create mode 100644 test/unit/typeschema/__snapshots__/tree-shake.test.ts.snap create mode 100644 test/unit/typeschema/tree-shake.test.ts diff --git a/src/api/builder.ts b/src/api/builder.ts index cafe1f2b4..d11e5a014 100644 --- a/src/api/builder.ts +++ b/src/api/builder.ts @@ -13,7 +13,8 @@ import { CSharp } from "@root/api/writer-generator/csharp/csharp.ts"; import { Python, type PythonGeneratorOptions } from "@root/api/writer-generator/python"; import { generateTypeSchemas } from "@root/typeschema"; import { registerFromManager } from "@root/typeschema/register"; -import { mkTypeSchemaIndex, type TreeShake, type TypeSchemaIndex, treeShake } from "@root/typeschema/utils"; +import { type TreeShake, treeShake } from "@root/typeschema/tree-shake"; +import { mkTypeSchemaIndex, type TypeSchemaIndex } from "@root/typeschema/utils"; import { extractNameFromCanonical, type PackageMeta, diff --git a/src/typeschema/tree-shake.ts b/src/typeschema/tree-shake.ts new file mode 100644 index 000000000..338134825 --- /dev/null +++ b/src/typeschema/tree-shake.ts @@ -0,0 +1,172 @@ +import assert from "node:assert"; +import type { CodegenLogger } from "@root/utils/codegen-logger"; +import { extractDependencies } from "./core/transformer"; +import type { ResolutionTree } from "./register"; +import { + type CanonicalUrl, + type Field, + isBindingSchema, + isChoiceDeclarationField, + isChoiceInstanceField, + isNestedIdentifier, + isNotChoiceDeclarationField, + isPrimitiveTypeSchema, + isSpecializationTypeSchema, + isValueSetTypeSchema, + type NestedType, + type RegularTypeSchema, + type TypeSchema, +} from "./types"; +import { mkTypeSchemaIndex, type TypeSchemaIndex } from "./utils"; + +export type TreeShake = Record>; + +export type TreeShakeRule = { ignoreFields?: string[]; selectFields?: string[] }; + +const mutableSelectFields = (schema: RegularTypeSchema, selectFields: string[]) => { + const selectedFields: Record = {}; + + const selectPolimorphic: Record = {}; + for (const fieldName of selectFields) { + const field = schema.fields?.[fieldName]; + if (!schema.fields || !field) throw new Error(`Field ${fieldName} not found`); + + if (isChoiceDeclarationField(field)) { + if (!selectPolimorphic[fieldName]) selectPolimorphic[fieldName] = {}; + selectPolimorphic[fieldName].declaration = field.choices; + } else if (isChoiceInstanceField(field)) { + const choiceName = field.choiceOf; + if (!selectPolimorphic[choiceName]) selectPolimorphic[choiceName] = {}; + selectPolimorphic[choiceName].instances = [...(selectPolimorphic[choiceName].instances ?? []), fieldName]; + } else { + selectedFields[fieldName] = field; + } + } + + for (const [choiceName, { declaration, instances }] of Object.entries(selectPolimorphic)) { + const choices = instances ?? declaration; + assert(choices); + for (const choiceInstanceName of choices) { + const field = schema.fields?.[choiceInstanceName]; + assert(field); + selectedFields[choiceInstanceName] = field; + } + const decl = schema.fields?.[choiceName]; + assert(decl); + selectedFields[choiceName] = { ...decl, choices: choices }; + } + schema.fields = selectedFields; +}; + +const mutableIgnoreFields = (schema: RegularTypeSchema, ignoreFields: string[]) => { + for (const fieldName of ignoreFields) { + const field = schema.fields?.[fieldName]; + if (!schema.fields || !field) throw new Error(`Field ${fieldName} not found`); + if (schema.fields) { + if (isChoiceDeclarationField(field)) { + for (const choiceName of field.choices) { + delete schema.fields[choiceName]; + } + } + + if (isChoiceInstanceField(field)) { + const choiceDeclaration = schema.fields[field.choiceOf]; + assert(isChoiceDeclarationField(choiceDeclaration)); + choiceDeclaration.choices = choiceDeclaration.choices.filter((c) => c !== fieldName); + if (choiceDeclaration.choices.length === 0) { + delete schema.fields[field.choiceOf]; + } + } + + delete schema.fields[fieldName]; + } + } +}; + +export const treeShakeTypeSchema = (schema: TypeSchema, rule: TreeShakeRule, _logger?: CodegenLogger): TypeSchema => { + schema = structuredClone(schema); + if (isPrimitiveTypeSchema(schema) || isValueSetTypeSchema(schema) || isBindingSchema(schema)) return schema; + + if (rule.selectFields) { + if (rule.ignoreFields) throw new Error("Cannot use both ignoreFields and selectFields in the same rule"); + mutableSelectFields(schema, rule.selectFields); + } + + if (rule.ignoreFields) { + if (rule.selectFields) throw new Error("Cannot use both ignoreFields and selectFields in the same rule"); + mutableIgnoreFields(schema, rule.ignoreFields); + } + + if (schema.nested) { + const usedTypes = new Set(); + const collectUsedNestedTypes = (s: RegularTypeSchema | NestedType) => { + Object.values(s.fields ?? {}) + .filter(isNotChoiceDeclarationField) + .filter((f) => isNestedIdentifier(f.type)) + .forEach((f) => { + const url = f.type.url; + if (!usedTypes.has(url)) { + usedTypes.add(url); + const nestedTypeDef = schema.nested?.find((f) => f.identifier.url === url); + assert(nestedTypeDef); + collectUsedNestedTypes(nestedTypeDef); + } + }); + }; + collectUsedNestedTypes(schema); + schema.nested = schema.nested.filter((n) => usedTypes.has(n.identifier.url)); + } + + schema.dependencies = extractDependencies(schema.identifier, schema.base, schema.fields, schema.nested); + return schema; +}; + +export const treeShake = ( + tsIndex: TypeSchemaIndex, + treeShake: TreeShake, + { resolutionTree, logger }: { resolutionTree?: ResolutionTree; logger?: CodegenLogger }, +): TypeSchemaIndex => { + const focusedSchemas: TypeSchema[] = []; + for (const [pkgId, requires] of Object.entries(treeShake)) { + for (const [url, rule] of Object.entries(requires)) { + const schema = tsIndex.resolveByUrl(pkgId, url as CanonicalUrl); + if (!schema) throw new Error(`Schema not found for ${pkgId} ${url}`); + const shaked = treeShakeTypeSchema(schema, rule); + focusedSchemas.push(shaked); + } + } + const collectDeps = (schemas: TypeSchema[], acc: Record): TypeSchema[] => { + if (schemas.length === 0) return Object.values(acc); + for (const schema of schemas) { + acc[JSON.stringify(schema.identifier)] = schema; + } + + const newSchemas: TypeSchema[] = []; + + for (const schema of schemas) { + if (isSpecializationTypeSchema(schema)) { + if (!schema.dependencies) continue; + schema.dependencies.forEach((dep) => { + const depSchema = tsIndex.resolve(dep); + if (!depSchema) + throw new Error( + `Dependent schema ${JSON.stringify(dep)} not found for ${JSON.stringify(schema.identifier)}`, + ); + const id = JSON.stringify(depSchema.identifier); + if (!acc[id]) newSchemas.push(depSchema); + }); + if (schema.nested) { + for (const nest of schema.nested) { + if (isNestedIdentifier(nest.identifier)) continue; + const id = JSON.stringify(nest.identifier); + if (!acc[id]) newSchemas.push(nest); + } + } + } + } + return collectDeps(newSchemas, acc); + }; + + const shaked = collectDeps(focusedSchemas, {}); + return mkTypeSchemaIndex(shaked, { resolutionTree, logger }); +}; diff --git a/src/typeschema/types.ts b/src/typeschema/types.ts index 39fc95e31..8d9b6900e 100644 --- a/src/typeschema/types.ts +++ b/src/typeschema/types.ts @@ -313,6 +313,11 @@ export const isChoiceDeclarationField = (field: Field | undefined): field is Cho return (field as ChoiceFieldDeclaration).choices !== undefined; }; +export const isChoiceInstanceField = (field: Field | undefined): field is ChoiceFieldInstance => { + if (!field) return false; + return (field as ChoiceFieldInstance).choiceOf !== undefined; +}; + export type TypeschemaParserOptions = { format?: "auto" | "ndjson" | "json"; validate?: boolean; diff --git a/src/typeschema/utils.ts b/src/typeschema/utils.ts index 44d7cf520..8e4b21609 100644 --- a/src/typeschema/utils.ts +++ b/src/typeschema/utils.ts @@ -2,23 +2,16 @@ import * as afs from "node:fs/promises"; import * as Path from "node:path"; import type { CodegenLogger } from "@root/utils/codegen-logger"; import * as YAML from "yaml"; -import { extractDependencies } from "./core/transformer"; import type { ResolutionTree } from "./register"; import { type CanonicalUrl, type Field, type Identifier, - isBindingSchema, isComplexTypeTypeSchema, isLogicalTypeSchema, - isNestedIdentifier, - isNotChoiceDeclarationField, - isPrimitiveTypeSchema, isProfileTypeSchema, isResourceTypeSchema, isSpecializationTypeSchema, - isValueSetTypeSchema, - type NestedType, type ProfileTypeSchema, type RegularTypeSchema, type TypeSchema, @@ -46,99 +39,6 @@ export const groupByPackages = (typeSchemas: TypeSchema[]) => { return grouped; }; -export type TypeSchemaShakeRule = { ignoreFields?: string[]; selectFields?: string[] }; - -export type TreeShake = Record>; - -export const treeShakeTypeSchema = ( - schema: TypeSchema, - rule: TypeSchemaShakeRule, - _logger?: CodegenLogger, -): TypeSchema => { - schema = structuredClone(schema); - if (isPrimitiveTypeSchema(schema) || isValueSetTypeSchema(schema) || isBindingSchema(schema)) return schema; - - if (rule.selectFields) { - if (rule.ignoreFields) throw new Error("Cannot use both ignoreFields and selectFields in the same rule"); - - const selectedFields: Record = {}; - for (const fieldName of rule.selectFields) { - if (!schema.fields || !schema.fields[fieldName]) throw new Error(`Field ${fieldName} not found`); - selectedFields[fieldName] = schema.fields[fieldName]; - } - schema.fields = selectedFields; - } - - if (rule.ignoreFields) { - if (rule.selectFields) throw new Error("Cannot use both ignoreFields and selectFields in the same rule"); - for (const fieldName of rule.ignoreFields) { - if (schema.fields && !schema.fields[fieldName]) throw new Error(`Field ${fieldName} not found`); - if (schema.fields) { - delete schema.fields[fieldName]; - } - } - } - - if (schema.nested) { - const usedTypes = new Set( - Object.values(schema.fields ?? {}) - .filter(isNotChoiceDeclarationField) - .map((f) => f.type.url), - ); - schema.nested = schema.nested.filter((n) => usedTypes.has(n.identifier.url)); - } - - schema.dependencies = extractDependencies(schema.identifier, schema.base, schema.fields, schema.nested); - return schema; -}; - -export const treeShake = ( - tsIndex: TypeSchemaIndex, - treeShake: TreeShake, - { resolutionTree, logger }: { resolutionTree?: ResolutionTree; logger?: CodegenLogger }, -): TypeSchemaIndex => { - const focusedSchemas: TypeSchema[] = []; - for (const [pkgId, requires] of Object.entries(treeShake)) { - for (const [url, rule] of Object.entries(requires)) { - const schema = tsIndex.resolveByUrl(pkgId, url as CanonicalUrl); - if (!schema) throw new Error(`Schema not found for ${pkgId} ${url}`); - const shaked = treeShakeTypeSchema(schema, rule); - focusedSchemas.push(shaked); - } - } - const collectDeps = (schemas: TypeSchema[], acc: Record): TypeSchema[] => { - if (schemas.length === 0) return Object.values(acc); - for (const schema of schemas) { - acc[JSON.stringify(schema.identifier)] = schema; - } - - const newSchemas: TypeSchema[] = []; - - for (const schema of schemas) { - if (isSpecializationTypeSchema(schema)) { - if (!schema.dependencies) continue; - schema.dependencies.forEach((dep) => { - const depSchema = tsIndex.resolve(dep); - if (!depSchema) throw new Error(`Schema not found for ${dep}`); - const id = JSON.stringify(depSchema.identifier); - if (!acc[id]) newSchemas.push(depSchema); - }); - if (schema.nested) { - for (const nest of schema.nested) { - if (isNestedIdentifier(nest.identifier)) continue; - const id = JSON.stringify(nest.identifier); - if (!acc[id]) newSchemas.push(nest); - } - } - } - } - return collectDeps(newSchemas, acc); - }; - - const shaked = collectDeps(focusedSchemas, {}); - return mkTypeSchemaIndex(shaked, { resolutionTree, logger }); -}; - const buildDependencyGraph = (schemas: RegularTypeSchema[]): Record => { const nameToMap: Record = {}; for (const schema of schemas) { diff --git a/test/unit/typeschema/__snapshots__/tree-shake.test.ts.snap b/test/unit/typeschema/__snapshots__/tree-shake.test.ts.snap new file mode 100644 index 000000000..84a84ee67 --- /dev/null +++ b/test/unit/typeschema/__snapshots__/tree-shake.test.ts.snap @@ -0,0 +1,1570 @@ +// Bun Snapshot v1, https://bun.sh/docs/test/snapshots + +exports[`treeShake specific TypeSchema Original Patient 1`] = ` +"{ + "identifier": { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Patient", + "url": "http://hl7.org/fhir/StructureDefinition/Patient" + }, + "base": { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "DomainResource", + "url": "http://hl7.org/fhir/StructureDefinition/DomainResource" + }, + "fields": { + "identifier": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Identifier", + "url": "http://hl7.org/fhir/StructureDefinition/Identifier" + }, + "required": false, + "excluded": false, + "array": true + }, + "active": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "boolean", + "url": "http://hl7.org/fhir/StructureDefinition/boolean" + }, + "required": false, + "excluded": false, + "array": false + }, + "name": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "HumanName", + "url": "http://hl7.org/fhir/StructureDefinition/HumanName" + }, + "required": false, + "excluded": false, + "array": true + }, + "telecom": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "ContactPoint", + "url": "http://hl7.org/fhir/StructureDefinition/ContactPoint" + }, + "required": false, + "excluded": false, + "array": true + }, + "gender": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "code", + "url": "http://hl7.org/fhir/StructureDefinition/code" + }, + "required": false, + "excluded": false, + "array": false, + "binding": { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "AdministrativeGender", + "url": "urn:fhir:binding:AdministrativeGender" + }, + "enum": [ + "male", + "female", + "other", + "unknown" + ] + }, + "birthDate": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "date", + "url": "http://hl7.org/fhir/StructureDefinition/date" + }, + "required": false, + "excluded": false, + "array": false + }, + "deceased": { + "required": false, + "excluded": false, + "array": false, + "choices": [ + "deceasedBoolean", + "deceasedDateTime" + ] + }, + "deceasedBoolean": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "boolean", + "url": "http://hl7.org/fhir/StructureDefinition/boolean" + }, + "required": false, + "excluded": false, + "array": false, + "choiceOf": "deceased" + }, + "deceasedDateTime": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "dateTime", + "url": "http://hl7.org/fhir/StructureDefinition/dateTime" + }, + "required": false, + "excluded": false, + "array": false, + "choiceOf": "deceased" + }, + "address": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Address", + "url": "http://hl7.org/fhir/StructureDefinition/Address" + }, + "required": false, + "excluded": false, + "array": true + }, + "maritalStatus": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "CodeableConcept", + "url": "http://hl7.org/fhir/StructureDefinition/CodeableConcept" + }, + "required": false, + "excluded": false, + "array": false, + "binding": { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "MaritalStatus", + "url": "urn:fhir:binding:MaritalStatus" + } + }, + "multipleBirth": { + "required": false, + "excluded": false, + "array": false, + "choices": [ + "multipleBirthBoolean", + "multipleBirthInteger" + ] + }, + "multipleBirthBoolean": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "boolean", + "url": "http://hl7.org/fhir/StructureDefinition/boolean" + }, + "required": false, + "excluded": false, + "array": false, + "choiceOf": "multipleBirth" + }, + "multipleBirthInteger": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "integer", + "url": "http://hl7.org/fhir/StructureDefinition/integer" + }, + "required": false, + "excluded": false, + "array": false, + "choiceOf": "multipleBirth" + }, + "photo": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Attachment", + "url": "http://hl7.org/fhir/StructureDefinition/Attachment" + }, + "required": false, + "excluded": false, + "array": true + }, + "contact": { + "type": { + "kind": "nested", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "contact", + "url": "http://hl7.org/fhir/StructureDefinition/Patient#contact" + }, + "array": true, + "required": false, + "excluded": false + }, + "communication": { + "type": { + "kind": "nested", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "communication", + "url": "http://hl7.org/fhir/StructureDefinition/Patient#communication" + }, + "array": true, + "required": false, + "excluded": false + }, + "generalPractitioner": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Reference", + "url": "http://hl7.org/fhir/StructureDefinition/Reference" + }, + "required": false, + "excluded": false, + "reference": [ + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Organization", + "url": "http://hl7.org/fhir/StructureDefinition/Organization" + }, + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Practitioner", + "url": "http://hl7.org/fhir/StructureDefinition/Practitioner" + }, + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "PractitionerRole", + "url": "http://hl7.org/fhir/StructureDefinition/PractitionerRole" + } + ], + "array": true + }, + "managingOrganization": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Reference", + "url": "http://hl7.org/fhir/StructureDefinition/Reference" + }, + "required": false, + "excluded": false, + "reference": [ + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Organization", + "url": "http://hl7.org/fhir/StructureDefinition/Organization" + } + ], + "array": false + }, + "link": { + "type": { + "kind": "nested", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "link", + "url": "http://hl7.org/fhir/StructureDefinition/Patient#link" + }, + "array": true, + "required": false, + "excluded": false + } + }, + "nested": [ + { + "identifier": { + "kind": "nested", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "communication", + "url": "http://hl7.org/fhir/StructureDefinition/Patient#communication" + }, + "base": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "BackboneElement", + "url": "http://hl7.org/fhir/StructureDefinition/BackboneElement" + }, + "fields": { + "language": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "CodeableConcept", + "url": "http://hl7.org/fhir/StructureDefinition/CodeableConcept" + }, + "required": true, + "excluded": false, + "array": false, + "binding": { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "Language", + "url": "urn:fhir:binding:Language" + } + }, + "preferred": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "boolean", + "url": "http://hl7.org/fhir/StructureDefinition/boolean" + }, + "required": false, + "excluded": false, + "array": false + } + } + }, + { + "identifier": { + "kind": "nested", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "contact", + "url": "http://hl7.org/fhir/StructureDefinition/Patient#contact" + }, + "base": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "BackboneElement", + "url": "http://hl7.org/fhir/StructureDefinition/BackboneElement" + }, + "fields": { + "relationship": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "CodeableConcept", + "url": "http://hl7.org/fhir/StructureDefinition/CodeableConcept" + }, + "required": false, + "excluded": false, + "array": true, + "binding": { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "ContactRelationship", + "url": "urn:fhir:binding:ContactRelationship" + } + }, + "name": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "HumanName", + "url": "http://hl7.org/fhir/StructureDefinition/HumanName" + }, + "required": false, + "excluded": false, + "array": false + }, + "telecom": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "ContactPoint", + "url": "http://hl7.org/fhir/StructureDefinition/ContactPoint" + }, + "required": false, + "excluded": false, + "array": true + }, + "address": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Address", + "url": "http://hl7.org/fhir/StructureDefinition/Address" + }, + "required": false, + "excluded": false, + "array": false + }, + "gender": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "code", + "url": "http://hl7.org/fhir/StructureDefinition/code" + }, + "required": false, + "excluded": false, + "array": false, + "binding": { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "AdministrativeGender", + "url": "urn:fhir:binding:AdministrativeGender" + }, + "enum": [ + "male", + "female", + "other", + "unknown" + ] + }, + "organization": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Reference", + "url": "http://hl7.org/fhir/StructureDefinition/Reference" + }, + "required": false, + "excluded": false, + "reference": [ + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Organization", + "url": "http://hl7.org/fhir/StructureDefinition/Organization" + } + ], + "array": false + }, + "period": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Period", + "url": "http://hl7.org/fhir/StructureDefinition/Period" + }, + "required": false, + "excluded": false, + "array": false + } + } + }, + { + "identifier": { + "kind": "nested", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "link", + "url": "http://hl7.org/fhir/StructureDefinition/Patient#link" + }, + "base": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "BackboneElement", + "url": "http://hl7.org/fhir/StructureDefinition/BackboneElement" + }, + "fields": { + "other": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Reference", + "url": "http://hl7.org/fhir/StructureDefinition/Reference" + }, + "required": true, + "excluded": false, + "reference": [ + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Patient", + "url": "http://hl7.org/fhir/StructureDefinition/Patient" + }, + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "RelatedPerson", + "url": "http://hl7.org/fhir/StructureDefinition/RelatedPerson" + } + ], + "array": false + }, + "type": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "code", + "url": "http://hl7.org/fhir/StructureDefinition/code" + }, + "required": true, + "excluded": false, + "array": false, + "binding": { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "LinkType", + "url": "urn:fhir:binding:LinkType" + }, + "enum": [ + "replaced-by", + "replaces", + "refer", + "seealso" + ] + } + } + } + ], + "description": "Demographics and other administrative information about an individual or animal receiving care or other health-related services.", + "dependencies": [ + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Address", + "url": "http://hl7.org/fhir/StructureDefinition/Address" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Attachment", + "url": "http://hl7.org/fhir/StructureDefinition/Attachment" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "BackboneElement", + "url": "http://hl7.org/fhir/StructureDefinition/BackboneElement" + }, + { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "boolean", + "url": "http://hl7.org/fhir/StructureDefinition/boolean" + }, + { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "code", + "url": "http://hl7.org/fhir/StructureDefinition/code" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "CodeableConcept", + "url": "http://hl7.org/fhir/StructureDefinition/CodeableConcept" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "ContactPoint", + "url": "http://hl7.org/fhir/StructureDefinition/ContactPoint" + }, + { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "date", + "url": "http://hl7.org/fhir/StructureDefinition/date" + }, + { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "dateTime", + "url": "http://hl7.org/fhir/StructureDefinition/dateTime" + }, + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "DomainResource", + "url": "http://hl7.org/fhir/StructureDefinition/DomainResource" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "HumanName", + "url": "http://hl7.org/fhir/StructureDefinition/HumanName" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Identifier", + "url": "http://hl7.org/fhir/StructureDefinition/Identifier" + }, + { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "integer", + "url": "http://hl7.org/fhir/StructureDefinition/integer" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Period", + "url": "http://hl7.org/fhir/StructureDefinition/Period" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Reference", + "url": "http://hl7.org/fhir/StructureDefinition/Reference" + }, + { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "AdministrativeGender", + "url": "urn:fhir:binding:AdministrativeGender" + }, + { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "ContactRelationship", + "url": "urn:fhir:binding:ContactRelationship" + }, + { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "Language", + "url": "urn:fhir:binding:Language" + }, + { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "LinkType", + "url": "urn:fhir:binding:LinkType" + }, + { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "MaritalStatus", + "url": "urn:fhir:binding:MaritalStatus" + } + ] +}" +`; + +exports[`treeShake specific TypeSchema ignoreFields regular field 1`] = ` +"{ + "identifier": { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Patient", + "url": "http://hl7.org/fhir/StructureDefinition/Patient" + }, + "base": { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "DomainResource", + "url": "http://hl7.org/fhir/StructureDefinition/DomainResource" + }, + "fields": { + "identifier": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Identifier", + "url": "http://hl7.org/fhir/StructureDefinition/Identifier" + }, + "required": false, + "excluded": false, + "array": true + }, + "active": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "boolean", + "url": "http://hl7.org/fhir/StructureDefinition/boolean" + }, + "required": false, + "excluded": false, + "array": false + }, + "name": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "HumanName", + "url": "http://hl7.org/fhir/StructureDefinition/HumanName" + }, + "required": false, + "excluded": false, + "array": true + }, + "telecom": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "ContactPoint", + "url": "http://hl7.org/fhir/StructureDefinition/ContactPoint" + }, + "required": false, + "excluded": false, + "array": true + }, + "birthDate": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "date", + "url": "http://hl7.org/fhir/StructureDefinition/date" + }, + "required": false, + "excluded": false, + "array": false + }, + "deceased": { + "required": false, + "excluded": false, + "array": false, + "choices": [ + "deceasedBoolean", + "deceasedDateTime" + ] + }, + "deceasedBoolean": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "boolean", + "url": "http://hl7.org/fhir/StructureDefinition/boolean" + }, + "required": false, + "excluded": false, + "array": false, + "choiceOf": "deceased" + }, + "deceasedDateTime": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "dateTime", + "url": "http://hl7.org/fhir/StructureDefinition/dateTime" + }, + "required": false, + "excluded": false, + "array": false, + "choiceOf": "deceased" + }, + "address": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Address", + "url": "http://hl7.org/fhir/StructureDefinition/Address" + }, + "required": false, + "excluded": false, + "array": true + }, + "maritalStatus": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "CodeableConcept", + "url": "http://hl7.org/fhir/StructureDefinition/CodeableConcept" + }, + "required": false, + "excluded": false, + "array": false, + "binding": { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "MaritalStatus", + "url": "urn:fhir:binding:MaritalStatus" + } + }, + "multipleBirth": { + "required": false, + "excluded": false, + "array": false, + "choices": [ + "multipleBirthBoolean", + "multipleBirthInteger" + ] + }, + "multipleBirthBoolean": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "boolean", + "url": "http://hl7.org/fhir/StructureDefinition/boolean" + }, + "required": false, + "excluded": false, + "array": false, + "choiceOf": "multipleBirth" + }, + "multipleBirthInteger": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "integer", + "url": "http://hl7.org/fhir/StructureDefinition/integer" + }, + "required": false, + "excluded": false, + "array": false, + "choiceOf": "multipleBirth" + }, + "photo": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Attachment", + "url": "http://hl7.org/fhir/StructureDefinition/Attachment" + }, + "required": false, + "excluded": false, + "array": true + }, + "contact": { + "type": { + "kind": "nested", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "contact", + "url": "http://hl7.org/fhir/StructureDefinition/Patient#contact" + }, + "array": true, + "required": false, + "excluded": false + }, + "communication": { + "type": { + "kind": "nested", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "communication", + "url": "http://hl7.org/fhir/StructureDefinition/Patient#communication" + }, + "array": true, + "required": false, + "excluded": false + }, + "generalPractitioner": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Reference", + "url": "http://hl7.org/fhir/StructureDefinition/Reference" + }, + "required": false, + "excluded": false, + "reference": [ + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Organization", + "url": "http://hl7.org/fhir/StructureDefinition/Organization" + }, + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Practitioner", + "url": "http://hl7.org/fhir/StructureDefinition/Practitioner" + }, + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "PractitionerRole", + "url": "http://hl7.org/fhir/StructureDefinition/PractitionerRole" + } + ], + "array": true + }, + "managingOrganization": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Reference", + "url": "http://hl7.org/fhir/StructureDefinition/Reference" + }, + "required": false, + "excluded": false, + "reference": [ + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Organization", + "url": "http://hl7.org/fhir/StructureDefinition/Organization" + } + ], + "array": false + }, + "link": { + "type": { + "kind": "nested", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "link", + "url": "http://hl7.org/fhir/StructureDefinition/Patient#link" + }, + "array": true, + "required": false, + "excluded": false + } + }, + "nested": [ + { + "identifier": { + "kind": "nested", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "communication", + "url": "http://hl7.org/fhir/StructureDefinition/Patient#communication" + }, + "base": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "BackboneElement", + "url": "http://hl7.org/fhir/StructureDefinition/BackboneElement" + }, + "fields": { + "language": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "CodeableConcept", + "url": "http://hl7.org/fhir/StructureDefinition/CodeableConcept" + }, + "required": true, + "excluded": false, + "array": false, + "binding": { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "Language", + "url": "urn:fhir:binding:Language" + } + }, + "preferred": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "boolean", + "url": "http://hl7.org/fhir/StructureDefinition/boolean" + }, + "required": false, + "excluded": false, + "array": false + } + } + }, + { + "identifier": { + "kind": "nested", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "contact", + "url": "http://hl7.org/fhir/StructureDefinition/Patient#contact" + }, + "base": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "BackboneElement", + "url": "http://hl7.org/fhir/StructureDefinition/BackboneElement" + }, + "fields": { + "relationship": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "CodeableConcept", + "url": "http://hl7.org/fhir/StructureDefinition/CodeableConcept" + }, + "required": false, + "excluded": false, + "array": true, + "binding": { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "ContactRelationship", + "url": "urn:fhir:binding:ContactRelationship" + } + }, + "name": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "HumanName", + "url": "http://hl7.org/fhir/StructureDefinition/HumanName" + }, + "required": false, + "excluded": false, + "array": false + }, + "telecom": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "ContactPoint", + "url": "http://hl7.org/fhir/StructureDefinition/ContactPoint" + }, + "required": false, + "excluded": false, + "array": true + }, + "address": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Address", + "url": "http://hl7.org/fhir/StructureDefinition/Address" + }, + "required": false, + "excluded": false, + "array": false + }, + "gender": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "code", + "url": "http://hl7.org/fhir/StructureDefinition/code" + }, + "required": false, + "excluded": false, + "array": false, + "binding": { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "AdministrativeGender", + "url": "urn:fhir:binding:AdministrativeGender" + }, + "enum": [ + "male", + "female", + "other", + "unknown" + ] + }, + "organization": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Reference", + "url": "http://hl7.org/fhir/StructureDefinition/Reference" + }, + "required": false, + "excluded": false, + "reference": [ + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Organization", + "url": "http://hl7.org/fhir/StructureDefinition/Organization" + } + ], + "array": false + }, + "period": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Period", + "url": "http://hl7.org/fhir/StructureDefinition/Period" + }, + "required": false, + "excluded": false, + "array": false + } + } + }, + { + "identifier": { + "kind": "nested", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "link", + "url": "http://hl7.org/fhir/StructureDefinition/Patient#link" + }, + "base": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "BackboneElement", + "url": "http://hl7.org/fhir/StructureDefinition/BackboneElement" + }, + "fields": { + "other": { + "type": { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Reference", + "url": "http://hl7.org/fhir/StructureDefinition/Reference" + }, + "required": true, + "excluded": false, + "reference": [ + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Patient", + "url": "http://hl7.org/fhir/StructureDefinition/Patient" + }, + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "RelatedPerson", + "url": "http://hl7.org/fhir/StructureDefinition/RelatedPerson" + } + ], + "array": false + }, + "type": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "code", + "url": "http://hl7.org/fhir/StructureDefinition/code" + }, + "required": true, + "excluded": false, + "array": false, + "binding": { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "LinkType", + "url": "urn:fhir:binding:LinkType" + }, + "enum": [ + "replaced-by", + "replaces", + "refer", + "seealso" + ] + } + } + } + ], + "description": "Demographics and other administrative information about an individual or animal receiving care or other health-related services.", + "dependencies": [ + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Address", + "url": "http://hl7.org/fhir/StructureDefinition/Address" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Attachment", + "url": "http://hl7.org/fhir/StructureDefinition/Attachment" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "BackboneElement", + "url": "http://hl7.org/fhir/StructureDefinition/BackboneElement" + }, + { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "boolean", + "url": "http://hl7.org/fhir/StructureDefinition/boolean" + }, + { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "code", + "url": "http://hl7.org/fhir/StructureDefinition/code" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "CodeableConcept", + "url": "http://hl7.org/fhir/StructureDefinition/CodeableConcept" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "ContactPoint", + "url": "http://hl7.org/fhir/StructureDefinition/ContactPoint" + }, + { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "date", + "url": "http://hl7.org/fhir/StructureDefinition/date" + }, + { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "dateTime", + "url": "http://hl7.org/fhir/StructureDefinition/dateTime" + }, + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "DomainResource", + "url": "http://hl7.org/fhir/StructureDefinition/DomainResource" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "HumanName", + "url": "http://hl7.org/fhir/StructureDefinition/HumanName" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Identifier", + "url": "http://hl7.org/fhir/StructureDefinition/Identifier" + }, + { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "integer", + "url": "http://hl7.org/fhir/StructureDefinition/integer" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Period", + "url": "http://hl7.org/fhir/StructureDefinition/Period" + }, + { + "kind": "complex-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Reference", + "url": "http://hl7.org/fhir/StructureDefinition/Reference" + }, + { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "AdministrativeGender", + "url": "urn:fhir:binding:AdministrativeGender" + }, + { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "ContactRelationship", + "url": "urn:fhir:binding:ContactRelationship" + }, + { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "Language", + "url": "urn:fhir:binding:Language" + }, + { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "LinkType", + "url": "urn:fhir:binding:LinkType" + }, + { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "MaritalStatus", + "url": "urn:fhir:binding:MaritalStatus" + } + ] +}" +`; + +exports[`treeShake specific TypeSchema selectFields regular field 1`] = ` +"{ + "identifier": { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Patient", + "url": "http://hl7.org/fhir/StructureDefinition/Patient" + }, + "base": { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "DomainResource", + "url": "http://hl7.org/fhir/StructureDefinition/DomainResource" + }, + "fields": { + "gender": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "code", + "url": "http://hl7.org/fhir/StructureDefinition/code" + }, + "required": false, + "excluded": false, + "array": false, + "binding": { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "AdministrativeGender", + "url": "urn:fhir:binding:AdministrativeGender" + }, + "enum": [ + "male", + "female", + "other", + "unknown" + ] + } + }, + "nested": [], + "description": "Demographics and other administrative information about an individual or animal receiving care or other health-related services.", + "dependencies": [ + { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "code", + "url": "http://hl7.org/fhir/StructureDefinition/code" + }, + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "DomainResource", + "url": "http://hl7.org/fhir/StructureDefinition/DomainResource" + }, + { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "AdministrativeGender", + "url": "urn:fhir:binding:AdministrativeGender" + } + ] +}" +`; + +exports[`treeShake specific TypeSchema selectFields multiple regular fields 1`] = ` +"{ + "identifier": { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "Patient", + "url": "http://hl7.org/fhir/StructureDefinition/Patient" + }, + "base": { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "DomainResource", + "url": "http://hl7.org/fhir/StructureDefinition/DomainResource" + }, + "fields": { + "gender": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "code", + "url": "http://hl7.org/fhir/StructureDefinition/code" + }, + "required": false, + "excluded": false, + "array": false, + "binding": { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "AdministrativeGender", + "url": "urn:fhir:binding:AdministrativeGender" + }, + "enum": [ + "male", + "female", + "other", + "unknown" + ] + }, + "birthDate": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "date", + "url": "http://hl7.org/fhir/StructureDefinition/date" + }, + "required": false, + "excluded": false, + "array": false + }, + "active": { + "type": { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "boolean", + "url": "http://hl7.org/fhir/StructureDefinition/boolean" + }, + "required": false, + "excluded": false, + "array": false + } + }, + "nested": [], + "description": "Demographics and other administrative information about an individual or animal receiving care or other health-related services.", + "dependencies": [ + { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "boolean", + "url": "http://hl7.org/fhir/StructureDefinition/boolean" + }, + { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "code", + "url": "http://hl7.org/fhir/StructureDefinition/code" + }, + { + "kind": "primitive-type", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "date", + "url": "http://hl7.org/fhir/StructureDefinition/date" + }, + { + "kind": "resource", + "package": "hl7.fhir.r4.core", + "version": "4.0.1", + "name": "DomainResource", + "url": "http://hl7.org/fhir/StructureDefinition/DomainResource" + }, + { + "kind": "binding", + "package": "shared", + "version": "1.0.0", + "name": "AdministrativeGender", + "url": "urn:fhir:binding:AdministrativeGender" + } + ] +}" +`; diff --git a/test/unit/typeschema/tree-shake.test.ts b/test/unit/typeschema/tree-shake.test.ts new file mode 100644 index 000000000..35bf7e3ed --- /dev/null +++ b/test/unit/typeschema/tree-shake.test.ts @@ -0,0 +1,210 @@ +import { describe, expect, it } from "bun:test"; +import assert from "node:assert"; +import { treeShakeTypeSchema } from "@root/typeschema/tree-shake"; +import type { CanonicalUrl, RegularTypeSchema } from "@root/typeschema/types"; +import { mkR4Register, r4Package, resolveTs } from "@typeschema-test/utils"; + +describe("treeShake specific TypeSchema", async () => { + const r4 = await mkR4Register(); + const patientTss = await resolveTs( + r4, + r4Package, + "http://hl7.org/fhir/StructureDefinition/Patient" as CanonicalUrl, + ); + const patientOrigin = patientTss[0] as RegularTypeSchema; + assert(patientOrigin !== undefined); + + it("Original Patient", () => { + expect(JSON.stringify(patientOrigin, null, 2)).toMatchSnapshot(); + }); + + it("No rule -- no change", () => { + const patient = treeShakeTypeSchema(patientOrigin, {}); + expect(JSON.stringify(patient, null, 2)).toBe(JSON.stringify(patientOrigin, null, 2)); + }); + + it("Select and Ignore fields should be mutually exclusive", () => { + expect(() => { + treeShakeTypeSchema(patientOrigin, { + ignoreFields: ["gender", "link", "active", "address", "birthDate"], + selectFields: ["name", "telecom", "gender", "birthDate"], + }); + }).toThrowError("Cannot use both ignoreFields and selectFields in the same rule"); + }); + + describe("ignoreFields", async () => { + it("regular field", () => { + const patient = treeShakeTypeSchema(patientOrigin, { + ignoreFields: ["gender"], + }) as RegularTypeSchema; + expect(patientOrigin.fields?.gender).toBeDefined(); + expect(patient.fields?.gender).toBeUndefined(); + expect(JSON.stringify(patient, null, 2)).toMatchSnapshot(); + }); + + describe("polimorphic field", () => { + expect(patientOrigin.fields?.multipleBirth).toMatchObject({ + choices: ["multipleBirthBoolean", "multipleBirthInteger"], + }); + expect(patientOrigin.fields?.multipleBirthBoolean).toMatchObject({ + type: { name: "boolean" }, + }); + expect(patientOrigin.fields?.multipleBirthInteger).toMatchObject({ + type: { name: "integer" }, + }); + + it("choice declaration", () => { + const patient = treeShakeTypeSchema(patientOrigin, { + ignoreFields: ["multipleBirth"], + }) as RegularTypeSchema; + expect(patient.fields?.multipleBirth).toBeUndefined(); + expect(patient.fields?.multipleBirthBoolean).toBeUndefined(); + expect(patient.fields?.multipleBirthInteger).toBeUndefined(); + }); + + it("choice instance", () => { + const patient = treeShakeTypeSchema(patientOrigin, { + ignoreFields: ["multipleBirthInteger"], + }) as RegularTypeSchema; + expect(patient.fields?.multipleBirth).toMatchObject({ + choices: ["multipleBirthBoolean"], + }); + expect(patient.fields?.multipleBirthBoolean).toMatchObject({ + type: { name: "boolean" }, + }); + expect(patient.fields?.multipleBirthInteger).toBeUndefined(); + }); + it("all choice instance", () => { + const patient = treeShakeTypeSchema(patientOrigin, { + ignoreFields: ["multipleBirthBoolean", "multipleBirthInteger"], + }) as RegularTypeSchema; + expect(patient.fields?.multipleBirth).toBeUndefined(); + expect(patient.fields?.multipleBirthBoolean).toBeUndefined(); + expect(patient.fields?.multipleBirthInteger).toBeUndefined(); + }); + }); + + describe("edge cases", () => { + it("non-existent field", () => { + expect(() => { + treeShakeTypeSchema(patientOrigin, { + ignoreFields: ["nonExistentField"], + }); + }).toThrowError("Field nonExistentField not found"); + }); + + it("empty ignoreFields array", () => { + const patient = treeShakeTypeSchema(patientOrigin, { + ignoreFields: [], + }) as RegularTypeSchema; + expect(JSON.stringify(patient, null, 2)).toBe(JSON.stringify(patientOrigin, null, 2)); + }); + }); + }); + + describe("selectFields", async () => { + it("regular field", () => { + const patient = treeShakeTypeSchema(patientOrigin, { + selectFields: ["gender"], + }) as RegularTypeSchema; + expect(patient.fields?.gender).toBeDefined(); + expect(patient.fields?.name).toBeUndefined(); + expect(patient.fields?.birthDate).toBeUndefined(); + expect(patient.fields?.address).toBeUndefined(); + expect(JSON.stringify(patient, null, 2)).toMatchSnapshot(); + }); + + it("multiple regular fields", () => { + const patient = treeShakeTypeSchema(patientOrigin, { + selectFields: ["gender", "birthDate", "active"], + }) as RegularTypeSchema; + expect(patient.fields?.gender).toBeDefined(); + expect(patient.fields?.birthDate).toBeDefined(); + expect(patient.fields?.active).toBeDefined(); + expect(patient.fields?.name).toBeUndefined(); + expect(patient.fields?.address).toBeUndefined(); + expect(patient.fields?.telecom).toBeUndefined(); + expect(JSON.stringify(patient, null, 2)).toMatchSnapshot(); + }); + + describe("polymorphic field", () => { + expect(patientOrigin.fields?.multipleBirth).toMatchObject({ + choices: ["multipleBirthBoolean", "multipleBirthInteger"], + }); + expect(patientOrigin.fields?.multipleBirthBoolean).toMatchObject({ + type: { name: "boolean" }, + }); + expect(patientOrigin.fields?.multipleBirthInteger).toMatchObject({ + type: { name: "integer" }, + }); + + it("choice declaration - get all polimorphic fields", () => { + const patient = treeShakeTypeSchema(patientOrigin, { + selectFields: ["multipleBirth"], + }) as RegularTypeSchema; + + expect(patient.fields?.multipleBirth).toMatchObject({ + choices: ["multipleBirthBoolean", "multipleBirthInteger"], + }); + expect(patient.fields?.multipleBirthBoolean).toMatchObject({ + type: { name: "boolean" }, + }); + expect(patient.fields?.multipleBirthInteger).toMatchObject({ + type: { name: "integer" }, + }); + expect(patient.fields?.gender).toBeUndefined(); + expect(patient.fields?.name).toBeUndefined(); + }); + + it("choice instance", () => { + const patient = treeShakeTypeSchema(patientOrigin, { + selectFields: ["multipleBirthBoolean"], + }) as RegularTypeSchema; + + expect(patient.fields?.multipleBirth).toMatchObject({ + choices: ["multipleBirthBoolean"], + }); + expect(patient.fields?.multipleBirthBoolean).toMatchObject({ + type: { name: "boolean" }, + }); + expect(patient.fields?.multipleBirthInteger).toBeUndefined(); + expect(patient.fields?.gender).toBeUndefined(); + expect(patient.fields?.name).toBeUndefined(); + }); + + it("choice declaration & instance", () => { + const patient = treeShakeTypeSchema(patientOrigin, { + selectFields: ["multipleBirth", "multipleBirthBoolean"], + }) as RegularTypeSchema; + + expect(patient.fields?.multipleBirth).toMatchObject({ + choices: ["multipleBirthBoolean"], + }); + expect(patient.fields?.multipleBirthBoolean).toMatchObject({ + type: { name: "boolean" }, + }); + expect(patient.fields?.multipleBirthInteger).toBeUndefined(); + expect(patient.fields?.gender).toBeUndefined(); + expect(patient.fields?.name).toBeUndefined(); + }); + }); + + describe("edge cases", () => { + it("empty selectFields array", () => { + const patient = treeShakeTypeSchema(patientOrigin, { + selectFields: [], + }) as RegularTypeSchema; + + expect(patient.fields).toEqual({}); + }); + + it("non-existent field", () => { + expect(() => { + treeShakeTypeSchema(patientOrigin, { + selectFields: ["nonExistentField"], + }); + }).toThrowError("Field nonExistentField not found"); + }); + }); + }); +}); diff --git a/test/unit/typeschema/utils.ts b/test/unit/typeschema/utils.ts index c8a001ce5..6fdc5afff 100644 --- a/test/unit/typeschema/utils.ts +++ b/test/unit/typeschema/utils.ts @@ -40,6 +40,12 @@ export const registerFs = (register: Register, fs: PFS) => { return rfs; }; +export const resolveTs = async (register: Register, pkgMeta: PackageMeta, url: string | CanonicalUrl) => { + const rfs = register.resolveFs(pkgMeta, url as CanonicalUrl); + if (!rfs) throw new Error("Failed to resolve registered FHIR schema"); + return await transformFhirSchema(register, rfs, logger); +}; + export const registerFsAndMkTs = async (register: Register, fs: PFS) => { registerFs(register, fs); if (!fs.package_meta) throw new Error("Package metadata is missing"); From 583049139c66864c31bb52cc146342f6e1b4c654 Mon Sep 17 00:00:00 2001 From: Aleksandr Penskoi Date: Thu, 18 Dec 2025 10:29:59 +0100 Subject: [PATCH 4/4] Document Tree Shaking configuration. --- README.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 47599180a..edeecd44c 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ yarn add @atomic-ehr/codegen See the [examples/](examples/) directory for working demonstrations: - **[typescript-r4/](examples/typescript-r4/)** - FHIR R4 type generation with resource creation demo and profile usage -- **[typescript-ccda/](examples/typescript-ccda/)** - C-CDA on FHIR type generation +- **[typescript-ccda/](examples/typescript-ccda/)** - C-CDA on FHIR type generation - **[typescript-sql-on-fhir/](examples/typescript-sql-on-fhir/)** - SQL on FHIR ViewDefinition with tree shaking - **[python/](examples/python/)** - Python/Pydantic model generation with configurable field formats - **[csharp/](examples/csharp/)** - C# class generation with namespace configuration @@ -193,6 +193,32 @@ Tree shaking optimizes the generated output by including only the resources you This feature automatically resolves and includes all dependencies (referenced types, base resources, nested types) while excluding unused resources, significantly reducing the size of generated code and improving compilation times. +##### Field-Level Tree Shaking + +Beyond resource-level filtering, tree shaking supports fine-grained field selection using `selectFields` (whitelist) or `ignoreFields` (blacklist): + +```typescript +.treeShake({ + "hl7.fhir.r4.core#4.0.1": { + "http://hl7.org/fhir/StructureDefinition/Patient": { + selectFields: ["id", "name", "birthDate", "gender"] + }, + "http://hl7.org/fhir/StructureDefinition/Observation": { + ignoreFields: ["performer", "note"] + } + } +}) +``` + +**Configuration Rules:** +- `selectFields`: Only includes the specified fields (whitelist approach) +- `ignoreFields`: Removes specified fields, keeps everything else (blacklist approach) +- These options are **mutually exclusive** - you cannot use both in the same rule + +**Polymorphic Field Handling:** + +FHIR choice types (like `multipleBirth[x]` which can be boolean or integer) are handled intelligently. Selecting/ignoring the base field affects all variants, while targeting specific variants only affects those types. + ### Generation The generation stage uses a `WriterGenerator` system that transforms Type Schema into target language code. The architecture consists of: