@atomic-ehr/codegen 0.0.1-canary.20251010131311.9439f74 → 0.0.1-canary.20251013100511.ff2ef29
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 +22 -22
- package/dist/index.d.ts +3 -0
- package/dist/index.js +96 -36
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -757,6 +757,7 @@ interface APIBuilderOptions {
|
|
|
757
757
|
typeSchemaConfig?: TypeSchemaConfig;
|
|
758
758
|
logger?: CodegenLogger;
|
|
759
759
|
manager?: ReturnType<typeof CanonicalManager> | null;
|
|
760
|
+
typeSchemaOutputDir?: string /** if .ndjson -- put in one file, else -- split into separated files*/;
|
|
760
761
|
throwException?: boolean;
|
|
761
762
|
}
|
|
762
763
|
/**
|
|
@@ -826,6 +827,8 @@ declare class APIBuilder {
|
|
|
826
827
|
verbose(enabled?: boolean): APIBuilder;
|
|
827
828
|
throwException(enabled?: boolean): APIBuilder;
|
|
828
829
|
cleanOutput(enabled?: boolean): APIBuilder;
|
|
830
|
+
writeTypeSchemas(target: string): this;
|
|
831
|
+
private tryWriteTypeSchema;
|
|
829
832
|
generate(): Promise<GenerationResult>;
|
|
830
833
|
/**
|
|
831
834
|
* Generate and return the results without writing to files
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { CanonicalManager } from '@atomic-ehr/fhir-canonical-manager';
|
|
|
2
2
|
import * as fhirschema from '@atomic-ehr/fhirschema';
|
|
3
3
|
import * as fs from 'fs';
|
|
4
4
|
import { existsSync, mkdirSync } from 'fs';
|
|
5
|
+
import * as afs from 'fs/promises';
|
|
5
6
|
import { readdir, stat, unlink, readFile, writeFile, access, mkdir, rm } from 'fs/promises';
|
|
6
7
|
import * as Path2 from 'path';
|
|
7
8
|
import { join, resolve, dirname, relative } from 'path';
|
|
@@ -2945,6 +2946,33 @@ var generateTypeSchemas = async (register, logger) => {
|
|
|
2945
2946
|
return fhirSchemas;
|
|
2946
2947
|
};
|
|
2947
2948
|
|
|
2949
|
+
// src/api/writer-generator/utils.ts
|
|
2950
|
+
var words = (s) => {
|
|
2951
|
+
return s.split(/(?<=[a-z])(?=[A-Z])|[-_.\s]/).filter(Boolean);
|
|
2952
|
+
};
|
|
2953
|
+
var kebabCase = (s) => {
|
|
2954
|
+
return words(s).map((s2) => s2.toLowerCase()).join("-");
|
|
2955
|
+
};
|
|
2956
|
+
var capitalCase = (s) => {
|
|
2957
|
+
if (s.length === 0) throw new Error("Empty string");
|
|
2958
|
+
return s[0]?.toUpperCase() + s.substring(1).toLowerCase();
|
|
2959
|
+
};
|
|
2960
|
+
var camelCase = (s) => {
|
|
2961
|
+
if (s.length === 0) throw new Error("Empty string");
|
|
2962
|
+
const [first, ...rest] = words(s);
|
|
2963
|
+
return [first?.toLowerCase(), ...rest.map(capitalCase)].join("");
|
|
2964
|
+
};
|
|
2965
|
+
var pascalCase = (s) => {
|
|
2966
|
+
return words(s).map(capitalCase).join("");
|
|
2967
|
+
};
|
|
2968
|
+
var uppercaseFirstLetter = (str) => {
|
|
2969
|
+
if (!str || str.length === 0) return str;
|
|
2970
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
2971
|
+
};
|
|
2972
|
+
var uppercaseFirstLetterOfEach = (strings) => {
|
|
2973
|
+
return strings.map((str) => uppercaseFirstLetter(str));
|
|
2974
|
+
};
|
|
2975
|
+
|
|
2948
2976
|
// src/typeschema/utils.ts
|
|
2949
2977
|
var groupByPackages = (typeSchemas) => {
|
|
2950
2978
|
const grouped = {};
|
|
@@ -4162,8 +4190,8 @@ function toCamelCase(str) {
|
|
|
4162
4190
|
return str.replace(/[-_\s]+(.)?/g, (_, char) => char?.toUpperCase() || "");
|
|
4163
4191
|
}
|
|
4164
4192
|
function toPascalCase(str) {
|
|
4165
|
-
const
|
|
4166
|
-
return
|
|
4193
|
+
const camelCase2 = toCamelCase(str);
|
|
4194
|
+
return camelCase2.charAt(0).toUpperCase() + camelCase2.slice(1);
|
|
4167
4195
|
}
|
|
4168
4196
|
function toSnakeCase(str) {
|
|
4169
4197
|
return str.replace(/([A-Z])/g, "_$1").replace(/[-\s]+/g, "_").toLowerCase().replace(/^_/, "");
|
|
@@ -4890,7 +4918,7 @@ ${nestedInterfaces}`;
|
|
|
4890
4918
|
}
|
|
4891
4919
|
const optional = required ? "" : "?";
|
|
4892
4920
|
const arrayType = isArray ? "[]" : "";
|
|
4893
|
-
return `${fieldName
|
|
4921
|
+
return `${fieldName}${optional}: ${typeString}${arrayType};`;
|
|
4894
4922
|
}
|
|
4895
4923
|
// ==========================================
|
|
4896
4924
|
/**
|
|
@@ -5112,28 +5140,6 @@ ${nestedInterfaces}`;
|
|
|
5112
5140
|
);
|
|
5113
5141
|
}
|
|
5114
5142
|
};
|
|
5115
|
-
|
|
5116
|
-
// src/api/writer-generator/utils.ts
|
|
5117
|
-
var words = (s) => {
|
|
5118
|
-
return s.split(/(?<=[a-z])(?=[A-Z])|[-_.\s]/).filter(Boolean);
|
|
5119
|
-
};
|
|
5120
|
-
var kebabCase = (s) => {
|
|
5121
|
-
return words(s).map((s2) => s2.toLowerCase()).join("-");
|
|
5122
|
-
};
|
|
5123
|
-
var capitalCase = (s) => {
|
|
5124
|
-
if (s.length === 0) throw new Error("Empty string");
|
|
5125
|
-
return s[0]?.toUpperCase() + s.substring(1).toLowerCase();
|
|
5126
|
-
};
|
|
5127
|
-
var pascalCase = (s) => {
|
|
5128
|
-
return words(s).map(capitalCase).join("");
|
|
5129
|
-
};
|
|
5130
|
-
var uppercaseFirstLetter = (str) => {
|
|
5131
|
-
if (!str || str.length === 0) return str;
|
|
5132
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
5133
|
-
};
|
|
5134
|
-
var uppercaseFirstLetterOfEach = (strings) => {
|
|
5135
|
-
return strings.map((str) => uppercaseFirstLetter(str));
|
|
5136
|
-
};
|
|
5137
5143
|
var FileSystemWriter = class {
|
|
5138
5144
|
opts;
|
|
5139
5145
|
currentDir;
|
|
@@ -5319,12 +5325,20 @@ var tsResourceName = (id) => {
|
|
|
5319
5325
|
}
|
|
5320
5326
|
return normalizeTsName(id.name);
|
|
5321
5327
|
};
|
|
5322
|
-
var
|
|
5328
|
+
var tsKeywords = /* @__PURE__ */ new Set(["class", "function", "return", "if", "for", "while", "const", "let", "var", "import", "export", "interface"]);
|
|
5329
|
+
var tsFieldName = (n) => {
|
|
5330
|
+
if (tsKeywords.has(n)) return `"${n}"`;
|
|
5331
|
+
if (n.includes(" ") || n.includes("-")) return `"${n}"`;
|
|
5332
|
+
return n;
|
|
5333
|
+
};
|
|
5323
5334
|
var normalizeTsName = (n) => {
|
|
5324
|
-
const tsKeywords = /* @__PURE__ */ new Set(["abstract", "any", "as", "async", "await", "boolean", "bigint", "break", "case", "catch", "class", "const", "constructor", "continue", "debugger", "declare", "default", "delete", "do", "else", "enum", "export", "extends", "extern", "false", "finally", "for", "function", "from", "get", "goto", "if", "implements", "import", "in", "infer", "instanceof", "interface", "keyof", "let", "module", "namespace", "never", "new", "null", "number", "object", "of", "override", "private", "protected", "public", "readonly", "return", "satisfies", "set", "static", "string", "super", "switch", "this", "throw", "true", "try", "type", "typeof", "unknown", "var", "void", "while"]);
|
|
5325
5335
|
if (tsKeywords.has(n)) n = `${n}_`;
|
|
5326
5336
|
return n.replace(/[- ]/g, "_");
|
|
5327
5337
|
};
|
|
5338
|
+
var tsGet = (object, tsFieldName2) => {
|
|
5339
|
+
if (tsFieldName2.startsWith('"')) return `${object}[${tsFieldName2}]`;
|
|
5340
|
+
return `${object}.${tsFieldName2}`;
|
|
5341
|
+
};
|
|
5328
5342
|
var TypeScript = class extends Writer {
|
|
5329
5343
|
tsImportType(tsPackageName, ...entities) {
|
|
5330
5344
|
this.lineSM(`import type { ${entities.join(", ")} } from "${tsPackageName}"`);
|
|
@@ -5388,7 +5402,8 @@ var TypeScript = class extends Writer {
|
|
|
5388
5402
|
}
|
|
5389
5403
|
addFieldExtension(fieldName, field) {
|
|
5390
5404
|
if (field.type.kind === "primitive-type") {
|
|
5391
|
-
|
|
5405
|
+
const extFieldName = tsFieldName(`_${fieldName}`);
|
|
5406
|
+
this.lineSM(`${extFieldName}?: Element`);
|
|
5392
5407
|
}
|
|
5393
5408
|
}
|
|
5394
5409
|
generateType(tsIndex, schema) {
|
|
@@ -5513,7 +5528,7 @@ var TypeScript = class extends Writer {
|
|
|
5513
5528
|
this.line(`profile: ['${flatProfile.identifier.url}']`);
|
|
5514
5529
|
}, [","]);
|
|
5515
5530
|
profileFields.forEach((fieldName) => {
|
|
5516
|
-
this.line(`${fieldName}
|
|
5531
|
+
this.line(`${fieldName}: ${tsGet("profile", fieldName)},`);
|
|
5517
5532
|
});
|
|
5518
5533
|
});
|
|
5519
5534
|
}
|
|
@@ -5544,7 +5559,7 @@ var TypeScript = class extends Writer {
|
|
|
5544
5559
|
if (!isNotChoiceDeclarationField(pField) || !isNotChoiceDeclarationField(rField)) return;
|
|
5545
5560
|
if (pField.required && !rField.required) {
|
|
5546
5561
|
this.curlyBlock(
|
|
5547
|
-
[`if (resource
|
|
5562
|
+
[`if (${tsGet("resource", tsField)} === undefined)`],
|
|
5548
5563
|
() => this.lineSM(
|
|
5549
5564
|
`throw new Error("'${tsField}' is required for ${flatProfile.identifier.url}")`
|
|
5550
5565
|
)
|
|
@@ -5563,11 +5578,11 @@ var TypeScript = class extends Writer {
|
|
|
5563
5578
|
this.line(";");
|
|
5564
5579
|
});
|
|
5565
5580
|
});
|
|
5566
|
-
let cond = !pField?.required ?
|
|
5581
|
+
let cond = !pField?.required ? `!${tsGet("resource", tsField)} || ` : "";
|
|
5567
5582
|
if (pField.array) {
|
|
5568
|
-
cond +=
|
|
5583
|
+
cond += `${tsGet("resource", tsField)}.every( (ref) => ${predName}(ref) )`;
|
|
5569
5584
|
} else {
|
|
5570
|
-
cond += `!${predName}(resource
|
|
5585
|
+
cond += `!${predName}(${tsGet("resource", tsField)})`;
|
|
5571
5586
|
}
|
|
5572
5587
|
this.curlyBlock(["if (", cond, ")"], () => {
|
|
5573
5588
|
this.lineSM(
|
|
@@ -5583,9 +5598,12 @@ var TypeScript = class extends Writer {
|
|
|
5583
5598
|
profileFields.forEach((fieldName) => {
|
|
5584
5599
|
const tsField = tsFieldName(fieldName);
|
|
5585
5600
|
if (shouldCast[fieldName]) {
|
|
5586
|
-
this.line(
|
|
5601
|
+
this.line(
|
|
5602
|
+
`${tsField}:`,
|
|
5603
|
+
`${tsGet("resource", tsField)} as ${tsProfileName}['${tsField}'],`
|
|
5604
|
+
);
|
|
5587
5605
|
} else {
|
|
5588
|
-
this.line(`${tsField}:`,
|
|
5606
|
+
this.line(`${tsField}:`, `${tsGet("resource", tsField)},`);
|
|
5589
5607
|
}
|
|
5590
5608
|
});
|
|
5591
5609
|
});
|
|
@@ -5657,6 +5675,7 @@ var writerToGenerator = (writerGen) => {
|
|
|
5657
5675
|
build: async (_schemas) => getGeneratedFiles()
|
|
5658
5676
|
};
|
|
5659
5677
|
};
|
|
5678
|
+
var normalizeFileName = (str) => str.replace(/[^a-zA-Z0-9]/g, "");
|
|
5660
5679
|
var APIBuilder = class {
|
|
5661
5680
|
schemas = [];
|
|
5662
5681
|
options;
|
|
@@ -5677,7 +5696,8 @@ var APIBuilder = class {
|
|
|
5677
5696
|
cleanOutput: options.cleanOutput ?? true,
|
|
5678
5697
|
typeSchemaConfig: options.typeSchemaConfig,
|
|
5679
5698
|
manager: options.manager || null,
|
|
5680
|
-
throwException: options.throwException || false
|
|
5699
|
+
throwException: options.throwException || false,
|
|
5700
|
+
typeSchemaOutputDir: options.typeSchemaOutputDir
|
|
5681
5701
|
};
|
|
5682
5702
|
this.typeSchemaConfig = options.typeSchemaConfig;
|
|
5683
5703
|
this.logger = options.logger || createLogger({
|
|
@@ -5780,6 +5800,45 @@ var APIBuilder = class {
|
|
|
5780
5800
|
this.options.cleanOutput = enabled;
|
|
5781
5801
|
return this;
|
|
5782
5802
|
}
|
|
5803
|
+
writeTypeSchemas(target) {
|
|
5804
|
+
this.options.typeSchemaOutputDir = target;
|
|
5805
|
+
return this;
|
|
5806
|
+
}
|
|
5807
|
+
async tryWriteTypeSchema(typeSchemas) {
|
|
5808
|
+
if (!this.options.typeSchemaOutputDir) return;
|
|
5809
|
+
try {
|
|
5810
|
+
if (this.options.cleanOutput) fs.rmSync(this.options.typeSchemaOutputDir, { recursive: true, force: true });
|
|
5811
|
+
await afs.mkdir(this.options.typeSchemaOutputDir, { recursive: true });
|
|
5812
|
+
let writtenCount = 0;
|
|
5813
|
+
let overrideCount = 0;
|
|
5814
|
+
const usedNames = {};
|
|
5815
|
+
this.logger.info(`Writing TypeSchema files to ${this.options.typeSchemaOutputDir}...`);
|
|
5816
|
+
for (const ts of typeSchemas) {
|
|
5817
|
+
const package_name = camelCase(ts.identifier.package.replaceAll("/", "-"));
|
|
5818
|
+
const name = normalizeFileName(ts.identifier.name.toString());
|
|
5819
|
+
const baseName = Path2.join(this.options.typeSchemaOutputDir, package_name, name);
|
|
5820
|
+
let fullName;
|
|
5821
|
+
if (usedNames[baseName] !== void 0) {
|
|
5822
|
+
usedNames[baseName]++;
|
|
5823
|
+
fullName = `${baseName}-${usedNames[baseName]}.typeschema.json`;
|
|
5824
|
+
} else {
|
|
5825
|
+
usedNames[baseName] = 0;
|
|
5826
|
+
fullName = `${baseName}.typeschema.json`;
|
|
5827
|
+
}
|
|
5828
|
+
await afs.mkdir(Path2.dirname(fullName), { recursive: true });
|
|
5829
|
+
afs.writeFile(fullName, JSON.stringify(ts, null, 2));
|
|
5830
|
+
if (await afs.exists(fullName)) overrideCount++;
|
|
5831
|
+
else writtenCount++;
|
|
5832
|
+
}
|
|
5833
|
+
this.logger.info(`Created ${writtenCount} new TypeSchema files, overrode ${overrideCount} files`);
|
|
5834
|
+
} catch (error) {
|
|
5835
|
+
if (this.options.throwException) throw error;
|
|
5836
|
+
this.logger.error(
|
|
5837
|
+
"Failed to write TypeSchema output",
|
|
5838
|
+
error instanceof Error ? error : new Error(String(error))
|
|
5839
|
+
);
|
|
5840
|
+
}
|
|
5841
|
+
}
|
|
5783
5842
|
async generate() {
|
|
5784
5843
|
const startTime = performance.now();
|
|
5785
5844
|
const result = {
|
|
@@ -5811,6 +5870,7 @@ var APIBuilder = class {
|
|
|
5811
5870
|
await manager.init();
|
|
5812
5871
|
const register = await registerFromManager(manager, this.logger);
|
|
5813
5872
|
const typeSchemas = await generateTypeSchemas(register, this.logger);
|
|
5873
|
+
await this.tryWriteTypeSchema(typeSchemas);
|
|
5814
5874
|
this.logger.debug(`Executing ${this.generators.size} generators`);
|
|
5815
5875
|
await this.executeGenerators(result, typeSchemas);
|
|
5816
5876
|
this.logger.info("Generation completed successfully");
|