babelfhir-ts 1.3.8 → 1.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -202,54 +202,7 @@ const { errors, warnings } = await patient.validate();
202
202
 
203
203
  <!-- CLI_HELP_START -->
204
204
  ```
205
- BabelFHIR-TS: Generate TypeScript interfaces from FHIR StructureDefinitions
206
-
207
- Usage:
208
- babelfhir-ts [options] [<input> [output]]
209
- babelfhir-ts install [--package] <pkg@version|path> [--registry <url>] [options]
210
-
211
- Arguments:
212
- input Input can be:
213
- - Canonical URL of a FHIR profile (http://... or https://...)
214
- - Directory containing FHIR packages (.tgz/.zip files)
215
- - Single FHIR package (.tgz/.zip file)
216
- - Single StructureDefinition (.json file)
217
- - Directory containing StructureDefinition files
218
- output Output directory or archive name (optional)
219
-
220
- Commands:
221
- install Download, process, and npm install package as dependency
222
-
223
- Options:
224
- -h, --help Show this help message
225
- -v, --version Show version number
226
- --log <dest> Log destination: console (default) or file
227
- --log-level <level> Log verbosity: error, warn, info (default), or debug
228
- --cache-dir <path> Custom cache directory (default: ~/.fhir/packages for FHIR packages, .cache for working files)
229
- --no-cache Delete .cache working folder after generation (does not affect shared ~/.fhir/packages)
230
- --no-classes Only generate interfaces and types (skip class generation)
231
- --no-client Skip FHIR client generation (client generated by default)
232
- --schema <format> Generate schema files alongside outputs (supported: zod)
233
- --dicomweb Generate DICOMweb helpers typed to ImagingStudy profiles in the IG
234
- --fhir-version <ver> FHIR version to target: r4, r4b, or r5 (auto-detected from package if omitted)
235
- --package <pkg@version> Download FHIR package from registry and process it
236
- --registry <url> FHIR package registry URL (default: https://packages.simplifier.net)
237
- --tx-server <url> Terminology server URL for ValueSet expansion (e.g., https://tx.fhir.org/r4)
238
- When set, expands ValueSets without explicit codes using $expand operation
239
-
240
- Examples:
241
- babelfhir-ts # Process ./input to ./output
242
- babelfhir-ts http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient # Generate from profile URL
243
- babelfhir-ts package.tgz # Process package to current directory
244
- babelfhir-ts package.tgz modified-package.tgz # Embed interfaces in package
245
- babelfhir-ts profiles/ generated/ # Process directory to directory
246
- babelfhir-ts --package hl7.fhir.us.core@8.0.0 # Download and process from default registry
247
- babelfhir-ts --package hl7.fhir.us.core@8.0.0 output/ # Download and output to directory
248
- babelfhir-ts --package pkg@version --log console --log-level debug # With verbose logging
249
- babelfhir-ts install de.gematik.isik-basismodul@3.1.0 # Download, process, and npm install
250
- babelfhir-ts install ./package.tgz # Install from local package file
251
- babelfhir-ts install hl7.fhir.us.core@8.0.0 --registry <url> # Install from custom registry
252
- babelfhir-ts install --package hl7.fhir.us.core@8.0.0 --registry <url> # Alternative syntax
205
+
253
206
  ```
254
207
  <!-- CLI_HELP_END -->
255
208
 
package/out/src/bin.js ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import { run } from './main.js';
3
+ import { logger } from './logger.js';
4
+ run()
5
+ .catch(err => {
6
+ console.error('Generation failed:', err instanceof Error ? err.message : err);
7
+ process.exit(1);
8
+ })
9
+ .finally(() => {
10
+ logger.close();
11
+ });
@@ -48,42 +48,59 @@ export function detectPackageManager(startDir) {
48
48
  }
49
49
  return { cmd: isWin ? 'npm.cmd' : 'npm', args: ['install'] };
50
50
  }
51
+ /**
52
+ * Build the install arguments for the package manager.
53
+ * Returns `['install']` when the dep already exists, or `[...pmArgs, relativePath]` for new deps.
54
+ * Always uses a relative path (e.g. `./lib/foo.tgz`) — never an absolute path.
55
+ * Exported for testing.
56
+ */
57
+ export function buildInstallArgs(opts) {
58
+ const { packagePath, pmArgs, cwd, packageJsonContent } = opts;
59
+ const tgzRelPath = './' + path.relative(cwd, packagePath).replace(/\\/g, '/');
60
+ if (packageJsonContent) {
61
+ try {
62
+ const projectPkg = JSON.parse(packageJsonContent);
63
+ const deps = projectPkg.dependencies || {};
64
+ for (const [, value] of Object.entries(deps)) {
65
+ if (typeof value === 'string' && (value === tgzRelPath || value === packagePath)) {
66
+ return { args: ['install'] };
67
+ }
68
+ }
69
+ const tgzBaseName = path.basename(packagePath, '.tgz').replace(/^-/, '');
70
+ for (const name of Object.keys(deps)) {
71
+ if (name.replace(/[@/]/g, '-') === tgzBaseName) {
72
+ deps[name] = tgzRelPath;
73
+ projectPkg.dependencies = deps;
74
+ return {
75
+ args: ['install'],
76
+ updatedPackageJson: JSON.stringify(projectPkg, null, 2) + '\n',
77
+ };
78
+ }
79
+ }
80
+ }
81
+ catch { /* ignore, fall through to normal add */ }
82
+ }
83
+ return { args: [...pmArgs, tgzRelPath] };
84
+ }
51
85
  function npmInstall(packagePath) {
52
86
  return new Promise((resolve, reject) => {
53
87
  const pm = detectPackageManager();
54
88
  const pmName = path.basename(pm.cmd).replace(/\.(cmd|exe)$/, '');
55
- // Check if the package already exists in package.json to avoid dependency loops.
56
89
  const projectPkgPath = path.join(process.cwd(), 'package.json');
57
- let useInstallOnly = false;
58
- if (fs.existsSync(projectPkgPath)) {
59
- try {
60
- const tgzRelPath = './' + path.relative(process.cwd(), packagePath).replace(/\\/g, '/');
61
- const projectPkg = JSON.parse(fs.readFileSync(projectPkgPath, 'utf8'));
62
- const deps = projectPkg.dependencies || {};
63
- for (const [name, value] of Object.entries(deps)) {
64
- if (typeof value === 'string' && (value === tgzRelPath || value === packagePath)) {
65
- console.log(`Package ${name} already in dependencies, running ${pmName} install`);
66
- useInstallOnly = true;
67
- break;
68
- }
69
- }
70
- if (!useInstallOnly) {
71
- const tgzBaseName = path.basename(packagePath, '.tgz').replace(/^-/, '');
72
- for (const name of Object.keys(deps)) {
73
- if (name.replace(/[@/]/g, '-') === tgzBaseName) {
74
- deps[name] = tgzRelPath;
75
- projectPkg.dependencies = deps;
76
- fs.writeFileSync(projectPkgPath, JSON.stringify(projectPkg, null, 2) + '\n');
77
- console.log(`Updated ${name} dependency to ${tgzRelPath}`);
78
- useInstallOnly = true;
79
- break;
80
- }
81
- }
82
- }
83
- }
84
- catch { /* ignore, fall through to normal add */ }
90
+ const packageJsonContent = fs.existsSync(projectPkgPath)
91
+ ? fs.readFileSync(projectPkgPath, 'utf8')
92
+ : undefined;
93
+ const result = buildInstallArgs({
94
+ packagePath,
95
+ pmArgs: pm.args,
96
+ cwd: process.cwd(),
97
+ packageJsonContent,
98
+ });
99
+ if (result.updatedPackageJson) {
100
+ fs.writeFileSync(projectPkgPath, result.updatedPackageJson);
101
+ console.log(`Updated dependency path in package.json`);
85
102
  }
86
- const args = useInstallOnly ? ['install'] : [...pm.args, packagePath];
103
+ const { args } = result;
87
104
  console.log(`Installing package with ${pmName}...`);
88
105
  const child = spawn(pm.cmd, args, {
89
106
  stdio: 'inherit',
@@ -67,8 +67,8 @@ export function getCacheConfig() {
67
67
  }
68
68
  /**
69
69
  * Override the cache configuration (useful for testing or CLI options).
70
- * When rootDir is changed, fhirPackagesDir is also updated to <rootDir>/.fhir/packages/
71
- * unless fhirPackagesDir is explicitly provided.
70
+ * Only rootDir (working cache) is updated; fhirPackagesDir stays at ~/.fhir/packages
71
+ * unless explicitly provided or FHIR_CACHE_ROOT is set.
72
72
  */
73
73
  export function setCacheConfig(config) {
74
74
  const current = getCacheConfig();
@@ -77,8 +77,8 @@ export function setCacheConfig(config) {
77
77
  ...current,
78
78
  ...config,
79
79
  rootDir: newRootDir,
80
- // If rootDir changed but fhirPackagesDir not explicitly set, derive from new rootDir
81
- fhirPackagesDir: config.fhirPackagesDir ?? (config.rootDir ? path.join(newRootDir, '.fhir', 'packages') : current.fhirPackagesDir),
80
+ // Only override fhirPackagesDir if explicitly provided; --cache-dir should NOT move the shared FHIR package cache
81
+ fhirPackagesDir: config.fhirPackagesDir ?? current.fhirPackagesDir,
82
82
  };
83
83
  }
84
84
  /**
@@ -19,26 +19,6 @@ export function writeInterfaceAndValidatorToFile(outputDir, interfaceName, inter
19
19
  const content = `${interfaceContent}\n\n${validateFunctionContent}`;
20
20
  caseAwareWriteFile(filePath, content);
21
21
  }
22
- export function extractFieldsFromInterface(interfaceDecl) {
23
- return interfaceDecl.getProperties().map((property) => {
24
- const name = property.getName();
25
- const type = property.getType().getText();
26
- const isOptional = property.hasQuestionToken();
27
- const isArray = type.endsWith("[]");
28
- return {
29
- constraints: [], // Placeholder for constraints
30
- elements: [], // Placeholder for nested elements
31
- fromBase: true, // Mark as coming from the base resource
32
- isForbidden: false, // Interfaces extracted this way cannot infer forbidden state
33
- isArray,
34
- isOptional,
35
- name,
36
- type: isArray ? type.slice(0, -2) : type,
37
- // Interfaces parsed from existing code cannot know profiling source
38
- isProfiled: false,
39
- };
40
- });
41
- }
42
22
  export function capitalize(str) {
43
23
  if (!str)
44
24
  return str;
@@ -250,7 +250,7 @@ export async function processStructureDefinition(sd, ctx) {
250
250
  if (f.isOptional || f.isForbidden)
251
251
  continue;
252
252
  const parentFieldName = parts[1].split(':')[0];
253
- let relativePath = parts.slice(2).map(p => p.split(':')[0]).join('.');
253
+ let relativePath = parts.slice(2).map((p) => p.split(':')[0]).join('.');
254
254
  if (relativePath.includes('[x]') && f.type && f.type !== 'any' && f.type !== 'unknown') {
255
255
  const fType = f.type;
256
256
  relativePath = relativePath.replace(/(\w+)\[x\]/g, (_match, baseName) => {
package/out/src/main.js CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env node
2
1
  import { generateIntoPackage, generateForDirectory, generateFromJsonFile, generateFromProfileUrl, resetFetchFailureTracking, getFetchFailureWarning, getTimingSummary, formatTimingSummary } from "./generator/index.js";
3
2
  import { generateClient } from "./generator/emitters/client/clientGenerator.js";
4
3
  import { extractPackage } from "./generator/parser/packageParser.js";
@@ -9,7 +8,6 @@ import { buildQualityReport, formatReportSummary, resetDiagnostics } from "./gen
9
8
  import fs from 'fs';
10
9
  import path from 'path';
11
10
  import { fileURLToPath } from 'url';
12
- import { logger } from './logger.js';
13
11
  // Resolve package.json relative to this file — works from both src/ (dev) and out/src/ (compiled)
14
12
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
15
13
  const _pkgJsonPath = [
@@ -290,7 +288,7 @@ export function parseCliArgs(argv) {
290
288
  }
291
289
  return out;
292
290
  }
293
- async function run() {
291
+ export async function run() {
294
292
  const argv = process.argv.slice(2);
295
293
  let parsed;
296
294
  try {
@@ -450,11 +448,5 @@ async function run() {
450
448
  console.error("Use --help for usage information");
451
449
  process.exit(1);
452
450
  }
453
- run()
454
- .catch(err => {
455
- console.error('Generation failed:', err instanceof Error ? err.message : err);
456
- process.exit(1);
457
- })
458
- .finally(() => {
459
- logger.close();
460
- });
451
+ // Note: run() is exported and called from bin.ts (the CLI entry point).
452
+ // Do NOT call run() here — importing this module should have no side effects.
package/package.json CHANGED
@@ -1,15 +1,16 @@
1
1
  {
2
2
  "name": "babelfhir-ts",
3
- "version": "1.3.8",
3
+ "version": "1.3.10",
4
4
  "description": "BabelFHIR-TS: generate TypeScript interfaces, validators, and helper classes from FHIR R4/R4B/R5 StructureDefinitions (profiles) directly inside package archives.",
5
5
  "type": "module",
6
6
  "main": "out/src/main.js",
7
7
  "bin": {
8
- "babelfhir-ts": "./out/src/main.js"
8
+ "babelfhir-ts": "./out/src/bin.js"
9
9
  },
10
10
  "files": [
11
11
  "out/src/generator/",
12
12
  "out/src/cli/",
13
+ "out/src/bin.js",
13
14
  "out/src/main.js",
14
15
  "out/src/logger.js",
15
16
  "out/fhir-r4.d.ts",
@@ -32,7 +33,7 @@
32
33
  "lint:output": "eslint output || exit 0",
33
34
  "typecheck": "tsc --noEmit",
34
35
  "typecheck:output": "tsc -p tsconfig.output.json",
35
- "generate": "tsx src/main.ts",
36
+ "generate": "tsx src/bin.ts",
36
37
  "generate:check": "npm run generate && npm run typecheck && npm run typecheck:output && npm run lint:output",
37
38
  "generate:check:package": "tsx scripts/generate-check-package.ts",
38
39
  "generate:check:pas": "tsx scripts/generate-check-package.ts hl7.fhir.us.davinci-pas@2.0.1 --list-errors --keep-output",
@@ -97,7 +98,6 @@
97
98
  "consola": "^3.4.2",
98
99
  "fhirpath": "^4.9.1",
99
100
  "tar": "^7.4.3",
100
- "ts-morph": "^27.0.2",
101
101
  "tsx": "^4.19.3",
102
102
  "unzipper": "^0.12.3"
103
103
  }