@atomic-ehr/codegen 0.0.1-canary.20251014085150.f19c2e1 → 0.0.1
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/dist/cli/index.js +26 -26
- package/dist/index.d.ts +14 -11
- package/dist/index.js +723 -306
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
|
-
import { existsSync, mkdirSync } from 'fs';
|
|
3
|
-
import * as
|
|
2
|
+
import fs__default, { existsSync, mkdirSync } from 'fs';
|
|
3
|
+
import * as afs2 from 'fs/promises';
|
|
4
4
|
import { readdir, stat, unlink, readFile, writeFile, access, mkdir, rm } from 'fs/promises';
|
|
5
|
-
import * as
|
|
6
|
-
import { join, resolve, dirname, relative } from 'path';
|
|
5
|
+
import * as Path4 from 'path';
|
|
6
|
+
import Path4__default, { join, resolve, dirname, relative } from 'path';
|
|
7
7
|
import { CanonicalManager } from '@atomic-ehr/fhir-canonical-manager';
|
|
8
8
|
import * as fhirschema from '@atomic-ehr/fhirschema';
|
|
9
|
+
import { isStructureDefinition } from '@atomic-ehr/fhirschema';
|
|
9
10
|
import pc from 'picocolors';
|
|
10
11
|
|
|
11
12
|
var __defProp = Object.defineProperty;
|
|
@@ -1445,6 +1446,22 @@ var isChoiceDeclarationField = (field) => {
|
|
|
1445
1446
|
if (!field) return false;
|
|
1446
1447
|
return field.choices !== void 0;
|
|
1447
1448
|
};
|
|
1449
|
+
var isValueSet = (res) => {
|
|
1450
|
+
return res.resourceType === "ValueSet";
|
|
1451
|
+
};
|
|
1452
|
+
var isCodeSystem = (res) => {
|
|
1453
|
+
return res.resourceType === "CodeSystem";
|
|
1454
|
+
};
|
|
1455
|
+
var enrichValueSet = (vs, packageMeta) => {
|
|
1456
|
+
if (!vs.url) throw new Error("ValueSet must have a URL");
|
|
1457
|
+
if (!vs.name) throw new Error("ValueSet must have a name");
|
|
1458
|
+
return {
|
|
1459
|
+
...vs,
|
|
1460
|
+
package_meta: vs.package_meta || packageMeta,
|
|
1461
|
+
name: vs.name,
|
|
1462
|
+
url: vs.url
|
|
1463
|
+
};
|
|
1464
|
+
};
|
|
1448
1465
|
|
|
1449
1466
|
// src/typeschema/core/identifier.ts
|
|
1450
1467
|
function dropVersionFromUrl(url) {
|
|
@@ -1479,7 +1496,7 @@ var getValueSetName = (url) => {
|
|
|
1479
1496
|
}
|
|
1480
1497
|
return url;
|
|
1481
1498
|
};
|
|
1482
|
-
function mkValueSetIdentifierByUrl(register, fullValueSetUrl) {
|
|
1499
|
+
function mkValueSetIdentifierByUrl(register, pkg, fullValueSetUrl) {
|
|
1483
1500
|
const valueSetUrl = dropVersionFromUrl(fullValueSetUrl);
|
|
1484
1501
|
const valueSetNameFallback = getValueSetName(valueSetUrl);
|
|
1485
1502
|
const valuesSetFallback = {
|
|
@@ -1488,7 +1505,7 @@ function mkValueSetIdentifierByUrl(register, fullValueSetUrl) {
|
|
|
1488
1505
|
version: getVersionFromUrl(valueSetUrl) || "0.0.0"
|
|
1489
1506
|
},
|
|
1490
1507
|
id: fullValueSetUrl};
|
|
1491
|
-
const valueSet = register.resolveVs(valueSetUrl) || valuesSetFallback;
|
|
1508
|
+
const valueSet = register.resolveVs(pkg, valueSetUrl) || valuesSetFallback;
|
|
1492
1509
|
const valueSetName = valueSet?.id && !/^[a-zA-Z0-9_-]{20,}$/.test(valueSet.id) ? valueSet.id : valueSetNameFallback;
|
|
1493
1510
|
return {
|
|
1494
1511
|
kind: "value-set",
|
|
@@ -1514,8 +1531,8 @@ function mkBindingIdentifier(fhirSchema, path, bindingName) {
|
|
|
1514
1531
|
function mkNestedIdentifier(register, fhirSchema, path, logger) {
|
|
1515
1532
|
const nestedTypeOrigins = {};
|
|
1516
1533
|
if (fhirSchema.derivation === "constraint") {
|
|
1517
|
-
const specializations = register.resolveFsSpecializations(fhirSchema.url);
|
|
1518
|
-
const nestedTypeGenealogy = specializations.map((
|
|
1534
|
+
const specializations = register.resolveFsSpecializations(fhirSchema.package_meta, fhirSchema.url);
|
|
1535
|
+
const nestedTypeGenealogy = specializations.map((fs4) => mkNestedTypes(register, fs4, logger)).filter((e) => e !== void 0).flat();
|
|
1519
1536
|
for (const nt of nestedTypeGenealogy.reverse()) {
|
|
1520
1537
|
nestedTypeOrigins[nt.identifier.name] = nt.identifier.url;
|
|
1521
1538
|
}
|
|
@@ -1575,7 +1592,7 @@ function mkNestedTypes(register, fhirSchema, logger) {
|
|
|
1575
1592
|
package: fhirSchema.package_meta.name,
|
|
1576
1593
|
version: fhirSchema.package_meta.version,
|
|
1577
1594
|
name: baseName,
|
|
1578
|
-
url: register.ensureSpecializationCanonicalUrl(baseName)
|
|
1595
|
+
url: register.ensureSpecializationCanonicalUrl(fhirSchema.package_meta, baseName)
|
|
1579
1596
|
};
|
|
1580
1597
|
const fields = transformNestedElements(register, fhirSchema, path, element.elements, logger);
|
|
1581
1598
|
const nestedType = {
|
|
@@ -1610,10 +1627,10 @@ function extractNestedDependencies(nestedTypes) {
|
|
|
1610
1627
|
function isRequired(register, fhirSchema, path) {
|
|
1611
1628
|
const fieldName = path[path.length - 1];
|
|
1612
1629
|
const parentPath = path.slice(0, -1);
|
|
1613
|
-
const requires = register.resolveFsGenealogy(fhirSchema.url).flatMap((
|
|
1614
|
-
if (parentPath.length === 0) return
|
|
1615
|
-
if (!
|
|
1616
|
-
let elem =
|
|
1630
|
+
const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((fs4) => {
|
|
1631
|
+
if (parentPath.length === 0) return fs4.required || [];
|
|
1632
|
+
if (!fs4.elements) return [];
|
|
1633
|
+
let elem = fs4;
|
|
1617
1634
|
for (const k of parentPath) {
|
|
1618
1635
|
elem = elem?.elements?.[k];
|
|
1619
1636
|
}
|
|
@@ -1625,10 +1642,10 @@ function isExcluded(register, fhirSchema, path) {
|
|
|
1625
1642
|
const fieldName = path[path.length - 1];
|
|
1626
1643
|
if (!fieldName) throw new Error(`Internal error: fieldName is missing for path ${path.join("/")}`);
|
|
1627
1644
|
const parentPath = path.slice(0, -1);
|
|
1628
|
-
const requires = register.resolveFsGenealogy(fhirSchema.url).flatMap((
|
|
1629
|
-
if (parentPath.length === 0) return
|
|
1630
|
-
if (!
|
|
1631
|
-
let elem =
|
|
1645
|
+
const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((fs4) => {
|
|
1646
|
+
if (parentPath.length === 0) return fs4.excluded || [];
|
|
1647
|
+
if (!fs4.elements) return [];
|
|
1648
|
+
let elem = fs4;
|
|
1632
1649
|
for (const k of parentPath) {
|
|
1633
1650
|
elem = elem?.elements?.[k];
|
|
1634
1651
|
}
|
|
@@ -1636,12 +1653,12 @@ function isExcluded(register, fhirSchema, path) {
|
|
|
1636
1653
|
});
|
|
1637
1654
|
return new Set(requires).has(fieldName);
|
|
1638
1655
|
}
|
|
1639
|
-
var buildReferences = (
|
|
1656
|
+
var buildReferences = (register, fhirSchema, element) => {
|
|
1640
1657
|
if (!element.refers) return void 0;
|
|
1641
1658
|
return element.refers.map((ref) => {
|
|
1642
|
-
const curl = register.ensureSpecializationCanonicalUrl(ref);
|
|
1643
|
-
const
|
|
1644
|
-
return mkIdentifier(
|
|
1659
|
+
const curl = register.ensureSpecializationCanonicalUrl(fhirSchema.package_meta, ref);
|
|
1660
|
+
const fs4 = register.resolveFs(fhirSchema.package_meta, curl);
|
|
1661
|
+
return mkIdentifier(fs4);
|
|
1645
1662
|
});
|
|
1646
1663
|
};
|
|
1647
1664
|
function buildFieldType(register, fhirSchema, path, element, logger) {
|
|
@@ -1649,9 +1666,9 @@ function buildFieldType(register, fhirSchema, path, element, logger) {
|
|
|
1649
1666
|
const refPath = element.elementReference.slice(1).filter((_, i) => i % 2 === 1);
|
|
1650
1667
|
return mkNestedIdentifier(register, fhirSchema, refPath, logger);
|
|
1651
1668
|
} else if (element.type) {
|
|
1652
|
-
const url = register.ensureSpecializationCanonicalUrl(element.type);
|
|
1653
|
-
const fieldFs = register.resolveFs(url);
|
|
1654
|
-
if (!fieldFs) throw new Error(`Could not resolve field '${element.type}'`);
|
|
1669
|
+
const url = register.ensureSpecializationCanonicalUrl(fhirSchema.package_meta, element.type);
|
|
1670
|
+
const fieldFs = register.resolveFs(fhirSchema.package_meta, url);
|
|
1671
|
+
if (!fieldFs) throw new Error(`Could not resolve field type: '${element.type}'`);
|
|
1655
1672
|
return mkIdentifier(fieldFs);
|
|
1656
1673
|
} else if (element.choices) {
|
|
1657
1674
|
return void 0;
|
|
@@ -1670,14 +1687,14 @@ var mkField = (register, fhirSchema, path, element, logger) => {
|
|
|
1670
1687
|
if (element.binding) {
|
|
1671
1688
|
binding = mkBindingIdentifier(fhirSchema, path, element.binding.bindingName);
|
|
1672
1689
|
if (element.binding.strength === "required" && element.type === "code") {
|
|
1673
|
-
enumValues = buildEnum(register, element, logger);
|
|
1690
|
+
enumValues = buildEnum(register, fhirSchema, element, logger);
|
|
1674
1691
|
}
|
|
1675
1692
|
}
|
|
1676
1693
|
return {
|
|
1677
1694
|
type: buildFieldType(register, fhirSchema, path, element, logger),
|
|
1678
1695
|
required: isRequired(register, fhirSchema, path),
|
|
1679
1696
|
excluded: isExcluded(register, fhirSchema, path),
|
|
1680
|
-
reference: buildReferences(
|
|
1697
|
+
reference: buildReferences(register, fhirSchema, element),
|
|
1681
1698
|
array: element.array || false,
|
|
1682
1699
|
min: element.min,
|
|
1683
1700
|
max: element.max,
|
|
@@ -1703,9 +1720,9 @@ function mkNestedField(register, fhirSchema, path, element, logger) {
|
|
|
1703
1720
|
}
|
|
1704
1721
|
|
|
1705
1722
|
// src/typeschema/core/binding.ts
|
|
1706
|
-
function extractValueSetConceptsByUrl(register, valueSetUrl, logger) {
|
|
1723
|
+
function extractValueSetConceptsByUrl(register, pkg, valueSetUrl, logger) {
|
|
1707
1724
|
const cleanUrl = dropVersionFromUrl(valueSetUrl) || valueSetUrl;
|
|
1708
|
-
const valueSet = register.resolveVs(cleanUrl);
|
|
1725
|
+
const valueSet = register.resolveVs(pkg, cleanUrl);
|
|
1709
1726
|
if (!valueSet) return void 0;
|
|
1710
1727
|
return extractValueSetConcepts(register, valueSet);
|
|
1711
1728
|
}
|
|
@@ -1748,14 +1765,14 @@ function extractValueSetConcepts(register, valueSet, _logger) {
|
|
|
1748
1765
|
return concepts.length > 0 ? concepts : void 0;
|
|
1749
1766
|
}
|
|
1750
1767
|
var MAX_ENUM_LENGTH = 100;
|
|
1751
|
-
function buildEnum(register, element, logger) {
|
|
1768
|
+
function buildEnum(register, fhirSchema, element, logger) {
|
|
1752
1769
|
if (!element.binding) return void 0;
|
|
1753
1770
|
const strength = element.binding.strength;
|
|
1754
1771
|
const valueSetUrl = element.binding.valueSet;
|
|
1755
1772
|
if (!valueSetUrl) return void 0;
|
|
1756
1773
|
const shouldGenerateEnum = strength === "required" || strength === "extensible" && (element.type === "code" || element.type === "Coding") || strength === "preferred" && (element.type === "code" || element.type === "Coding");
|
|
1757
1774
|
if (!shouldGenerateEnum) return void 0;
|
|
1758
|
-
const concepts = extractValueSetConceptsByUrl(register, valueSetUrl);
|
|
1775
|
+
const concepts = extractValueSetConceptsByUrl(register, fhirSchema.package_meta, valueSetUrl);
|
|
1759
1776
|
if (!concepts || concepts.length === 0) return void 0;
|
|
1760
1777
|
const codes = concepts.map((c) => c.code).filter((code) => code && typeof code === "string" && code.trim().length > 0);
|
|
1761
1778
|
if (codes.length > MAX_ENUM_LENGTH) {
|
|
@@ -1770,13 +1787,17 @@ function generateBindingSchema(register, fhirSchema, path, element, logger) {
|
|
|
1770
1787
|
if (!element.binding?.valueSet) return void 0;
|
|
1771
1788
|
const identifier = mkBindingIdentifier(fhirSchema, path, element.binding.bindingName);
|
|
1772
1789
|
const fieldType = buildFieldType(register, fhirSchema, path, element, logger);
|
|
1773
|
-
const valueSetIdentifier = mkValueSetIdentifierByUrl(
|
|
1790
|
+
const valueSetIdentifier = mkValueSetIdentifierByUrl(
|
|
1791
|
+
register,
|
|
1792
|
+
fhirSchema.package_meta,
|
|
1793
|
+
element.binding.valueSet
|
|
1794
|
+
);
|
|
1774
1795
|
const dependencies = [];
|
|
1775
1796
|
if (fieldType) {
|
|
1776
1797
|
dependencies.push(fieldType);
|
|
1777
1798
|
}
|
|
1778
1799
|
dependencies.push(valueSetIdentifier);
|
|
1779
|
-
const enumValues = buildEnum(register, element, logger);
|
|
1800
|
+
const enumValues = buildEnum(register, fhirSchema, element, logger);
|
|
1780
1801
|
return {
|
|
1781
1802
|
identifier,
|
|
1782
1803
|
type: fieldType,
|
|
@@ -1877,8 +1898,8 @@ function isExtensionSchema(fhirSchema, _identifier) {
|
|
|
1877
1898
|
}
|
|
1878
1899
|
async function transformValueSet(register, valueSet, logger) {
|
|
1879
1900
|
if (!valueSet.url) throw new Error("ValueSet URL is required");
|
|
1880
|
-
const identifier = mkValueSetIdentifierByUrl(register, valueSet.url);
|
|
1881
|
-
const concept = extractValueSetConceptsByUrl(register, valueSet.url);
|
|
1901
|
+
const identifier = mkValueSetIdentifierByUrl(register, valueSet.package_meta, valueSet.url);
|
|
1902
|
+
const concept = extractValueSetConceptsByUrl(register, valueSet.package_meta, valueSet.url);
|
|
1882
1903
|
return {
|
|
1883
1904
|
identifier,
|
|
1884
1905
|
description: valueSet.description,
|
|
@@ -1964,7 +1985,10 @@ function transformFhirSchemaResource(register, fhirSchema, logger) {
|
|
|
1964
1985
|
const identifier = mkIdentifier(fhirSchema);
|
|
1965
1986
|
let base;
|
|
1966
1987
|
if (fhirSchema.base && fhirSchema.type !== "Element") {
|
|
1967
|
-
const baseFs = register.resolveFs(
|
|
1988
|
+
const baseFs = register.resolveFs(
|
|
1989
|
+
fhirSchema.package_meta,
|
|
1990
|
+
register.ensureSpecializationCanonicalUrl(fhirSchema.package_meta, fhirSchema.base)
|
|
1991
|
+
);
|
|
1968
1992
|
if (!baseFs) {
|
|
1969
1993
|
throw new Error(`Base resource not found '${fhirSchema.base}' for '${fhirSchema.url}'`);
|
|
1970
1994
|
}
|
|
@@ -2256,7 +2280,7 @@ var CodegenLogger = class _CodegenLogger {
|
|
|
2256
2280
|
*/
|
|
2257
2281
|
error(message, error) {
|
|
2258
2282
|
if (this.isSuppressed(3 /* ERROR */)) return;
|
|
2259
|
-
console.error(this.formatMessage("", message, pc.red));
|
|
2283
|
+
console.error(this.formatMessage("X", message, pc.red));
|
|
2260
2284
|
if (error && this.options.verbose) {
|
|
2261
2285
|
console.error(pc.red(` ${error.message}`));
|
|
2262
2286
|
if (error.stack) {
|
|
@@ -2336,120 +2360,129 @@ new CodegenLogger();
|
|
|
2336
2360
|
function createLogger(options = {}) {
|
|
2337
2361
|
return new CodegenLogger(options);
|
|
2338
2362
|
}
|
|
2339
|
-
var
|
|
2340
|
-
const
|
|
2341
|
-
const
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
const pkg = (await manager.resolveEntry(url)).package;
|
|
2351
|
-
if (!pkg) throw new Error(`Can't resolve package for ${url}`);
|
|
2352
|
-
if (!canonicalToPackages[url]) canonicalToPackages[url] = [];
|
|
2353
|
-
canonicalToPackages[url].push(pkg);
|
|
2354
|
-
flatRawIndex[url] = res;
|
|
2355
|
-
}
|
|
2356
|
-
const collisions = Object.entries(canonicalToPackages).filter(([_, e]) => e.length > 1).map(([url, pkgs]) => `${url}: ${pkgs.map((p) => `${p.name}@${p.version}`).join(", ")}`).join("\n");
|
|
2357
|
-
logger?.warn(`Duplicated canonicals: ${collisions}`);
|
|
2358
|
-
const packageToResources = {};
|
|
2359
|
-
const packageToPackageMeta = {};
|
|
2360
|
-
for (const [url, _pkgs] of Object.entries(canonicalToPackages)) {
|
|
2361
|
-
const pkg = (await manager.resolveEntry(url)).package;
|
|
2362
|
-
if (!pkg) throw new Error(`Can't find package for ${url}`);
|
|
2363
|
-
const pkgId = packageMetaToFhir(pkg);
|
|
2364
|
-
packageToPackageMeta[pkgId] = pkg;
|
|
2365
|
-
const res = await manager.resolve(url);
|
|
2366
|
-
if (!packageToResources[pkgId]) {
|
|
2367
|
-
packageToResources[pkgId] = {};
|
|
2368
|
-
}
|
|
2369
|
-
const index = packageToResources[pkgId];
|
|
2370
|
-
if (!index) throw new Error(`Can't find index for ${pkg.name}@${pkg.version}`);
|
|
2371
|
-
index[url] = res;
|
|
2372
|
-
}
|
|
2373
|
-
const indexByPackages = [];
|
|
2374
|
-
for (const [pkgId, index] of Object.entries(packageToResources)) {
|
|
2375
|
-
indexByPackages.push({
|
|
2376
|
-
package_meta: packageToPackageMeta[pkgId],
|
|
2377
|
-
index
|
|
2363
|
+
var readPackageJSON = async (workDir, packageMeta) => {
|
|
2364
|
+
const packageJSONFileName = Path4.join(workDir, "node_modules", packageMeta.name, "package.json");
|
|
2365
|
+
const packageJSON = JSON.parse(await afs2.readFile(packageJSONFileName, "utf8"));
|
|
2366
|
+
return packageJSON;
|
|
2367
|
+
};
|
|
2368
|
+
var readPackageDependencies = async (workDir, packageMeta) => {
|
|
2369
|
+
const packageJSON = await readPackageJSON(workDir, packageMeta);
|
|
2370
|
+
const dependencies = packageJSON.dependencies;
|
|
2371
|
+
if (dependencies !== void 0) {
|
|
2372
|
+
return Object.entries(dependencies).map(([name, version]) => {
|
|
2373
|
+
return { name, version };
|
|
2378
2374
|
});
|
|
2379
2375
|
}
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2376
|
+
return [];
|
|
2377
|
+
};
|
|
2378
|
+
var mkEmptyPkgIndex = (pkg) => {
|
|
2379
|
+
return { pkg, nameResolution: {}, canonicalResolution: {}, fhirSchemas: {}, valueSets: {} };
|
|
2380
|
+
};
|
|
2381
|
+
var mkPackageAwareResolver = async (manager, pkg, deep = 0, _logger) => {
|
|
2382
|
+
const options = {};
|
|
2383
|
+
const deps = await readPackageDependencies("tmp/fhir", pkg);
|
|
2384
|
+
for (const dep of deps) {
|
|
2385
|
+
const depOptions = mkPackageAwareResolver(manager, dep, deep);
|
|
2386
|
+
Object.assign(options, depOptions);
|
|
2387
|
+
}
|
|
2388
|
+
const pkgId = packageMetaToFhir(pkg);
|
|
2389
|
+
if (!options[pkgId]) options[pkgId] = mkEmptyPkgIndex(pkg);
|
|
2390
|
+
for (const resource of await manager.search({ package: pkg })) {
|
|
2391
|
+
const rawUrl = resource.url;
|
|
2392
|
+
if (!rawUrl) continue;
|
|
2393
|
+
if (!(isStructureDefinition(resource) || isValueSet(resource) || isCodeSystem(resource))) continue;
|
|
2394
|
+
const url = rawUrl;
|
|
2395
|
+
if (!options[pkgId].canonicalResolution[url]) {
|
|
2396
|
+
options[pkgId].canonicalResolution[url] = [];
|
|
2397
|
+
for (const [depPkgId, { canonicalResolution }] of Object.entries(options)) {
|
|
2398
|
+
if (pkgId === depPkgId) continue;
|
|
2399
|
+
if (!canonicalResolution[url]) continue;
|
|
2400
|
+
for (const deepRes of canonicalResolution[url]) {
|
|
2401
|
+
options[pkgId].canonicalResolution[url].push({
|
|
2402
|
+
deep: deepRes.deep + 1,
|
|
2403
|
+
resource: deepRes.resource,
|
|
2404
|
+
packageMeta: deepRes.packageMeta
|
|
2405
|
+
});
|
|
2399
2406
|
}
|
|
2400
|
-
vsIndex[resource.url] = resource;
|
|
2401
2407
|
}
|
|
2402
2408
|
}
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
if (!specNameToCanonicals[name]) specNameToCanonicals[name] = {};
|
|
2412
|
-
specNameToCanonicals[name][rfs.package_meta.name] = rfs.url;
|
|
2413
|
-
}
|
|
2414
|
-
const specNameToCanonical = {};
|
|
2415
|
-
for (const [sname, canonicals] of Object.entries(specNameToCanonicals)) {
|
|
2416
|
-
const name = sname;
|
|
2417
|
-
const canonicalValues = Object.values(canonicals);
|
|
2418
|
-
if (canonicalValues.length === 1) {
|
|
2419
|
-
const url = canonicalValues[0];
|
|
2420
|
-
specNameToCanonical[name] = url;
|
|
2421
|
-
} else {
|
|
2422
|
-
for (const pname of packageNameResolver) {
|
|
2423
|
-
if (canonicals[pname]) {
|
|
2424
|
-
specNameToCanonical[name] = canonicals[pname];
|
|
2425
|
-
break;
|
|
2426
|
-
}
|
|
2409
|
+
options[pkgId].canonicalResolution[url].push({ deep, packageMeta: pkg, resource });
|
|
2410
|
+
options[pkgId].canonicalResolution[url].sort((a, b) => a.deep - b.deep);
|
|
2411
|
+
const name = resource.name;
|
|
2412
|
+
if (name && isStructureDefinition(resource) && (resource.derivation === "specialization" || resource.derivation === void 0)) {
|
|
2413
|
+
if (!options[pkgId].nameResolution[name]) {
|
|
2414
|
+
options[pkgId].nameResolution[name] = resource.url;
|
|
2415
|
+
} else {
|
|
2416
|
+
throw new Error(`Duplicate name ${name} in package ${pkgId}`);
|
|
2427
2417
|
}
|
|
2428
|
-
if (specNameToCanonical[name] === void 0) throw new Error(`No canonical URL found for ${name}`);
|
|
2429
2418
|
}
|
|
2430
2419
|
}
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2420
|
+
return options;
|
|
2421
|
+
};
|
|
2422
|
+
var packageAwareResolveName = (resolver, pkg, name) => {
|
|
2423
|
+
const pkgId = packageMetaToFhir(pkg);
|
|
2424
|
+
if (!resolver[pkgId]) throw new Error(`Package ${pkgId} not found`);
|
|
2425
|
+
const resource = resolver[pkgId].nameResolution[name];
|
|
2426
|
+
return resource;
|
|
2427
|
+
};
|
|
2428
|
+
var packageAgnosticResolveCanonical = (resolver, url, logger) => {
|
|
2429
|
+
const options = Object.values(resolver).flatMap((pkg) => pkg.canonicalResolution[url]);
|
|
2430
|
+
if (!options) throw new Error(`No canonical resolution found for ${url} in any package`);
|
|
2431
|
+
if (options.length > 1)
|
|
2432
|
+
logger?.dry_warn(
|
|
2433
|
+
`Multiple canonical resolutions found for ${url} in: ${JSON.stringify(options, void 0, 2)}`
|
|
2434
|
+
);
|
|
2435
|
+
return options[0]?.resource;
|
|
2436
|
+
};
|
|
2437
|
+
var registerFromManager = async (manager, { logger, fallbackPackageForNameResolution }) => {
|
|
2438
|
+
const packages = await manager.packages();
|
|
2439
|
+
const resolver = {};
|
|
2440
|
+
for (const pkg of packages) {
|
|
2441
|
+
Object.assign(resolver, await mkPackageAwareResolver(manager, pkg));
|
|
2442
|
+
}
|
|
2443
|
+
for (const { pkg, canonicalResolution } of Object.values(resolver)) {
|
|
2444
|
+
const pkgId = packageMetaToFhir(pkg);
|
|
2445
|
+
if (!resolver[pkgId]) throw new Error(`Package ${pkgId} not found`);
|
|
2446
|
+
let counter = 0;
|
|
2447
|
+
logger?.info(`FHIR Schema conversion for '${packageMetaToFhir(pkg)}' begins...`);
|
|
2448
|
+
for (const [_url, options] of Object.entries(canonicalResolution)) {
|
|
2449
|
+
const resource = options[0]?.resource;
|
|
2450
|
+
if (!resource) throw new Error(`Resource not found`);
|
|
2451
|
+
if (isStructureDefinition(resource)) {
|
|
2452
|
+
const rfs = enrichFHIRSchema(fhirschema.translate(resource), pkg);
|
|
2453
|
+
counter++;
|
|
2454
|
+
resolver[pkgId].fhirSchemas[rfs.url] = rfs;
|
|
2455
|
+
}
|
|
2456
|
+
if (isValueSet(resource)) {
|
|
2457
|
+
const rvs = enrichValueSet(resource, pkg);
|
|
2458
|
+
resolver[pkgId].valueSets[rvs.url] = rvs;
|
|
2459
|
+
}
|
|
2435
2460
|
}
|
|
2461
|
+
logger?.success(`FHIR Schema conversion for '${packageMetaToFhir(pkg)}' completed: ${counter} successful`);
|
|
2436
2462
|
}
|
|
2437
|
-
const
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2463
|
+
const resolveFs = (pkg, canonicalUrl) => {
|
|
2464
|
+
return resolver[packageMetaToFhir(pkg)]?.fhirSchemas[canonicalUrl] || fallbackPackageForNameResolution && resolver[packageMetaToFhir(fallbackPackageForNameResolution)]?.fhirSchemas[canonicalUrl];
|
|
2465
|
+
};
|
|
2466
|
+
const resolveVs = (pkg, canonicalUrl) => {
|
|
2467
|
+
return resolver[packageMetaToFhir(pkg)]?.valueSets[canonicalUrl] || fallbackPackageForNameResolution && resolver[packageMetaToFhir(fallbackPackageForNameResolution)]?.valueSets[canonicalUrl];
|
|
2468
|
+
};
|
|
2469
|
+
const ensureSpecializationCanonicalUrl = (pkg, name) => packageAwareResolveName(resolver, pkg, name) || fallbackPackageForNameResolution && packageAwareResolveName(resolver, fallbackPackageForNameResolution, name) || name;
|
|
2470
|
+
const resolveFsGenealogy = (pkg, canonicalUrl) => {
|
|
2471
|
+
let fs4 = resolveFs(pkg, canonicalUrl);
|
|
2472
|
+
if (fs4 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
|
|
2473
|
+
const genealogy = [fs4];
|
|
2474
|
+
while (fs4?.base) {
|
|
2475
|
+
fs4 = resolveFs(fs4.package_meta, fs4.base) || resolveFs(fs4.package_meta, ensureSpecializationCanonicalUrl(fs4.package_meta, fs4.base));
|
|
2476
|
+
if (fs4 === void 0) throw new Error(`Failed to resolve FHIR Schema genealogy for '${canonicalUrl}'`);
|
|
2477
|
+
genealogy.push(fs4);
|
|
2445
2478
|
}
|
|
2446
2479
|
return genealogy;
|
|
2447
2480
|
};
|
|
2448
|
-
const resolveFsSpecializations = (canonicalUrl) => {
|
|
2449
|
-
return resolveFsGenealogy(canonicalUrl).filter((
|
|
2481
|
+
const resolveFsSpecializations = (pkg, canonicalUrl) => {
|
|
2482
|
+
return resolveFsGenealogy(pkg, canonicalUrl).filter((fs4) => fs4.derivation === "specialization");
|
|
2450
2483
|
};
|
|
2451
2484
|
const resolveElementSnapshot = (fhirSchema, path) => {
|
|
2452
|
-
const geneology = resolveFsGenealogy(fhirSchema.url);
|
|
2485
|
+
const geneology = resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url);
|
|
2453
2486
|
const elemGeneology = resolveFsElementGenealogy(geneology, path);
|
|
2454
2487
|
const elemSnapshot = fsElementSnapshot(elemGeneology);
|
|
2455
2488
|
return elemSnapshot;
|
|
@@ -2468,22 +2501,26 @@ var registerFromManager = async (manager, conf) => {
|
|
|
2468
2501
|
};
|
|
2469
2502
|
return {
|
|
2470
2503
|
...manager,
|
|
2471
|
-
|
|
2472
|
-
const rfs = enrichFHIRSchema(
|
|
2473
|
-
|
|
2474
|
-
|
|
2504
|
+
unsafeAppendFs(fs4) {
|
|
2505
|
+
const rfs = enrichFHIRSchema(fs4);
|
|
2506
|
+
const pkgId = packageMetaToFhir(rfs.package_meta);
|
|
2507
|
+
if (!resolver[pkgId]) resolver[pkgId] = mkEmptyPkgIndex(rfs.package_meta);
|
|
2508
|
+
resolver[pkgId].nameResolution[rfs.name] = rfs.url;
|
|
2509
|
+
resolver[pkgId].fhirSchemas[rfs.url] = rfs;
|
|
2475
2510
|
},
|
|
2476
|
-
resolveFs
|
|
2511
|
+
resolveFs,
|
|
2477
2512
|
resolveFsGenealogy,
|
|
2478
2513
|
resolveFsSpecializations,
|
|
2479
|
-
ensureSpecializationCanonicalUrl
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2514
|
+
ensureSpecializationCanonicalUrl,
|
|
2515
|
+
resolveSd: (_pkg, canonicalUrl) => {
|
|
2516
|
+
const res = packageAgnosticResolveCanonical(resolver, canonicalUrl, logger);
|
|
2517
|
+
if (isStructureDefinition(res)) return res;
|
|
2518
|
+
return void 0;
|
|
2519
|
+
},
|
|
2520
|
+
allFs: () => Object.values(resolver).flatMap((pkgIndex) => Object.values(pkgIndex.fhirSchemas)),
|
|
2521
|
+
allVs: () => Object.values(resolver).flatMap((pkgIndex) => Object.values(pkgIndex.valueSets)),
|
|
2522
|
+
resolveVs,
|
|
2523
|
+
resolveAny: (canonicalUrl) => packageAgnosticResolveCanonical(resolver, canonicalUrl, logger),
|
|
2487
2524
|
resolveElementSnapshot,
|
|
2488
2525
|
getAllElementKeys
|
|
2489
2526
|
};
|
|
@@ -2491,9 +2528,9 @@ var registerFromManager = async (manager, conf) => {
|
|
|
2491
2528
|
var resolveFsElementGenealogy = (genealogy, path) => {
|
|
2492
2529
|
const [top, ...rest] = path;
|
|
2493
2530
|
if (top === void 0) return [];
|
|
2494
|
-
return genealogy.map((
|
|
2495
|
-
if (!
|
|
2496
|
-
let elem =
|
|
2531
|
+
return genealogy.map((fs4) => {
|
|
2532
|
+
if (!fs4.elements) return void 0;
|
|
2533
|
+
let elem = fs4.elements?.[top];
|
|
2497
2534
|
for (const k of rest) {
|
|
2498
2535
|
elem = elem?.elements?.[k];
|
|
2499
2536
|
}
|
|
@@ -2501,7 +2538,8 @@ var resolveFsElementGenealogy = (genealogy, path) => {
|
|
|
2501
2538
|
}).filter((elem) => elem !== void 0);
|
|
2502
2539
|
};
|
|
2503
2540
|
function fsElementSnapshot(genealogy) {
|
|
2504
|
-
const
|
|
2541
|
+
const revGenealogy = genealogy.reverse();
|
|
2542
|
+
const snapshot = Object.assign({}, ...revGenealogy);
|
|
2505
2543
|
snapshot.elements = void 0;
|
|
2506
2544
|
return snapshot;
|
|
2507
2545
|
}
|
|
@@ -2532,7 +2570,7 @@ var TypeSchemaGenerator = class {
|
|
|
2532
2570
|
const packageNames = packageMetas.map((meta) => `${meta.name}${meta.version}`);
|
|
2533
2571
|
this.logger?.step(`Loading FHIR packages: ${packageNames.join(", ")}`);
|
|
2534
2572
|
await this.manager.init();
|
|
2535
|
-
return registerFromManager(this.manager);
|
|
2573
|
+
return registerFromManager(this.manager, {});
|
|
2536
2574
|
}
|
|
2537
2575
|
generateFhirSchemas(structureDefinitions) {
|
|
2538
2576
|
this.logger?.progress(`Converting ${structureDefinitions.length} StructureDefinitions to FHIRSchemas`);
|
|
@@ -2606,7 +2644,7 @@ var TypeSchemaGenerator = class {
|
|
|
2606
2644
|
};
|
|
2607
2645
|
const register = await this.registerFromPackageMetas([packageInfo]);
|
|
2608
2646
|
const valueSets = await this.generateValueSetSchemas(register.allVs(), logger);
|
|
2609
|
-
const fhirSchemas = (await Promise.all(register.allFs().map(async (
|
|
2647
|
+
const fhirSchemas = (await Promise.all(register.allFs().map(async (fs4) => await transformFhirSchema(register, fs4, logger)))).flat();
|
|
2610
2648
|
const allSchemas = [...fhirSchemas, ...valueSets];
|
|
2611
2649
|
if (this.cache) {
|
|
2612
2650
|
for (const schema of allSchemas) {
|
|
@@ -3012,6 +3050,468 @@ var uppercaseFirstLetter = (str) => {
|
|
|
3012
3050
|
var uppercaseFirstLetterOfEach = (strings) => {
|
|
3013
3051
|
return strings.map((str) => uppercaseFirstLetter(str));
|
|
3014
3052
|
};
|
|
3053
|
+
function deepEqual(obj1, obj2) {
|
|
3054
|
+
if (obj1 === obj2) return true;
|
|
3055
|
+
if (obj1 === null || obj2 === null || typeof obj1 !== "object" || typeof obj2 !== "object") {
|
|
3056
|
+
return false;
|
|
3057
|
+
}
|
|
3058
|
+
if (Array.isArray(obj1) && Array.isArray(obj2)) {
|
|
3059
|
+
if (obj1.length !== obj2.length) return false;
|
|
3060
|
+
return obj1.every((item, index) => deepEqual(item, obj2[index]));
|
|
3061
|
+
}
|
|
3062
|
+
if (Array.isArray(obj1) || Array.isArray(obj2)) {
|
|
3063
|
+
return false;
|
|
3064
|
+
}
|
|
3065
|
+
const keys1 = Object.keys(obj1);
|
|
3066
|
+
const keys2 = Object.keys(obj2);
|
|
3067
|
+
if (keys1.length !== keys2.length) return false;
|
|
3068
|
+
return keys1.every((key) => keys2.includes(key) && deepEqual(obj1[key], obj2[key]));
|
|
3069
|
+
}
|
|
3070
|
+
var FileSystemWriter = class {
|
|
3071
|
+
opts;
|
|
3072
|
+
currentDir;
|
|
3073
|
+
currentFileDescriptor;
|
|
3074
|
+
writtenFilesSet = /* @__PURE__ */ new Set();
|
|
3075
|
+
constructor(opts) {
|
|
3076
|
+
this.opts = opts;
|
|
3077
|
+
this.currentDir = opts.outputDir;
|
|
3078
|
+
}
|
|
3079
|
+
logger() {
|
|
3080
|
+
return this.opts.logger;
|
|
3081
|
+
}
|
|
3082
|
+
cd(path, gen) {
|
|
3083
|
+
this.currentDir = path.startsWith("/") ? Path4.join(this.opts.outputDir, path) : Path4.join(this.currentDir, path);
|
|
3084
|
+
if (!fs.existsSync(this.currentDir)) {
|
|
3085
|
+
fs.mkdirSync(this.currentDir, { recursive: true });
|
|
3086
|
+
}
|
|
3087
|
+
this.logger()?.debug(`cd '${this.currentDir}'`);
|
|
3088
|
+
gen();
|
|
3089
|
+
}
|
|
3090
|
+
cat(fn, gen) {
|
|
3091
|
+
if (this.currentFileDescriptor) throw new Error("Can't open file in file");
|
|
3092
|
+
if (fn.includes("/")) throw new Error(`Change file path separatly: ${fn}`);
|
|
3093
|
+
const fullFn = `${this.currentDir}/${fn}`;
|
|
3094
|
+
try {
|
|
3095
|
+
this.currentFileDescriptor = fs.openSync(fullFn, "w");
|
|
3096
|
+
this.writtenFilesSet.add(fn);
|
|
3097
|
+
this.logger()?.debug(`cat > '${fullFn}'`);
|
|
3098
|
+
gen();
|
|
3099
|
+
} finally {
|
|
3100
|
+
if (this.currentFileDescriptor) {
|
|
3101
|
+
fs.closeSync(this.currentFileDescriptor);
|
|
3102
|
+
}
|
|
3103
|
+
this.currentFileDescriptor = void 0;
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
write(str) {
|
|
3107
|
+
if (!this.currentFileDescriptor) throw new Error("No file opened");
|
|
3108
|
+
fs.writeSync(this.currentFileDescriptor, str);
|
|
3109
|
+
}
|
|
3110
|
+
generate(_tsIndex) {
|
|
3111
|
+
throw new Error("Not implemented");
|
|
3112
|
+
}
|
|
3113
|
+
writtenFiles() {
|
|
3114
|
+
return Array.from(this.writtenFilesSet);
|
|
3115
|
+
}
|
|
3116
|
+
};
|
|
3117
|
+
var Writer = class extends FileSystemWriter {
|
|
3118
|
+
currentIndent = 0;
|
|
3119
|
+
indent() {
|
|
3120
|
+
this.currentIndent += this.opts.tabSize;
|
|
3121
|
+
}
|
|
3122
|
+
deindent() {
|
|
3123
|
+
this.currentIndent -= this.opts.tabSize;
|
|
3124
|
+
}
|
|
3125
|
+
writeIndent() {
|
|
3126
|
+
this.write(" ".repeat(this.currentIndent));
|
|
3127
|
+
}
|
|
3128
|
+
line(...tokens) {
|
|
3129
|
+
if (tokens.length === 0) {
|
|
3130
|
+
this.write("\n");
|
|
3131
|
+
} else {
|
|
3132
|
+
this.writeIndent();
|
|
3133
|
+
this.write(`${tokens.join(" ")}
|
|
3134
|
+
`);
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
lineSM(...tokens) {
|
|
3138
|
+
this.writeIndent();
|
|
3139
|
+
this.write(`${tokens.join(" ")};
|
|
3140
|
+
`);
|
|
3141
|
+
}
|
|
3142
|
+
comment(...tokens) {
|
|
3143
|
+
const lines = tokens.join(" ").split("\n");
|
|
3144
|
+
for (const line of lines) {
|
|
3145
|
+
this.line(this.opts.commentLinePrefix, line);
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
debugComment(...tokens) {
|
|
3149
|
+
if (this.opts.withDebugComment) {
|
|
3150
|
+
tokens = tokens.map((token) => {
|
|
3151
|
+
if (typeof token === "string") {
|
|
3152
|
+
return token;
|
|
3153
|
+
} else {
|
|
3154
|
+
return JSON.stringify(token, null, 2);
|
|
3155
|
+
}
|
|
3156
|
+
});
|
|
3157
|
+
this.comment(...tokens);
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
disclaimer() {
|
|
3161
|
+
return [
|
|
3162
|
+
"WARNING: This file is autogenerated by @atomic-ehr/codegen.",
|
|
3163
|
+
"GitHub: https://github.com/orgs/atomic-ehr/repositories",
|
|
3164
|
+
"Any manual changes made to this file may be overwritten."
|
|
3165
|
+
];
|
|
3166
|
+
}
|
|
3167
|
+
generateDisclaimer() {
|
|
3168
|
+
this.disclaimer().forEach((e) => this.comment(e));
|
|
3169
|
+
this.line();
|
|
3170
|
+
}
|
|
3171
|
+
indentBlock(gencontent) {
|
|
3172
|
+
this.indent();
|
|
3173
|
+
gencontent();
|
|
3174
|
+
this.deindent();
|
|
3175
|
+
}
|
|
3176
|
+
curlyBlock(tokens, gencontent, endTokens) {
|
|
3177
|
+
this.line(`${tokens.filter(Boolean).join(" ")} {`);
|
|
3178
|
+
this.indent();
|
|
3179
|
+
gencontent();
|
|
3180
|
+
this.deindent();
|
|
3181
|
+
this.line(`}${endTokens?.filter(Boolean).join(" ") ?? ""}`);
|
|
3182
|
+
}
|
|
3183
|
+
squareBlock(tokens, gencontent, endTokens) {
|
|
3184
|
+
this.line(`${tokens.filter(Boolean).join(" ")} [`);
|
|
3185
|
+
this.indent();
|
|
3186
|
+
gencontent();
|
|
3187
|
+
this.deindent();
|
|
3188
|
+
this.line(`]${endTokens?.filter(Boolean).join(" ") ?? ""}`);
|
|
3189
|
+
}
|
|
3190
|
+
};
|
|
3191
|
+
|
|
3192
|
+
// src/api/writer-generator/csharp/formatHelper.ts
|
|
3193
|
+
var ops = {
|
|
3194
|
+
"!": "Not",
|
|
3195
|
+
"<=": "LessOrEqual",
|
|
3196
|
+
">=": "GreaterOrEqual",
|
|
3197
|
+
"<": "Less",
|
|
3198
|
+
">": "Greater",
|
|
3199
|
+
"=": "Equal",
|
|
3200
|
+
"-": "Dash",
|
|
3201
|
+
"+": "Plus",
|
|
3202
|
+
"*": "Asterisk",
|
|
3203
|
+
"/": "Slash",
|
|
3204
|
+
"%": "Percent",
|
|
3205
|
+
"&": "And",
|
|
3206
|
+
"|": "Or",
|
|
3207
|
+
"^": "Xor",
|
|
3208
|
+
"~": "Tilde",
|
|
3209
|
+
"?": "Question",
|
|
3210
|
+
".": "Dot"
|
|
3211
|
+
};
|
|
3212
|
+
function formatEnumDashHandle(entry) {
|
|
3213
|
+
return entry.split("-").map((part) => uppercaseFirstLetter(part)).join("-");
|
|
3214
|
+
}
|
|
3215
|
+
function formatEnumEntryOperation(entry) {
|
|
3216
|
+
let res = entry;
|
|
3217
|
+
for (const op in ops) res = res.replaceAll(op, ops[op] ?? "");
|
|
3218
|
+
return res;
|
|
3219
|
+
}
|
|
3220
|
+
function formatEnumNumber(entry) {
|
|
3221
|
+
const num = Number(entry[0]);
|
|
3222
|
+
if (Number.isInteger(num) && !Number.isNaN(num)) {
|
|
3223
|
+
return `_${entry}`;
|
|
3224
|
+
}
|
|
3225
|
+
return entry;
|
|
3226
|
+
}
|
|
3227
|
+
function formatEnumEntry(entry) {
|
|
3228
|
+
let res = formatEnumDashHandle(entry);
|
|
3229
|
+
res = formatEnumNumber(res);
|
|
3230
|
+
res = formatEnumEntryOperation(res);
|
|
3231
|
+
res = uppercaseFirstLetter(res);
|
|
3232
|
+
return res;
|
|
3233
|
+
}
|
|
3234
|
+
function formatName(input) {
|
|
3235
|
+
return uppercaseFirstLetter(camelCase(input.replaceAll(".", "-")));
|
|
3236
|
+
}
|
|
3237
|
+
|
|
3238
|
+
// src/api/writer-generator/csharp/csharp.ts
|
|
3239
|
+
var PRIMITIVE_TYPE_MAP = {
|
|
3240
|
+
boolean: "bool",
|
|
3241
|
+
instant: "string",
|
|
3242
|
+
time: "string",
|
|
3243
|
+
date: "string",
|
|
3244
|
+
dateTime: "string",
|
|
3245
|
+
decimal: "decimal",
|
|
3246
|
+
integer: "int",
|
|
3247
|
+
unsignedInt: "long",
|
|
3248
|
+
positiveInt: "long",
|
|
3249
|
+
integer64: "long",
|
|
3250
|
+
base64Binary: "string",
|
|
3251
|
+
uri: "string",
|
|
3252
|
+
url: "string",
|
|
3253
|
+
canonical: "string",
|
|
3254
|
+
oid: "string",
|
|
3255
|
+
uuid: "string",
|
|
3256
|
+
string: "string",
|
|
3257
|
+
code: "string",
|
|
3258
|
+
markdown: "string",
|
|
3259
|
+
id: "string",
|
|
3260
|
+
xhtml: "string"
|
|
3261
|
+
};
|
|
3262
|
+
var RESERVED_TYPE_NAMES = ["Reference", "Expression"];
|
|
3263
|
+
var getFieldModifiers = (field) => {
|
|
3264
|
+
return field.required ? ["required"] : [];
|
|
3265
|
+
};
|
|
3266
|
+
var formatClassName = (schema) => {
|
|
3267
|
+
const name = prefixReservedTypeName(getResourceName(schema.identifier));
|
|
3268
|
+
return uppercaseFirstLetter(name);
|
|
3269
|
+
};
|
|
3270
|
+
var formatBaseClass = (schema) => {
|
|
3271
|
+
return schema.base ? `: ${schema.base.name}` : "";
|
|
3272
|
+
};
|
|
3273
|
+
var canonicalToName = (canonical, dropFragment = true) => {
|
|
3274
|
+
if (!canonical) return void 0;
|
|
3275
|
+
let localName = canonical.split("/").pop();
|
|
3276
|
+
if (!localName) return void 0;
|
|
3277
|
+
if (dropFragment && localName.includes("#")) localName = localName.split("#")[0];
|
|
3278
|
+
if (!localName) return void 0;
|
|
3279
|
+
if (/^\d/.test(localName)) {
|
|
3280
|
+
localName = `number_${localName}`;
|
|
3281
|
+
}
|
|
3282
|
+
return formatName(localName);
|
|
3283
|
+
};
|
|
3284
|
+
var getResourceName = (id) => {
|
|
3285
|
+
if (id.kind === "nested") {
|
|
3286
|
+
const url = id.url;
|
|
3287
|
+
const path = canonicalToName(url, false);
|
|
3288
|
+
if (!path) return "";
|
|
3289
|
+
const [resourceName, fragment] = path.split("#");
|
|
3290
|
+
const name = uppercaseFirstLetterOfEach((fragment ?? "").split(".")).join("");
|
|
3291
|
+
return formatName([resourceName, name].join(""));
|
|
3292
|
+
}
|
|
3293
|
+
return formatName(id.name);
|
|
3294
|
+
};
|
|
3295
|
+
var isReservedTypeName = (name) => RESERVED_TYPE_NAMES.includes(name);
|
|
3296
|
+
var prefixReservedTypeName = (name) => isReservedTypeName(name) ? `Resource${name}` : name;
|
|
3297
|
+
var CSharp = class extends Writer {
|
|
3298
|
+
enums = {};
|
|
3299
|
+
staticSourceDir;
|
|
3300
|
+
targetNamespace;
|
|
3301
|
+
constructor(options) {
|
|
3302
|
+
super({
|
|
3303
|
+
outputDir: options.outputDir,
|
|
3304
|
+
tabSize: 4,
|
|
3305
|
+
withDebugComment: false,
|
|
3306
|
+
commentLinePrefix: "//",
|
|
3307
|
+
logger: options.logger
|
|
3308
|
+
});
|
|
3309
|
+
this.staticSourceDir = options.staticSourceDir;
|
|
3310
|
+
this.targetNamespace = options.targetNamespace;
|
|
3311
|
+
}
|
|
3312
|
+
generate(typeSchemaIndex) {
|
|
3313
|
+
const complexTypes = typeSchemaIndex.collectComplexTypes();
|
|
3314
|
+
const resources = typeSchemaIndex.collectResources();
|
|
3315
|
+
const packages = Array.from(new Set(resources.map((r) => formatName(r.identifier.package))));
|
|
3316
|
+
this.generateAllFiles(complexTypes, resources, packages);
|
|
3317
|
+
this.copyStaticFiles();
|
|
3318
|
+
}
|
|
3319
|
+
generateAllFiles(complexTypes, resources, packages) {
|
|
3320
|
+
this.generateUsingFile(packages);
|
|
3321
|
+
this.generateBaseTypes(complexTypes);
|
|
3322
|
+
this.generateResources(resources);
|
|
3323
|
+
this.generateEnumFiles(packages);
|
|
3324
|
+
this.generateResourceDictionaries(resources, packages);
|
|
3325
|
+
this.generateHelperFile();
|
|
3326
|
+
}
|
|
3327
|
+
generateType(schema, packageName) {
|
|
3328
|
+
const className = formatClassName(schema);
|
|
3329
|
+
const baseClass = formatBaseClass(schema);
|
|
3330
|
+
this.curlyBlock(["public", "class", className, baseClass], () => {
|
|
3331
|
+
this.generateFields(schema, packageName);
|
|
3332
|
+
this.generateNestedTypes(schema, packageName);
|
|
3333
|
+
this.line();
|
|
3334
|
+
this.includeHelperMethods();
|
|
3335
|
+
});
|
|
3336
|
+
this.line();
|
|
3337
|
+
}
|
|
3338
|
+
generateFields(schema, packageName) {
|
|
3339
|
+
if (!schema.fields) return;
|
|
3340
|
+
const sortedFields = Object.entries(schema.fields).sort(([a], [b]) => a.localeCompare(b));
|
|
3341
|
+
for (const [fieldName, field] of sortedFields) {
|
|
3342
|
+
this.generateField(fieldName, field, packageName);
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
generateNestedTypes(schema, packageName) {
|
|
3346
|
+
if (!("nested" in schema) || !schema.nested) return;
|
|
3347
|
+
this.line();
|
|
3348
|
+
for (const subtype of schema.nested) {
|
|
3349
|
+
this.generateType(subtype, packageName);
|
|
3350
|
+
}
|
|
3351
|
+
}
|
|
3352
|
+
generateField(fieldName, field, packageName) {
|
|
3353
|
+
try {
|
|
3354
|
+
if (isChoiceDeclarationField(field)) return;
|
|
3355
|
+
const fieldDeclaration = this.buildFieldDeclaration(fieldName, field, packageName);
|
|
3356
|
+
this.line(...fieldDeclaration);
|
|
3357
|
+
} catch (error) {
|
|
3358
|
+
this.logger()?.error(`Error processing field ${fieldName}: ${error.message}`);
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
buildFieldDeclaration(fieldName, field, packageName) {
|
|
3362
|
+
const fieldType = this.determineFieldType(fieldName, field, packageName);
|
|
3363
|
+
const modifiers = getFieldModifiers(field);
|
|
3364
|
+
const propertyName = pascalCase(fieldName);
|
|
3365
|
+
const accessors = "{ get; set; }";
|
|
3366
|
+
return ["public", ...modifiers, fieldType, propertyName, accessors].filter(Boolean);
|
|
3367
|
+
}
|
|
3368
|
+
determineFieldType(fieldName, field, packageName) {
|
|
3369
|
+
let typeName = this.getBaseTypeName(field);
|
|
3370
|
+
if ("enum" in field && field.enum) {
|
|
3371
|
+
typeName = this.registerAndGetEnumType(fieldName, field, packageName);
|
|
3372
|
+
}
|
|
3373
|
+
typeName = prefixReservedTypeName(typeName);
|
|
3374
|
+
const baseNamespacePrefix = "";
|
|
3375
|
+
const nullable = field.required ? "" : "?";
|
|
3376
|
+
const arraySpecifier = field.array ? "[]" : "";
|
|
3377
|
+
return `${baseNamespacePrefix}${typeName}${arraySpecifier}${nullable}`;
|
|
3378
|
+
}
|
|
3379
|
+
getBaseTypeName(field) {
|
|
3380
|
+
if ("type" in field) {
|
|
3381
|
+
let typeName = field.type.name.toString();
|
|
3382
|
+
if (field.type.kind === "nested") {
|
|
3383
|
+
typeName = getResourceName(field.type);
|
|
3384
|
+
} else if (field.type.kind === "primitive-type") typeName = PRIMITIVE_TYPE_MAP[field.type.name] ?? "string";
|
|
3385
|
+
return typeName;
|
|
3386
|
+
}
|
|
3387
|
+
return "";
|
|
3388
|
+
}
|
|
3389
|
+
registerAndGetEnumType(fieldName, field, packageName) {
|
|
3390
|
+
const enumName = formatName(field.binding?.name ?? fieldName);
|
|
3391
|
+
const enumTypeName = `${enumName}Enum`;
|
|
3392
|
+
if (!this.enums[packageName]) this.enums[packageName] = {};
|
|
3393
|
+
if (field.enum) this.enums[packageName][enumTypeName] = field.enum;
|
|
3394
|
+
return enumTypeName;
|
|
3395
|
+
}
|
|
3396
|
+
includeHelperMethods() {
|
|
3397
|
+
this.line("public override string ToString() => ");
|
|
3398
|
+
this.line(" JsonSerializer.Serialize(this, Helper.JsonSerializerOptions);");
|
|
3399
|
+
this.line();
|
|
3400
|
+
}
|
|
3401
|
+
generateUsingFile(packages) {
|
|
3402
|
+
this.cd("/", async () => {
|
|
3403
|
+
this.cat("Usings.cs", () => {
|
|
3404
|
+
this.generateDisclaimer();
|
|
3405
|
+
this.generateGlobalUsings(packages);
|
|
3406
|
+
});
|
|
3407
|
+
});
|
|
3408
|
+
}
|
|
3409
|
+
generateGlobalUsings(packages) {
|
|
3410
|
+
const globalUsings = [
|
|
3411
|
+
"CSharpSDK",
|
|
3412
|
+
"System.Text.Json",
|
|
3413
|
+
"System.Text.Json.Serialization",
|
|
3414
|
+
this.targetNamespace,
|
|
3415
|
+
...packages.map((pkg) => `${this.targetNamespace}.${pkg}`)
|
|
3416
|
+
];
|
|
3417
|
+
for (const using of globalUsings) this.lineSM("global", "using", using);
|
|
3418
|
+
}
|
|
3419
|
+
generateBaseTypes(complexTypes) {
|
|
3420
|
+
this.cd("/", async () => {
|
|
3421
|
+
this.cat("base.cs", () => {
|
|
3422
|
+
this.generateDisclaimer();
|
|
3423
|
+
this.line();
|
|
3424
|
+
this.lineSM("namespace", this.targetNamespace);
|
|
3425
|
+
for (const schema of complexTypes) {
|
|
3426
|
+
const packageName = formatName(schema.identifier.package);
|
|
3427
|
+
this.generateType(schema, packageName);
|
|
3428
|
+
}
|
|
3429
|
+
});
|
|
3430
|
+
});
|
|
3431
|
+
}
|
|
3432
|
+
generateResources(resources) {
|
|
3433
|
+
for (const schema of resources) this.generateResourceFile(schema);
|
|
3434
|
+
}
|
|
3435
|
+
generateResourceFile(schema) {
|
|
3436
|
+
const packageName = formatName(schema.identifier.package);
|
|
3437
|
+
this.cd(`/${packageName}`, async () => {
|
|
3438
|
+
this.cat(`${schema.identifier.name}.cs`, () => {
|
|
3439
|
+
this.generateDisclaimer();
|
|
3440
|
+
this.line();
|
|
3441
|
+
this.lineSM("namespace", `${this.targetNamespace}.${packageName}`);
|
|
3442
|
+
this.line();
|
|
3443
|
+
this.generateType(schema, packageName);
|
|
3444
|
+
});
|
|
3445
|
+
});
|
|
3446
|
+
}
|
|
3447
|
+
generateEnumFiles(packages) {
|
|
3448
|
+
for (const packageName of packages) {
|
|
3449
|
+
this.generatePackageEnums(packageName);
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
generatePackageEnums(packageName) {
|
|
3453
|
+
const packageEnums = this.enums[packageName];
|
|
3454
|
+
if (!packageEnums || Object.keys(packageEnums).length === 0) return;
|
|
3455
|
+
this.cd(`/${packageName}`, async () => {
|
|
3456
|
+
this.cat(`${packageName}Enums.cs`, () => {
|
|
3457
|
+
this.generateDisclaimer();
|
|
3458
|
+
this.generateEnumFileContent(packageName, packageEnums);
|
|
3459
|
+
});
|
|
3460
|
+
});
|
|
3461
|
+
}
|
|
3462
|
+
generateEnumFileContent(packageName, enums) {
|
|
3463
|
+
this.lineSM("using", "System.ComponentModel");
|
|
3464
|
+
this.line();
|
|
3465
|
+
this.lineSM(`namespace ${this.targetNamespace}.${packageName}`);
|
|
3466
|
+
for (const [enumName, values] of Object.entries(enums)) {
|
|
3467
|
+
this.generateEnum(enumName, values);
|
|
3468
|
+
}
|
|
3469
|
+
}
|
|
3470
|
+
generateEnum(enumName, values) {
|
|
3471
|
+
this.curlyBlock(["public", "enum", enumName], () => {
|
|
3472
|
+
for (const value of values) {
|
|
3473
|
+
this.line(`[Description("${value}")]`);
|
|
3474
|
+
this.line(`${formatEnumEntry(value)},`);
|
|
3475
|
+
}
|
|
3476
|
+
});
|
|
3477
|
+
this.line();
|
|
3478
|
+
}
|
|
3479
|
+
generateResourceDictionaries(resources, packages) {
|
|
3480
|
+
this.cd("/", async () => {
|
|
3481
|
+
for (const packageName of packages) {
|
|
3482
|
+
const packageResources = resources.filter((r) => formatName(r.identifier.package) === packageName);
|
|
3483
|
+
if (packageResources.length === 0) return;
|
|
3484
|
+
this.cat(`${packageName}ResourceDictionary.cs`, () => {
|
|
3485
|
+
this.generateDisclaimer();
|
|
3486
|
+
this.line();
|
|
3487
|
+
this.lineSM(`namespace ${this.targetNamespace}`);
|
|
3488
|
+
this.generateResourceDictionaryClass(packageName, packageResources);
|
|
3489
|
+
});
|
|
3490
|
+
}
|
|
3491
|
+
});
|
|
3492
|
+
}
|
|
3493
|
+
generateResourceDictionaryClass(packageName, resources) {
|
|
3494
|
+
this.curlyBlock(["public", "static", "class", "ResourceDictionary"], () => {
|
|
3495
|
+
this.curlyBlock(["public static readonly Dictionary<Type, string> Map = new()"], () => {
|
|
3496
|
+
for (const schema of resources) {
|
|
3497
|
+
const typeName = schema.identifier.name;
|
|
3498
|
+
this.line(`{ typeof(${packageName}.${typeName}), "${typeName}" },`);
|
|
3499
|
+
}
|
|
3500
|
+
});
|
|
3501
|
+
this.lineSM();
|
|
3502
|
+
});
|
|
3503
|
+
}
|
|
3504
|
+
copyStaticFiles() {
|
|
3505
|
+
if (!this.staticSourceDir) return;
|
|
3506
|
+
const sourcePath = Path4__default.resolve(this.staticSourceDir);
|
|
3507
|
+
fs__default.cpSync(sourcePath, this.opts.outputDir, { recursive: true });
|
|
3508
|
+
}
|
|
3509
|
+
generateHelperFile() {
|
|
3510
|
+
const sourceFile = "src/api/writer-generator/csharp/Helper.cs";
|
|
3511
|
+
const destFile = Path4__default.join(this.opts.outputDir, "Helper.cs");
|
|
3512
|
+
fs__default.copyFileSync(sourceFile, destFile);
|
|
3513
|
+
}
|
|
3514
|
+
};
|
|
3015
3515
|
|
|
3016
3516
|
// src/typeschema/utils.ts
|
|
3017
3517
|
var groupByPackages = (typeSchemas) => {
|
|
@@ -5188,127 +5688,6 @@ ${nestedInterfaces}`;
|
|
|
5188
5688
|
);
|
|
5189
5689
|
}
|
|
5190
5690
|
};
|
|
5191
|
-
var FileSystemWriter = class {
|
|
5192
|
-
opts;
|
|
5193
|
-
currentDir;
|
|
5194
|
-
currentFileDescriptor;
|
|
5195
|
-
writtenFilesSet = /* @__PURE__ */ new Set();
|
|
5196
|
-
constructor(opts) {
|
|
5197
|
-
this.opts = opts;
|
|
5198
|
-
this.currentDir = opts.outputDir;
|
|
5199
|
-
}
|
|
5200
|
-
logger() {
|
|
5201
|
-
return this.opts.logger;
|
|
5202
|
-
}
|
|
5203
|
-
cd(path, gen) {
|
|
5204
|
-
this.currentDir = path.startsWith("/") ? Path2.join(this.opts.outputDir, path) : Path2.join(this.currentDir, path);
|
|
5205
|
-
if (!fs.existsSync(this.currentDir)) {
|
|
5206
|
-
fs.mkdirSync(this.currentDir, { recursive: true });
|
|
5207
|
-
}
|
|
5208
|
-
this.logger()?.debug(`cd '${this.currentDir}'`);
|
|
5209
|
-
gen();
|
|
5210
|
-
}
|
|
5211
|
-
cat(fn, gen) {
|
|
5212
|
-
if (this.currentFileDescriptor) throw new Error("Can't open file in file");
|
|
5213
|
-
if (fn.includes("/")) throw new Error(`Change file path separatly: ${fn}`);
|
|
5214
|
-
const fullFn = `${this.currentDir}/${fn}`;
|
|
5215
|
-
try {
|
|
5216
|
-
this.currentFileDescriptor = fs.openSync(fullFn, "w");
|
|
5217
|
-
this.writtenFilesSet.add(fn);
|
|
5218
|
-
this.logger()?.debug(`cat > '${fullFn}'`);
|
|
5219
|
-
gen();
|
|
5220
|
-
} finally {
|
|
5221
|
-
if (this.currentFileDescriptor) {
|
|
5222
|
-
fs.closeSync(this.currentFileDescriptor);
|
|
5223
|
-
}
|
|
5224
|
-
this.currentFileDescriptor = void 0;
|
|
5225
|
-
}
|
|
5226
|
-
}
|
|
5227
|
-
write(str) {
|
|
5228
|
-
if (!this.currentFileDescriptor) throw new Error("No file opened");
|
|
5229
|
-
fs.writeSync(this.currentFileDescriptor, str);
|
|
5230
|
-
}
|
|
5231
|
-
generate(_tsIndex) {
|
|
5232
|
-
throw new Error("Not implemented");
|
|
5233
|
-
}
|
|
5234
|
-
writtenFiles() {
|
|
5235
|
-
return Array.from(this.writtenFilesSet);
|
|
5236
|
-
}
|
|
5237
|
-
};
|
|
5238
|
-
var Writer = class extends FileSystemWriter {
|
|
5239
|
-
currentIndent = 0;
|
|
5240
|
-
indent() {
|
|
5241
|
-
this.currentIndent += this.opts.tabSize;
|
|
5242
|
-
}
|
|
5243
|
-
deindent() {
|
|
5244
|
-
this.currentIndent -= this.opts.tabSize;
|
|
5245
|
-
}
|
|
5246
|
-
writeIndent() {
|
|
5247
|
-
this.write(" ".repeat(this.currentIndent));
|
|
5248
|
-
}
|
|
5249
|
-
line(...tokens) {
|
|
5250
|
-
if (tokens.length === 0) {
|
|
5251
|
-
this.write("\n");
|
|
5252
|
-
} else {
|
|
5253
|
-
this.writeIndent();
|
|
5254
|
-
this.write(`${tokens.join(" ")}
|
|
5255
|
-
`);
|
|
5256
|
-
}
|
|
5257
|
-
}
|
|
5258
|
-
lineSM(...tokens) {
|
|
5259
|
-
this.writeIndent();
|
|
5260
|
-
this.write(`${tokens.join(" ")};
|
|
5261
|
-
`);
|
|
5262
|
-
}
|
|
5263
|
-
comment(...tokens) {
|
|
5264
|
-
const lines = tokens.join(" ").split("\n");
|
|
5265
|
-
for (const line of lines) {
|
|
5266
|
-
this.line(this.opts.commentLinePrefix, line);
|
|
5267
|
-
}
|
|
5268
|
-
}
|
|
5269
|
-
debugComment(...tokens) {
|
|
5270
|
-
if (this.opts.withDebugComment) {
|
|
5271
|
-
tokens = tokens.map((token) => {
|
|
5272
|
-
if (typeof token === "string") {
|
|
5273
|
-
return token;
|
|
5274
|
-
} else {
|
|
5275
|
-
return JSON.stringify(token, null, 2);
|
|
5276
|
-
}
|
|
5277
|
-
});
|
|
5278
|
-
this.comment(...tokens);
|
|
5279
|
-
}
|
|
5280
|
-
}
|
|
5281
|
-
disclaimer() {
|
|
5282
|
-
return [
|
|
5283
|
-
"WARNING: This file is autogenerated by @atomic-ehr/codegen.",
|
|
5284
|
-
"GitHub: https://github.com/orgs/atomic-ehr/repositories",
|
|
5285
|
-
"Any manual changes made to this file may be overwritten."
|
|
5286
|
-
];
|
|
5287
|
-
}
|
|
5288
|
-
generateDisclaimer() {
|
|
5289
|
-
this.disclaimer().forEach((e) => this.comment(e));
|
|
5290
|
-
this.line();
|
|
5291
|
-
}
|
|
5292
|
-
indentBlock(gencontent) {
|
|
5293
|
-
this.indent();
|
|
5294
|
-
gencontent();
|
|
5295
|
-
this.deindent();
|
|
5296
|
-
}
|
|
5297
|
-
curlyBlock(tokens, gencontent, endTokens) {
|
|
5298
|
-
this.line(`${tokens.filter(Boolean).join(" ")} {`);
|
|
5299
|
-
this.indent();
|
|
5300
|
-
gencontent();
|
|
5301
|
-
this.deindent();
|
|
5302
|
-
this.line(`}${endTokens?.filter(Boolean).join(" ") ?? ""}`);
|
|
5303
|
-
}
|
|
5304
|
-
squareBlock(tokens, gencontent, endTokens) {
|
|
5305
|
-
this.line(`${tokens.filter(Boolean).join(" ")} [`);
|
|
5306
|
-
this.indent();
|
|
5307
|
-
gencontent();
|
|
5308
|
-
this.deindent();
|
|
5309
|
-
this.line(`]${endTokens?.filter(Boolean).join(" ") ?? ""}`);
|
|
5310
|
-
}
|
|
5311
|
-
};
|
|
5312
5691
|
|
|
5313
5692
|
// src/api/writer-generator/typescript.ts
|
|
5314
5693
|
var primitiveType2tsType = {
|
|
@@ -5349,7 +5728,7 @@ var tsModuleName = (id) => {
|
|
|
5349
5728
|
var tsModuleFileName = (id) => {
|
|
5350
5729
|
return `${tsModuleName(id)}.ts`;
|
|
5351
5730
|
};
|
|
5352
|
-
var
|
|
5731
|
+
var canonicalToName2 = (canonical, dropFragment = true) => {
|
|
5353
5732
|
if (!canonical) return void 0;
|
|
5354
5733
|
let localName = canonical.split("/").pop();
|
|
5355
5734
|
if (!localName) return void 0;
|
|
@@ -5365,7 +5744,7 @@ var canonicalToName = (canonical, dropFragment = true) => {
|
|
|
5365
5744
|
var tsResourceName = (id) => {
|
|
5366
5745
|
if (id.kind === "nested") {
|
|
5367
5746
|
const url = id.url;
|
|
5368
|
-
const path =
|
|
5747
|
+
const path = canonicalToName2(url, false);
|
|
5369
5748
|
if (!path) return "";
|
|
5370
5749
|
const [resourceName, fragment] = path.split("#");
|
|
5371
5750
|
const name = uppercaseFirstLetterOfEach((fragment ?? "").split(".")).join("");
|
|
@@ -5419,7 +5798,7 @@ var TypeScript = class extends Writer {
|
|
|
5419
5798
|
});
|
|
5420
5799
|
} else if (isNestedIdentifier(dep)) {
|
|
5421
5800
|
imports.push({
|
|
5422
|
-
tsPackage: `../${kebabCase(dep.package)}/${pascalCase(
|
|
5801
|
+
tsPackage: `../${kebabCase(dep.package)}/${pascalCase(canonicalToName2(dep.url) ?? "")}`,
|
|
5423
5802
|
name: tsResourceName(dep)
|
|
5424
5803
|
});
|
|
5425
5804
|
} else {
|
|
@@ -5464,7 +5843,7 @@ var TypeScript = class extends Writer {
|
|
|
5464
5843
|
name = tsResourceName(schema.identifier);
|
|
5465
5844
|
}
|
|
5466
5845
|
let extendsClause;
|
|
5467
|
-
if (schema.base) extendsClause = `extends ${
|
|
5846
|
+
if (schema.base) extendsClause = `extends ${canonicalToName2(schema.base.url)}`;
|
|
5468
5847
|
this.debugComment(schema.identifier);
|
|
5469
5848
|
this.curlyBlock(["export", "interface", name, extendsClause], () => {
|
|
5470
5849
|
if (isResourceTypeSchema(schema)) {
|
|
@@ -5704,7 +6083,7 @@ var writerToGenerator = (writerGen) => {
|
|
|
5704
6083
|
const getGeneratedFiles = () => {
|
|
5705
6084
|
return writerGen.writtenFiles().map((fn) => {
|
|
5706
6085
|
return {
|
|
5707
|
-
path:
|
|
6086
|
+
path: Path4.normalize(Path4.join(writerGen.opts.outputDir, fn)),
|
|
5708
6087
|
filename: fn.replace(/^.*[\\/]/, ""),
|
|
5709
6088
|
content: "",
|
|
5710
6089
|
exports: [],
|
|
@@ -5724,7 +6103,7 @@ var writerToGenerator = (writerGen) => {
|
|
|
5724
6103
|
};
|
|
5725
6104
|
};
|
|
5726
6105
|
var normalizeFileName = (str) => str.replace(/[^a-zA-Z0-9]/g, "");
|
|
5727
|
-
var APIBuilder = class {
|
|
6106
|
+
var APIBuilder = class _APIBuilder {
|
|
5728
6107
|
schemas = [];
|
|
5729
6108
|
options;
|
|
5730
6109
|
generators = /* @__PURE__ */ new Map();
|
|
@@ -5804,7 +6183,7 @@ var APIBuilder = class {
|
|
|
5804
6183
|
}
|
|
5805
6184
|
typescript2(opts) {
|
|
5806
6185
|
const writerOpts = {
|
|
5807
|
-
outputDir:
|
|
6186
|
+
outputDir: Path4.join(this.options.outputDir, "/types"),
|
|
5808
6187
|
tabSize: 4,
|
|
5809
6188
|
withDebugComment: false,
|
|
5810
6189
|
commentLinePrefix: "//"
|
|
@@ -5815,6 +6194,24 @@ var APIBuilder = class {
|
|
|
5815
6194
|
this.logger.debug(`Configured TypeScript2 generator (${JSON.stringify(effectiveOpts, void 0, 2)})`);
|
|
5816
6195
|
return this;
|
|
5817
6196
|
}
|
|
6197
|
+
csharp(namespace, staticSourceDir) {
|
|
6198
|
+
const generator = writerToGenerator(
|
|
6199
|
+
new CSharp({
|
|
6200
|
+
outputDir: Path4.join(this.options.outputDir, "/types"),
|
|
6201
|
+
staticSourceDir: staticSourceDir ?? void 0,
|
|
6202
|
+
targetNamespace: namespace,
|
|
6203
|
+
logger: new CodegenLogger({
|
|
6204
|
+
prefix: "C#",
|
|
6205
|
+
timestamp: true,
|
|
6206
|
+
verbose: true,
|
|
6207
|
+
suppressLoggingLevel: []
|
|
6208
|
+
})
|
|
6209
|
+
})
|
|
6210
|
+
);
|
|
6211
|
+
this.generators.set("C#", generator);
|
|
6212
|
+
this.logger.debug(`Configured C# generator`);
|
|
6213
|
+
return this;
|
|
6214
|
+
}
|
|
5818
6215
|
/**
|
|
5819
6216
|
* Set a progress callback for monitoring generation
|
|
5820
6217
|
*/
|
|
@@ -5852,33 +6249,53 @@ var APIBuilder = class {
|
|
|
5852
6249
|
this.options.typeSchemaOutputDir = target;
|
|
5853
6250
|
return this;
|
|
5854
6251
|
}
|
|
6252
|
+
static async isIdenticalTo(path, tsJSON) {
|
|
6253
|
+
if (!await afs2.exists(path)) return false;
|
|
6254
|
+
const json = await afs2.readFile(path);
|
|
6255
|
+
const ts1 = JSON.parse(json.toString());
|
|
6256
|
+
const ts2 = JSON.parse(tsJSON);
|
|
6257
|
+
return deepEqual(ts1, ts2);
|
|
6258
|
+
}
|
|
6259
|
+
async writeTypeSchemasToSeparateFiles(typeSchemas, outputDir) {
|
|
6260
|
+
if (this.options.cleanOutput) fs.rmSync(outputDir, { recursive: true, force: true });
|
|
6261
|
+
await afs2.mkdir(outputDir, { recursive: true });
|
|
6262
|
+
const usedNames = {};
|
|
6263
|
+
this.logger.info(`Writing TypeSchema files to ${outputDir}...`);
|
|
6264
|
+
for (const ts of typeSchemas) {
|
|
6265
|
+
const package_name = camelCase(ts.identifier.package.replaceAll("/", "-"));
|
|
6266
|
+
const name = normalizeFileName(ts.identifier.name.toString());
|
|
6267
|
+
const json = JSON.stringify(ts, null, 2);
|
|
6268
|
+
const baseName = Path4.join(outputDir, package_name, name);
|
|
6269
|
+
let fullName;
|
|
6270
|
+
if (usedNames[baseName] !== void 0) {
|
|
6271
|
+
usedNames[baseName]++;
|
|
6272
|
+
fullName = `${baseName}-${usedNames[baseName]}.typeschema.json`;
|
|
6273
|
+
} else {
|
|
6274
|
+
usedNames[baseName] = 0;
|
|
6275
|
+
fullName = `${baseName}.typeschema.json`;
|
|
6276
|
+
}
|
|
6277
|
+
if (await _APIBuilder.isIdenticalTo(fullName, json)) continue;
|
|
6278
|
+
await afs2.mkdir(Path4.dirname(fullName), { recursive: true });
|
|
6279
|
+
await afs2.writeFile(fullName, json);
|
|
6280
|
+
}
|
|
6281
|
+
}
|
|
6282
|
+
async writeTypeSchemasToSingleFile(typeSchemas, outputFile) {
|
|
6283
|
+
if (this.options.cleanOutput && fs.existsSync(outputFile)) fs.rmSync(outputFile);
|
|
6284
|
+
await afs2.mkdir(Path4.dirname(outputFile), { recursive: true });
|
|
6285
|
+
this.logger.info(`Writing TypeSchemas to one file ${outputFile}...`);
|
|
6286
|
+
for (const ts of typeSchemas) {
|
|
6287
|
+
const json = JSON.stringify(ts, null, 2);
|
|
6288
|
+
await afs2.appendFile(outputFile, json + "\n");
|
|
6289
|
+
}
|
|
6290
|
+
}
|
|
5855
6291
|
async tryWriteTypeSchema(typeSchemas) {
|
|
5856
6292
|
if (!this.options.typeSchemaOutputDir) return;
|
|
5857
6293
|
try {
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
this.logger.info(`Writing TypeSchema files to ${this.options.typeSchemaOutputDir}...`);
|
|
5864
|
-
for (const ts of typeSchemas) {
|
|
5865
|
-
const package_name = camelCase(ts.identifier.package.replaceAll("/", "-"));
|
|
5866
|
-
const name = normalizeFileName(ts.identifier.name.toString());
|
|
5867
|
-
const baseName = Path2.join(this.options.typeSchemaOutputDir, package_name, name);
|
|
5868
|
-
let fullName;
|
|
5869
|
-
if (usedNames[baseName] !== void 0) {
|
|
5870
|
-
usedNames[baseName]++;
|
|
5871
|
-
fullName = `${baseName}-${usedNames[baseName]}.typeschema.json`;
|
|
5872
|
-
} else {
|
|
5873
|
-
usedNames[baseName] = 0;
|
|
5874
|
-
fullName = `${baseName}.typeschema.json`;
|
|
5875
|
-
}
|
|
5876
|
-
await afs.mkdir(Path2.dirname(fullName), { recursive: true });
|
|
5877
|
-
afs.writeFile(fullName, JSON.stringify(ts, null, 2));
|
|
5878
|
-
if (await afs.exists(fullName)) overrideCount++;
|
|
5879
|
-
else writtenCount++;
|
|
5880
|
-
}
|
|
5881
|
-
this.logger.info(`Created ${writtenCount} new TypeSchema files, overrode ${overrideCount} files`);
|
|
6294
|
+
this.logger.info(`Starting writing TypeSchema files.`);
|
|
6295
|
+
if (Path4.extname(this.options.typeSchemaOutputDir) === ".ndjson")
|
|
6296
|
+
await this.writeTypeSchemasToSingleFile(typeSchemas, this.options.typeSchemaOutputDir);
|
|
6297
|
+
else await this.writeTypeSchemasToSeparateFiles(typeSchemas, this.options.typeSchemaOutputDir);
|
|
6298
|
+
this.logger.info(`Finished writing TypeSchema files.`);
|
|
5882
6299
|
} catch (error) {
|
|
5883
6300
|
if (this.options.throwException) throw error;
|
|
5884
6301
|
this.logger.error(
|