babelfhir-ts 1.0.31 → 1.0.32

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.
@@ -7,7 +7,6 @@ import { extractPackage, readStructureDefinitionsFromDir, createPackageFromDir,
7
7
  import { fetchStructureDefinitions, parseStructureDefinition, fetchStructureDefinition, registerLocalStructureDefinitions, buildFhirChildTypeMap } from './sdParser.js';
8
8
  import { writeInterfaceAndValidatorToFile, mergeFields, alignFields, ensureDirectoryExists, writeToFile, getBaseResource, downloadFile, sanitizeIdentifier, toPascalCase } from './utils.js';
9
9
  import { generateValidateProfileFunction } from './validatorGenerator.js';
10
- import { loadExamplesFromPackage, generateTestFile } from './testGenerator.js';
11
10
  import { generateValueSetTypeScript, generateValueSetRegistry } from './valueSetGenerator.js';
12
11
  import { generateRandomSupportContent } from './randomSupportGenerator.js';
13
12
  import { logger } from '../logger.js';
@@ -177,7 +176,7 @@ async function generateIndexFile(dir) {
177
176
  }
178
177
  // Reusable processor for a single StructureDefinition
179
178
  async function processStructureDefinition(sd, ctx) {
180
- const { outputDir, fhirSourceHint, valueSetCodesMap, valueSets, examplesMap, existingStructureDefinitions, profileIdToName, profileUrlToName, flags, fhirChildTypeMap } = ctx;
179
+ const { outputDir, fhirSourceHint, valueSetCodesMap, valueSets, existingStructureDefinitions, profileIdToName, profileUrlToName, flags, fhirChildTypeMap } = ctx;
181
180
  const rawInterfaceName = sd.name || sd.id || 'UnnamedInterface';
182
181
  let interfaceName = toPascalCase(rawInterfaceName);
183
182
  // Check if the interface name conflicts with a FHIR base type
@@ -234,7 +233,7 @@ async function processStructureDefinition(sd, ctx) {
234
233
  if (externalSD) {
235
234
  try {
236
235
  logger.log(`Processing external profile as StructureDefinition: ${normalizedTypeName}`);
237
- const actualInterfaceName = await processStructureDefinition(externalSD, { outputDir, fhirSourceHint: profileInfo.profileUrl, valueSetCodesMap, valueSets, examplesMap, existingStructureDefinitions, profileIdToName, profileUrlToName, flags, fhirChildTypeMap });
236
+ const actualInterfaceName = await processStructureDefinition(externalSD, { outputDir, fhirSourceHint: profileInfo.profileUrl, valueSetCodesMap, valueSets, existingStructureDefinitions, profileIdToName, profileUrlToName, flags, fhirChildTypeMap });
238
237
  // If the actual generated interface name differs from what we expected,
239
238
  // add a mapping so other profiles can find it
240
239
  if (actualInterfaceName !== normalizedTypeName) {
@@ -402,10 +401,6 @@ async function processStructureDefinition(sd, ctx) {
402
401
  // Only generate class files if --no-classes flag is not set
403
402
  if (!flags?.noClasses) {
404
403
  writeToFile(path.join(outputDir, `${interfaceName}Class.ts`), classContent);
405
- if (examplesMap && examplesMap[interfaceName]) {
406
- const { valid, invalid } = examplesMap[interfaceName];
407
- generateTestFile(outputDir, interfaceName, valid, invalid);
408
- }
409
404
  }
410
405
  console.log(`Generated artifacts for ${interfaceName}`);
411
406
  return interfaceName;
@@ -482,9 +477,8 @@ export async function generate(fhirSource, outputDir, flags) {
482
477
  }
483
478
  }
484
479
  logger.log(`Profile registries pre-populated: ${profileUrlToName.size} URLs, ${profileIdToName.size} IDs`);
485
- const examples = loadExamplesFromPackage(fhirSource);
486
480
  for (const sd of structureDefinitions) {
487
- await processStructureDefinition(sd, { outputDir, fhirSourceHint: fhirSource, valueSetCodesMap, valueSets, examplesMap: examples, existingStructureDefinitions, profileIdToName, profileUrlToName, flags, fhirChildTypeMap });
481
+ await processStructureDefinition(sd, { outputDir, fhirSourceHint: fhirSource, valueSetCodesMap, valueSets, existingStructureDefinitions, profileIdToName, profileUrlToName, flags, fhirChildTypeMap });
488
482
  }
489
483
  }
490
484
  /**
@@ -585,9 +579,8 @@ export async function generateIntoPackage(packageArchivePath, outArchivePath, fl
585
579
  logger.log(`Profile registries pre-populated: ${profileUrlToName.size} URLs, ${profileIdToName.size} IDs`);
586
580
  // Register all local StructureDefinitions for resolution before HTTP fetches
587
581
  registerLocalStructureDefinitions(structureDefinitions);
588
- const examples = loadExamplesFromPackage(extractedRoot);
589
582
  for (const sd of structureDefinitions) {
590
- await processStructureDefinition(sd, { outputDir, fhirSourceHint: '', valueSetCodesMap, valueSets, examplesMap: examples, existingStructureDefinitions, profileIdToName, profileUrlToName, flags });
583
+ await processStructureDefinition(sd, { outputDir, fhirSourceHint: '', valueSetCodesMap, valueSets, existingStructureDefinitions, profileIdToName, profileUrlToName, flags });
591
584
  }
592
585
  // Create package.json in generated folder
593
586
  const originalPackageJsonPath = path.join(extractedRoot, 'package', 'package.json');
@@ -637,6 +630,10 @@ export async function generateIntoPackage(packageArchivePath, outArchivePath, fl
637
630
  // Compile TypeScript to JavaScript
638
631
  logger.log('Compiling TypeScript to JavaScript...');
639
632
  await compileTypeScriptToJS(outputDir);
633
+ // Copy package.json to root of extractedRoot so it's at the tarball root
634
+ const rootPackageJsonPath = path.join(extractedRoot, 'package.json');
635
+ fs.copyFileSync(generatedPackageJsonPath, rootPackageJsonPath);
636
+ logger.log('Copied package.json to tarball root');
640
637
  const finalArchive = outArchivePath || deriveOutputArchiveName(packageArchivePath);
641
638
  await createPackageFromDir(extractedRoot, finalArchive);
642
639
  return finalArchive;
@@ -674,10 +674,10 @@ export function generateValidateProfileFunction(interfaceName, fields // Array o
674
674
  // Only import fhirpath and run evaluation logic when there are constraints.
675
675
  if (!hasConstraints) {
676
676
  return `
677
- export async function validate${interfaceName}(resource: ${interfaceName}): Promise<{ errors: string[], warnings: string[] }> {\n // Touch the parameter so eslint no-unused-vars doesn't flag it when there are no constraints.\n void resource;\n return { errors: [], warnings: [] };\n}`;
677
+ export async function validate${interfaceName}(resource: ${interfaceName}): Promise<{ errors: string[]; warnings: string[] }> {\n // Touch the parameter so eslint no-unused-vars doesn't flag it when there are no constraints.\n void resource;\n return { errors: [], warnings: [] };\n}`;
678
678
  }
679
- const fhirpathImport = filteredConstraints.length > 0 ? 'import fhirpath from "fhirpath";\nimport fhirpath_r4_model from "fhirpath/fhir-context/r4";\n\n' : '';
680
- return `${fhirpathImport}export async function validate${interfaceName}(resource: ${interfaceName}): Promise<{ errors: string[], warnings: string[] }> {
679
+ const fhirpathImport = filteredConstraints.length > 0 ? 'import fhirpath from "fhirpath";\nimport fhirpath_r4_model from "fhirpath/fhir-context/r4/index.js";\n\n' : '';
680
+ return `${fhirpathImport}export async function validate${interfaceName}(resource: ${interfaceName}): Promise<{ errors: string[]; warnings: string[] }> {
681
681
  const errors: string[] = [];
682
682
  const warnings: string[] = [];
683
683
  ${validationLogic}${patternValidations.join('')}${fixedValueValidations.join('')}${sliceValidations.join('')}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "babelfhir-ts",
3
- "version": "1.0.31",
3
+ "version": "1.0.32",
4
4
  "description": "BabelFHIR-TS: generate TypeScript interfaces, validators, and helper classes from FHIR R4 StructureDefinitions (profiles) directly inside package archives.",
5
5
  "type": "module",
6
6
  "main": "out/main.js",
@@ -1,71 +0,0 @@
1
- import path from "path";
2
- import fs from "fs";
3
- import { logger } from "../logger.js";
4
- export function loadExamplesFromPackage(packagePath) {
5
- const examplesDir = path.join(packagePath, "examples");
6
- const resourceMap = {};
7
- if (!fs.existsSync(examplesDir)) {
8
- console.warn(`No examples directory found in package: ${examplesDir}`);
9
- return resourceMap;
10
- }
11
- const files = fs.readdirSync(examplesDir);
12
- files.forEach((file) => {
13
- const match = file.match(/^(Example|Invalid)(\w+)\.json$/);
14
- if (match) {
15
- const [, type, resourceName] = match;
16
- if (!resourceMap[resourceName]) {
17
- resourceMap[resourceName] = { valid: [], invalid: [] };
18
- }
19
- if (type === "Example") {
20
- resourceMap[resourceName].valid.push(path.join(examplesDir, file));
21
- }
22
- else if (type === "Invalid") {
23
- resourceMap[resourceName].invalid.push(path.join(examplesDir, file));
24
- }
25
- }
26
- });
27
- return resourceMap;
28
- }
29
- export function generateTestFile(outputDir, resourceName, validExamples, invalidExamples) {
30
- const className = `${resourceName}Class`;
31
- const testFileName = `${resourceName}.test.ts`;
32
- const testFilePath = path.join(outputDir, testFileName);
33
- const testContent = `
34
- import { describe, it, expect } from "vitest";
35
- import { ${className} } from "../../generated/${className}";
36
- import fs from "fs";
37
- import path from "path";
38
-
39
- function loadExample(filePath: string) {
40
- const fileContent = fs.readFileSync(filePath, "utf-8");
41
- return JSON.parse(fileContent);
42
- }
43
-
44
- describe("${className} Validation", () => {
45
- ${validExamples
46
- .map((examplePath) => `
47
- it("should validate ${path.basename(examplePath)} without errors", async () => {
48
- const resource = loadExample("${examplePath.replace(/\\/g, "/")}");
49
- const instance = new ${className}(resource);
50
- const errors = await instance.validate();
51
- expect(errors.errors).toEqual([]);
52
- });
53
- `)
54
- .join("\n")}
55
-
56
- ${invalidExamples
57
- .map((examplePath) => `
58
- it("should validate ${path.basename(examplePath)} with errors", async () => {
59
- const resource = loadExample("${examplePath.replace(/\\/g, "/")}");
60
- const instance = new ${className}(resource);
61
- const { errors } = await instance.validate();
62
- expect(errors.length).toBeGreaterThan(0);
63
- });
64
- `)
65
- .join("\n")}
66
-
67
- });
68
- `;
69
- fs.writeFileSync(testFilePath, testContent);
70
- logger.log(`Generated test file: ${testFilePath}`);
71
- }