babelfhir-ts 1.0.13 → 1.0.15

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.
@@ -9,6 +9,7 @@ import { generateValidateProfileFunction } from './validatorGenerator';
9
9
  import { loadExamplesFromPackage, generateTestFile } from './testGenerator';
10
10
  import { generateValueSetTypeScript, generateValueSetRegistry } from './valueSetGenerator.js';
11
11
  import { logger } from '../logger';
12
+ import fhirInterfaceNames from './fhirInterfaces.json';
12
13
  /** Ensure RandomSupport.ts exists in a generation output directory */
13
14
  function ensureRandomSupportFile(dir) {
14
15
  const filePath = path.join(dir, 'RandomSupport.ts');
@@ -63,12 +64,14 @@ async function processStructureDefinition(sd, ctx) {
63
64
  if (profileIdToName?.has(baseResource)) {
64
65
  baseResource = profileIdToName.get(baseResource);
65
66
  }
66
- if (/^coverage_de_/.test(baseResource)) {
67
- baseResource = 'Coverage';
67
+ // Generic fallback: if baseResource isn't a known FHIR type, prefer the declared SD type (core resource)
68
+ const isFhirType = (name) => !!name && (name === 'FhirResource' || fhirInterfaceNames.includes(name));
69
+ if (!isFhirType(baseResource) && sd.type && isFhirType(sd.type)) {
70
+ baseResource = sd.type;
68
71
  }
69
72
  const parsed = await parseStructureDefinition(sd, fhirSourceHint, valueSetCodesMap);
70
73
  if (!parsed.newFields.length) {
71
- console.warn(`No new fields for ${interfaceName}, skipping.`);
74
+ logger.log(`No new SD fields for ${interfaceName}, skipping.`);
72
75
  return;
73
76
  }
74
77
  const aligned = alignFields(parsed.newFields, parsed.oldFields);
@@ -1518,12 +1518,7 @@ export function generateInterfaces(interfaceName, newFields, baseResource, baseF
1518
1518
  }
1519
1519
  if (baseResource && !isPrimitiveType(baseResource) && sanitizeIdentifier(baseResource) !== 'Base') {
1520
1520
  const sanitizedBase = sanitizeIdentifier(baseResource);
1521
- // Skip custom German coverage base pseudo-types that we cannot resolve
1522
- if (/^coverage_de_/.test(sanitizedBase)) {
1523
- // Treat as Coverage
1524
- imports.add('Coverage');
1525
- }
1526
- else if (!isFhirType(sanitizedBase)) {
1521
+ if (!isFhirType(sanitizedBase)) {
1527
1522
  // Check if this profile exists in our generated files
1528
1523
  if (existingProfiles?.has(sanitizedBase)) {
1529
1524
  customImports.add(sanitizedBase);
@@ -1583,10 +1578,7 @@ export function generateInterfaces(interfaceName, newFields, baseResource, baseF
1583
1578
  if (baseResource && sanitizeIdentifier(baseResource) !== 'Base') {
1584
1579
  const sanitizedBase = sanitizeIdentifier(baseResource);
1585
1580
  // Ensure import of the base resource type
1586
- if (/^coverage_de_/.test(sanitizedBase)) {
1587
- imports.add('Coverage');
1588
- }
1589
- else if (!isFhirType(sanitizedBase)) {
1581
+ if (!isFhirType(sanitizedBase)) {
1590
1582
  customImports.add(sanitizedBase);
1591
1583
  }
1592
1584
  else {
package/out/main.js CHANGED
@@ -12,7 +12,7 @@ function printUsage() {
12
12
  console.log("");
13
13
  console.log("Usage:");
14
14
  console.log(" babelfhir-ts [options] [<input> [output]]");
15
- console.log(" babelfhir-ts install [--package] <pkg@version> [--registry <url>] [options]");
15
+ console.log(" babelfhir-ts install [--package] <pkg@version|path> [--registry <url>] [options]");
16
16
  console.log("");
17
17
  console.log("Arguments:");
18
18
  console.log(" input Input can be:");
@@ -35,12 +35,13 @@ function printUsage() {
35
35
  console.log(" --registry <url> FHIR package registry URL (default: https://packages.simplifier.net)");
36
36
  console.log("");
37
37
  console.log("Examples:");
38
- console.log(" babelfhir-ts # Process ./input to ./output");
39
- console.log(" babelfhir-ts package.tgz # Process package to ./output");
38
+ console.log(" babelfhir-ts # Process ./input to ./output");
39
+ console.log(" babelfhir-ts package.tgz # Process package to current directory");
40
40
  console.log(" babelfhir-ts package.tgz modified-package.tgz # Embed interfaces in package");
41
41
  console.log(" babelfhir-ts profiles/ generated/ # Process directory to directory");
42
42
  console.log(" babelfhir-ts --package hl7.fhir.us.core@8.0.0 # Download and process from default registry");
43
43
  console.log(" babelfhir-ts install de.gematik.isik-basismodul@3.1.0 # Download, process, and npm install");
44
+ console.log(" babelfhir-ts install ./package.tgz # Install from local package file");
44
45
  console.log(" babelfhir-ts install hl7.fhir.us.core@8.0.0 --registry <url> # Install from custom registry");
45
46
  console.log(" babelfhir-ts install --package hl7.fhir.us.core@8.0.0 --registry <url> # Alternative syntax");
46
47
  }
@@ -114,65 +115,122 @@ async function downloadPackage(packageSpec, registry) {
114
115
  throw error;
115
116
  }
116
117
  }
118
+ function parseCliArgs(argv) {
119
+ const out = {
120
+ registry: 'https://packages.simplifier.net',
121
+ logLevel: 'none',
122
+ packageSpec: null,
123
+ flags: { noCache: false, noClasses: false, logLevel: 'none' },
124
+ positionals: []
125
+ };
126
+ for (let i = 0; i < argv.length; i++) {
127
+ const arg = argv[i];
128
+ if (arg === '--help' || arg === '-h' || arg === '--version' || arg === '-v' || arg === 'install') {
129
+ // Commands or special flags handled later as positionals to preserve order
130
+ out.positionals.push(arg);
131
+ continue;
132
+ }
133
+ if (arg === '--no-cache') {
134
+ out.flags.noCache = true;
135
+ continue;
136
+ }
137
+ if (arg === '--no-classes') {
138
+ out.flags.noClasses = true;
139
+ continue;
140
+ }
141
+ if (arg === '--log') {
142
+ const val = argv[i + 1];
143
+ if (!val || val.startsWith('-')) {
144
+ throw new Error('Missing value for --log');
145
+ }
146
+ if (!['none', 'console', 'file'].includes(val)) {
147
+ throw new Error(`Invalid --log value: ${val}. Must be: none, console, or file`);
148
+ }
149
+ out.logLevel = val;
150
+ out.flags.logLevel = out.logLevel;
151
+ i++;
152
+ continue;
153
+ }
154
+ if (arg === '--package') {
155
+ const val = argv[i + 1];
156
+ if (!val || val.startsWith('-'))
157
+ throw new Error('Missing value for --package');
158
+ out.packageSpec = val;
159
+ i++;
160
+ continue;
161
+ }
162
+ if (arg === '--registry') {
163
+ const val = argv[i + 1];
164
+ if (!val || val.startsWith('-'))
165
+ throw new Error('Missing value for --registry');
166
+ out.registry = val;
167
+ i++;
168
+ continue;
169
+ }
170
+ // Anything else that doesn't start with -- is positional
171
+ if (!arg.startsWith('--')) {
172
+ out.positionals.push(arg);
173
+ continue;
174
+ }
175
+ // Unknown --flag -> keep as positional to trigger usage error later
176
+ out.positionals.push(arg);
177
+ }
178
+ return out;
179
+ }
117
180
  async function run() {
118
- const args = process.argv.slice(2);
119
- // Parse --registry flag
120
- let registry = 'https://packages.simplifier.net';
121
- const registryIndex = args.indexOf('--registry');
122
- if (registryIndex !== -1 && args[registryIndex + 1]) {
123
- registry = args[registryIndex + 1];
181
+ const argv = process.argv.slice(2);
182
+ let parsed;
183
+ try {
184
+ parsed = parseCliArgs(argv);
124
185
  }
125
- // Parse --log flag
126
- const logIndex = args.indexOf('--log');
127
- const logLevel = logIndex !== -1 && args[logIndex + 1] ? args[logIndex + 1] : 'none';
128
- if (!['none', 'console', 'file'].includes(logLevel)) {
129
- console.error(`Invalid --log value: ${logLevel}. Must be: none, console, or file`);
186
+ catch (e) {
187
+ console.error(`Error: ${e.message}`);
188
+ printUsage();
130
189
  process.exit(1);
131
190
  }
132
- // Parse --package flag
133
- const packageIndex = args.indexOf('--package');
134
- const packageSpec = packageIndex !== -1 && args[packageIndex + 1] ? args[packageIndex + 1] : null;
135
- // Parse flags
136
- const flags = {
137
- noCache: args.includes('--no-cache'),
138
- noClasses: args.includes('--no-classes'),
139
- logLevel: logLevel
140
- };
141
- // Filter out flags to get positional arguments
142
- const positionalArgs = args.filter(arg => !arg.startsWith('--') && !arg.startsWith('-'));
143
- let firstArg = args.find(arg => !arg.startsWith('--') || arg === '--help' || arg === '--version');
144
- const secondArg = positionalArgs[1];
191
+ const { registry, packageSpec, flags, positionals } = parsed;
145
192
  // Check for help flags
146
- if (firstArg === '--help' || firstArg === '-h') {
193
+ if (positionals.includes('--help') || positionals.includes('-h')) {
147
194
  printUsage();
148
195
  process.exit(0);
149
196
  }
150
197
  // Check for version flag
151
- if (firstArg === '--version' || firstArg === '-v') {
198
+ if (positionals.includes('--version') || positionals.includes('-v')) {
152
199
  console.log(`babelfhir-ts version ${packageJson.version}`);
153
200
  process.exit(0);
154
201
  }
155
202
  // Handle install command
156
- if (firstArg === 'install') {
203
+ if (positionals[0] === 'install') {
157
204
  // Package can come from --package flag OR as positional argument after 'install'
158
- const packageToInstall = packageSpec || secondArg;
205
+ const packageToInstall = packageSpec || positionals[1];
159
206
  if (!packageToInstall) {
160
- console.error('Error: install command requires a package name');
207
+ console.error('Error: install command requires a package name or local .tgz/.zip file');
161
208
  console.error('Example: babelfhir-ts install de.gematik.isik-basismodul@3.1.0');
162
209
  console.error(' or: babelfhir-ts install --package hl7.fhir.us.core@8.0.0');
210
+ console.error(' or: babelfhir-ts install ./package.tgz');
163
211
  process.exit(1);
164
212
  }
165
213
  try {
166
- // Download and process package
167
- const downloadedPath = await downloadPackage(packageToInstall, registry);
214
+ let downloadedPath;
215
+ // Check if packageToInstall is a local file path
216
+ if (fs.existsSync(packageToInstall) && (packageToInstall.endsWith('.tgz') || packageToInstall.endsWith('.zip'))) {
217
+ console.log(`Using local package: ${packageToInstall}`);
218
+ downloadedPath = path.resolve(packageToInstall);
219
+ }
220
+ else {
221
+ // Download from registry
222
+ downloadedPath = await downloadPackage(packageToInstall, registry);
223
+ }
168
224
  console.log(`Processing package...`);
169
225
  const baseName = path.basename(downloadedPath, path.extname(downloadedPath));
170
226
  const outputArchive = path.join(process.cwd(), `${baseName}.with-generated.tgz`);
171
227
  await generateIntoPackage(downloadedPath, outputArchive, flags);
172
- // Clean up temp directory
173
- const tempDir = path.join(process.cwd(), '.temp-download');
174
- if (fs.existsSync(tempDir)) {
175
- fs.rmSync(tempDir, { recursive: true, force: true });
228
+ // Clean up temp directory only if we downloaded from registry (not for local files)
229
+ if (!fs.existsSync(packageToInstall) || !(packageToInstall.endsWith('.tgz') || packageToInstall.endsWith('.zip'))) {
230
+ const tempDir = path.join(process.cwd(), '.temp-download');
231
+ if (fs.existsSync(tempDir)) {
232
+ fs.rmSync(tempDir, { recursive: true, force: true });
233
+ }
176
234
  }
177
235
  console.log(`✓ Package processed successfully: ${path.basename(outputArchive)}`);
178
236
  // Extract the archive to access generated folder
@@ -212,7 +270,7 @@ async function run() {
212
270
  if (flags.noCache) {
213
271
  cleanupCache();
214
272
  }
215
- console.log(`\n✓ Package ${secondArg} installed and ready to use!`);
273
+ console.log(`\n✓ Package ${packageToInstall} installed and ready to use!`);
216
274
  return;
217
275
  }
218
276
  catch (error) {
@@ -259,12 +317,11 @@ async function run() {
259
317
  process.exit(1);
260
318
  }
261
319
  }
262
- if (!firstArg) {
320
+ if (positionals.length === 0) {
263
321
  // Default: attempt input -> output
264
322
  if (fs.existsSync('input') && fs.lstatSync('input').isDirectory()) {
265
- firstArg = 'input';
266
323
  const outputDir = 'output';
267
- await generateForDirectory(firstArg, outputDir, flags);
324
+ await generateForDirectory('input', outputDir, flags);
268
325
  console.log(`Batch generation complete. Output in: ${outputDir}`);
269
326
  if (flags.noCache) {
270
327
  cleanupCache();
@@ -274,6 +331,8 @@ async function run() {
274
331
  printError();
275
332
  process.exit(1);
276
333
  }
334
+ const firstArg = positionals[0];
335
+ const secondArg = positionals[1];
277
336
  if (!fs.existsSync(firstArg)) {
278
337
  console.error(`Path does not exist: ${firstArg}`);
279
338
  console.error("Use --help for usage information");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "babelfhir-ts",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
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",