babelfhir-ts 1.2.2 → 1.2.3
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/out/src/cli/installCommand.js +18 -50
- package/out/src/generator/fhir/corePackageResolver.js +3 -4
- package/out/src/generator/fhir/corePackageResolver.ts +3 -4
- package/out/src/generator/fhir/fallback/r5.json +18377 -0
- package/out/src/generator/index.js +148 -0
- package/out/src/generator/parser/packageParser.js +54 -46
- package/package.json +1 -1
|
@@ -345,6 +345,9 @@ export async function generateIntoPackage(packageArchivePath, outArchivePath, fl
|
|
|
345
345
|
packageName = originalPkg.name || packageName;
|
|
346
346
|
packageVersion = originalPkg.version || packageVersion;
|
|
347
347
|
}
|
|
348
|
+
const generatedExports = {
|
|
349
|
+
'.': { types: './index.d.ts', import: './index.js' }
|
|
350
|
+
};
|
|
348
351
|
const generatedPackageJson = {
|
|
349
352
|
name: `${packageName}-generated`,
|
|
350
353
|
version: packageVersion,
|
|
@@ -352,6 +355,7 @@ export async function generateIntoPackage(packageArchivePath, outArchivePath, fl
|
|
|
352
355
|
type: 'module',
|
|
353
356
|
main: 'index.js',
|
|
354
357
|
types: 'index.d.ts',
|
|
358
|
+
exports: generatedExports,
|
|
355
359
|
scripts: {},
|
|
356
360
|
keywords: ['fhir', 'typescript', 'generated'],
|
|
357
361
|
license: 'MIT',
|
|
@@ -367,6 +371,7 @@ export async function generateIntoPackage(packageArchivePath, outArchivePath, fl
|
|
|
367
371
|
// When client is generated, add @babelfhir-ts/client-<version> as dependency (generated client extends base)
|
|
368
372
|
if (!flags?.noClient) {
|
|
369
373
|
generatedPackageJson.dependencies[`@babelfhir-ts/client-${versionSlug()}`] = '^0.2.0';
|
|
374
|
+
generatedExports['./fhir-client'] = { types: './fhir-client/index.d.ts', import: './fhir-client/index.js' };
|
|
370
375
|
}
|
|
371
376
|
// When zod schemas are generated, add @babelfhir-ts/zod-<version> + zod peer dependency
|
|
372
377
|
if (flags?.schema === 'zod') {
|
|
@@ -458,6 +463,149 @@ export async function generateIntoPackage(packageArchivePath, outArchivePath, fl
|
|
|
458
463
|
}
|
|
459
464
|
}
|
|
460
465
|
}
|
|
466
|
+
/**
|
|
467
|
+
* Generate TypeScript artefacts into a package but skip the intermediate archive
|
|
468
|
+
* round-trip. Returns the generated directory path and a cleanup callback.
|
|
469
|
+
* Used by the install command to avoid double tar operations.
|
|
470
|
+
*/
|
|
471
|
+
export async function generateIntoPackageDirect(packageArchivePath, flags) {
|
|
472
|
+
initGenerationContext(flags, path.dirname(packageArchivePath));
|
|
473
|
+
if (!packageArchivePath.endsWith('.tgz') && !packageArchivePath.endsWith('.zip'))
|
|
474
|
+
throw new Error('Only .tgz or .zip supported.');
|
|
475
|
+
const extractResult = await extractPackage(packageArchivePath);
|
|
476
|
+
const extractedRoot = extractResult.path;
|
|
477
|
+
const shouldCleanup = !extractResult.isCached;
|
|
478
|
+
// Auto-detect FHIR version from package when not explicitly set
|
|
479
|
+
if (!flags?.fhirVersion) {
|
|
480
|
+
const detected = detectFhirVersion(extractedRoot);
|
|
481
|
+
await initVersionContext(detected ?? DEFAULT_FHIR_VERSION);
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
await initVersionContext(flags.fhirVersion);
|
|
485
|
+
}
|
|
486
|
+
const fhirInterfaceNames = ctx().interfaceNames;
|
|
487
|
+
const { getPackageManager } = await import('./parser/packageManager.js');
|
|
488
|
+
const packageManager = getPackageManager();
|
|
489
|
+
packageManager.loadDependenciesFromExtractedPackage(extractedRoot);
|
|
490
|
+
logger.log(`Loaded package dependencies (${packageManager.getTotalStructureDefinitionCount()} total SDs available)`);
|
|
491
|
+
const structureDefinitions = readStructureDefinitionsFromDir(extractedRoot);
|
|
492
|
+
await ensureDependenciesDownloaded(extractedRoot);
|
|
493
|
+
const valueSetCodesMap = readValueSetCodesWithDependencies(extractedRoot);
|
|
494
|
+
let valueSets = readValueSetsFromDir(extractedRoot);
|
|
495
|
+
logger.log(`Package contains ${structureDefinitions.length} StructureDefinitions.`);
|
|
496
|
+
logger.log(`Loaded ${Object.keys(valueSetCodesMap).length} ValueSet code mappings (including dependencies)`);
|
|
497
|
+
const bindingUrls = collectValueSetBindingUrls(structureDefinitions);
|
|
498
|
+
enrichValueSetsFromCodeMap(valueSets, valueSetCodesMap, bindingUrls);
|
|
499
|
+
if (flags?.txServer) {
|
|
500
|
+
valueSets = await expandValueSetsWithTx(valueSets, valueSetCodesMap, structureDefinitions, flags.txServer);
|
|
501
|
+
}
|
|
502
|
+
const outputDir = path.join(extractedRoot, 'generated');
|
|
503
|
+
ensureDirectoryExists(outputDir);
|
|
504
|
+
const didGenerateValueSets = emitValueSetFiles(valueSets, outputDir, { deduplicateFilenames: true });
|
|
505
|
+
if (!didGenerateValueSets)
|
|
506
|
+
cleanupStaleValueSetDir(outputDir);
|
|
507
|
+
const { existingStructureDefinitions, profileIdToName, profileUrlToName } = buildProfileRegistries(structureDefinitions, fhirInterfaceNames);
|
|
508
|
+
registerLocalStructureDefinitions(structureDefinitions);
|
|
509
|
+
const depSDs = readStructureDefinitionsFromDependencies(extractedRoot);
|
|
510
|
+
registerLocalStructureDefinitions(depSDs);
|
|
511
|
+
for (const sd of structureDefinitions) {
|
|
512
|
+
await processStructureDefinition(sd, { outputDir, fhirSourceHint: '', valueSetCodesMap, valueSets, existingStructureDefinitions, profileIdToName, profileUrlToName, flags });
|
|
513
|
+
}
|
|
514
|
+
// Create package.json in generated folder
|
|
515
|
+
const originalPackageJsonPath = path.join(extractedRoot, 'package', 'package.json');
|
|
516
|
+
let packageName = path.basename(packageArchivePath, path.extname(packageArchivePath));
|
|
517
|
+
let packageVersion = '1.0.0';
|
|
518
|
+
if (fs.existsSync(originalPackageJsonPath)) {
|
|
519
|
+
const originalPkg = JSON.parse(fs.readFileSync(originalPackageJsonPath, 'utf-8'));
|
|
520
|
+
packageName = originalPkg.name || packageName;
|
|
521
|
+
packageVersion = originalPkg.version || packageVersion;
|
|
522
|
+
}
|
|
523
|
+
const generatedExports = {
|
|
524
|
+
'.': { types: './index.d.ts', import: './index.js' }
|
|
525
|
+
};
|
|
526
|
+
const generatedPackageJson = {
|
|
527
|
+
name: `${packageName}-generated`,
|
|
528
|
+
version: packageVersion,
|
|
529
|
+
description: `Generated TypeScript interfaces for ${packageName}`,
|
|
530
|
+
type: 'module',
|
|
531
|
+
main: 'index.js',
|
|
532
|
+
types: 'index.d.ts',
|
|
533
|
+
exports: generatedExports,
|
|
534
|
+
scripts: {},
|
|
535
|
+
keywords: ['fhir', 'typescript', 'generated'],
|
|
536
|
+
license: 'MIT',
|
|
537
|
+
peerDependencies: { 'fhirpath': '^3.0.0 || ^4.0.0' },
|
|
538
|
+
dependencies: { '@types/fhir': '^0.0.41' }
|
|
539
|
+
};
|
|
540
|
+
if (!flags?.noClient) {
|
|
541
|
+
generatedPackageJson.dependencies[`@babelfhir-ts/client-${versionSlug()}`] = '^0.2.0';
|
|
542
|
+
generatedExports['./fhir-client'] = { types: './fhir-client/index.d.ts', import: './fhir-client/index.js' };
|
|
543
|
+
}
|
|
544
|
+
if (flags?.schema === 'zod') {
|
|
545
|
+
generatedPackageJson.dependencies[`@babelfhir-ts/zod-${versionSlug()}`] = '^0.1.0';
|
|
546
|
+
generatedPackageJson.peerDependencies['zod'] = '^4.0.0';
|
|
547
|
+
}
|
|
548
|
+
const generatedPackageJsonPath = path.join(outputDir, 'package.json');
|
|
549
|
+
fs.writeFileSync(generatedPackageJsonPath, JSON.stringify(generatedPackageJson, null, 2));
|
|
550
|
+
logger.log(`Created package.json in generated folder: ${packageName}-generated@${packageVersion}`);
|
|
551
|
+
copyFhirAmbientDeclaration(outputDir);
|
|
552
|
+
logger.log('Generating index.ts exports...');
|
|
553
|
+
await generateIndexFile(outputDir);
|
|
554
|
+
if (!flags?.noClient) {
|
|
555
|
+
logger.log('Generating FHIR client...');
|
|
556
|
+
const { generateClient } = await import('./emitters/client/clientGenerator.js');
|
|
557
|
+
generateClient({ outputDir });
|
|
558
|
+
logger.log('FHIR client generated');
|
|
559
|
+
}
|
|
560
|
+
if (flags?.schema === 'zod') {
|
|
561
|
+
const { installBaseZodTypes } = await import('./emitters/zod/zodStubInstaller.js');
|
|
562
|
+
installBaseZodTypes(outputDir);
|
|
563
|
+
}
|
|
564
|
+
const { installFhirpathStub, removeFhirpathStub } = await import('./emitters/validator/fhirpathStubInstaller.js');
|
|
565
|
+
installFhirpathStub(outputDir);
|
|
566
|
+
const nodeTypesDir = path.join(outputDir, 'node_modules', '@types', 'node');
|
|
567
|
+
fs.mkdirSync(nodeTypesDir, { recursive: true });
|
|
568
|
+
fs.writeFileSync(path.join(nodeTypesDir, 'index.d.ts'), `declare module 'module' {\n export function createRequire(filename: string | URL): NodeRequire;\n}\n`);
|
|
569
|
+
fs.writeFileSync(path.join(nodeTypesDir, 'package.json'), JSON.stringify({ name: '@types/node', version: '0.0.0-stub', types: 'index.d.ts' }));
|
|
570
|
+
logger.log('Compiling TypeScript to JavaScript...');
|
|
571
|
+
await compileTypeScriptToJS(outputDir);
|
|
572
|
+
removeFhirpathStub(outputDir);
|
|
573
|
+
try {
|
|
574
|
+
fs.rmSync(path.join(outputDir, 'node_modules', '@types'), { recursive: true, force: true });
|
|
575
|
+
}
|
|
576
|
+
catch { /* ignore */ }
|
|
577
|
+
const babelfhirDir = path.join(outputDir, 'node_modules', '@babelfhir-ts');
|
|
578
|
+
if (fs.existsSync(babelfhirDir)) {
|
|
579
|
+
for (const entry of fs.readdirSync(babelfhirDir)) {
|
|
580
|
+
if (flags?.schema === 'zod' && entry.startsWith('zod-'))
|
|
581
|
+
continue;
|
|
582
|
+
try {
|
|
583
|
+
fs.rmSync(path.join(babelfhirDir, entry), { recursive: true, force: true });
|
|
584
|
+
}
|
|
585
|
+
catch { /* ignore */ }
|
|
586
|
+
}
|
|
587
|
+
try {
|
|
588
|
+
fs.rmdirSync(babelfhirDir);
|
|
589
|
+
}
|
|
590
|
+
catch { /* not empty — fine */ }
|
|
591
|
+
}
|
|
592
|
+
const nodeModulesDir = path.join(outputDir, 'node_modules');
|
|
593
|
+
try {
|
|
594
|
+
fs.rmdirSync(nodeModulesDir);
|
|
595
|
+
}
|
|
596
|
+
catch { /* not empty or doesn't exist — fine */ }
|
|
597
|
+
return {
|
|
598
|
+
generatedDir: outputDir,
|
|
599
|
+
cleanup: () => {
|
|
600
|
+
if (shouldCleanup) {
|
|
601
|
+
try {
|
|
602
|
+
fs.rmSync(extractedRoot, { recursive: true, force: true });
|
|
603
|
+
}
|
|
604
|
+
catch { /* ignore */ }
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
}
|
|
461
609
|
function deriveOutputArchiveName(input) {
|
|
462
610
|
const ext = input.endsWith('.tgz') ? '.tgz' : '.zip';
|
|
463
611
|
return input.replace(ext, `.with-generated${ext}`);
|
|
@@ -5,7 +5,7 @@ import unzipper from "unzipper";
|
|
|
5
5
|
import { parseValueSet } from "./vsParser.js";
|
|
6
6
|
import { logger } from "../../logger.js";
|
|
7
7
|
import { getFhirPackagesCacheDir, ensureCacheDir } from "../core/cacheConfig.js";
|
|
8
|
-
import {
|
|
8
|
+
import { downloadFile } from '../core/utils.js';
|
|
9
9
|
import { versionSlug } from '../fhir/versionContext.js';
|
|
10
10
|
/**
|
|
11
11
|
* FHIR package registries in priority order.
|
|
@@ -451,7 +451,51 @@ export function readValueSetCodesFromDir(extractedRoot, codeSystemMap) {
|
|
|
451
451
|
*/
|
|
452
452
|
export async function ensureDependenciesDownloaded(extractedRoot) {
|
|
453
453
|
const cacheDir = getFhirPackagesCacheDir();
|
|
454
|
+
ensureCacheDir(cacheDir);
|
|
454
455
|
const visited = new Set();
|
|
456
|
+
async function downloadAndExtract(depName, depVersion) {
|
|
457
|
+
const depDir = path.join(cacheDir, `${depName}@${depVersion}`);
|
|
458
|
+
// Already cached (@ separator)?
|
|
459
|
+
if (fs.existsSync(depDir) && fs.readdirSync(depDir).length > 0) {
|
|
460
|
+
return depDir;
|
|
461
|
+
}
|
|
462
|
+
// Already cached (# separator used by Java tooling)?
|
|
463
|
+
const altDir = path.join(cacheDir, `${depName}#${depVersion}`);
|
|
464
|
+
if (fs.existsSync(altDir) && fs.readdirSync(altDir).length > 0) {
|
|
465
|
+
return altDir;
|
|
466
|
+
}
|
|
467
|
+
// Download from registries (streaming to avoid buffering large packages in RAM)
|
|
468
|
+
log.info(`Downloading dependency: ${depName}@${depVersion}`);
|
|
469
|
+
const tgzPath = path.join(cacheDir, `${depName}-${depVersion}.tgz`);
|
|
470
|
+
let downloaded = false;
|
|
471
|
+
for (const registry of FHIR_REGISTRIES) {
|
|
472
|
+
const url = `${registry}/${depName}/${depVersion}`;
|
|
473
|
+
try {
|
|
474
|
+
await downloadFile(url, tgzPath);
|
|
475
|
+
downloaded = true;
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
catch (err) {
|
|
479
|
+
log.debug(`Failed to download ${depName}@${depVersion} from ${registry}: ${err.message}`);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
if (!downloaded) {
|
|
483
|
+
log.warn(`Could not download dependency ${depName}@${depVersion} from any registry`);
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
// Extract the downloaded package
|
|
487
|
+
fs.mkdirSync(depDir, { recursive: true });
|
|
488
|
+
try {
|
|
489
|
+
const { extract } = await import('tar');
|
|
490
|
+
await extract({ cwd: depDir, file: tgzPath });
|
|
491
|
+
log.info(`Cached dependency: ${depName}@${depVersion}`);
|
|
492
|
+
return depDir;
|
|
493
|
+
}
|
|
494
|
+
catch (err) {
|
|
495
|
+
log.warn(`Failed to extract ${depName}@${depVersion}: ${err.message}`);
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
455
499
|
async function walkDeps(pkgDir) {
|
|
456
500
|
const pkgDirNorm = path.normalize(pkgDir);
|
|
457
501
|
if (visited.has(pkgDirNorm))
|
|
@@ -472,52 +516,16 @@ export async function ensureDependenciesDownloaded(extractedRoot) {
|
|
|
472
516
|
catch {
|
|
473
517
|
return;
|
|
474
518
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
const altDir = path.join(cacheDir, `${depName}#${depVersion}`);
|
|
484
|
-
if (fs.existsSync(altDir) && fs.readdirSync(altDir).length > 0) {
|
|
485
|
-
await walkDeps(altDir);
|
|
486
|
-
continue;
|
|
487
|
-
}
|
|
488
|
-
// Download from registries
|
|
489
|
-
log.info(`Downloading dependency: ${depName}@${depVersion}`);
|
|
490
|
-
const tgzPath = path.join(cacheDir, `${depName}-${depVersion}.tgz`);
|
|
491
|
-
let downloaded = false;
|
|
492
|
-
for (const registry of FHIR_REGISTRIES) {
|
|
493
|
-
const url = `${registry}/${depName}/${depVersion}`;
|
|
494
|
-
try {
|
|
495
|
-
const response = await fetchArrayBuffer(url);
|
|
496
|
-
ensureCacheDir(cacheDir);
|
|
497
|
-
fs.writeFileSync(tgzPath, Buffer.from(response.data));
|
|
498
|
-
downloaded = true;
|
|
499
|
-
break;
|
|
500
|
-
}
|
|
501
|
-
catch (err) {
|
|
502
|
-
log.debug(`Failed to download ${depName}@${depVersion} from ${registry}: ${err.message}`);
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
if (!downloaded) {
|
|
506
|
-
log.warn(`Could not download dependency ${depName}@${depVersion} from any registry`);
|
|
507
|
-
continue;
|
|
508
|
-
}
|
|
509
|
-
// Extract the downloaded package
|
|
510
|
-
fs.mkdirSync(depDir, { recursive: true });
|
|
511
|
-
try {
|
|
512
|
-
const { extract } = await import('tar');
|
|
513
|
-
await extract({ cwd: depDir, file: tgzPath });
|
|
514
|
-
log.info(`Cached dependency: ${depName}@${depVersion}`);
|
|
515
|
-
await walkDeps(depDir);
|
|
516
|
-
}
|
|
517
|
-
catch (err) {
|
|
518
|
-
log.warn(`Failed to extract ${depName}@${depVersion}: ${err.message}`);
|
|
519
|
-
}
|
|
519
|
+
// Download all sibling dependencies in parallel
|
|
520
|
+
const depEntries = Object.entries(deps);
|
|
521
|
+
const results = await Promise.all(depEntries.map(([depName, depVersion]) => downloadAndExtract(depName, depVersion)));
|
|
522
|
+
// Recursively walk downloaded dependencies in parallel
|
|
523
|
+
const walkPromises = [];
|
|
524
|
+
for (const depDir of results) {
|
|
525
|
+
if (depDir)
|
|
526
|
+
walkPromises.push(walkDeps(depDir));
|
|
520
527
|
}
|
|
528
|
+
await Promise.all(walkPromises);
|
|
521
529
|
}
|
|
522
530
|
await walkDeps(extractedRoot);
|
|
523
531
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "babelfhir-ts",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "BabelFHIR-TS: generate TypeScript interfaces, validators, and helper classes from FHIR R4/R4B StructureDefinitions (profiles) directly inside package archives.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "out/src/main.js",
|