Skip to content

Commit

Permalink
feat(fhir-ts-codegen): full rewrite
Browse files Browse the repository at this point in the history
  • Loading branch information
tangdrew committed Oct 24, 2019
1 parent 5d2bde4 commit fc20dc3
Show file tree
Hide file tree
Showing 6 changed files with 488 additions and 574 deletions.
6 changes: 2 additions & 4 deletions packages/fhir-ts-codegen/bin/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#!/usr/bin/env node

const program = require("commander");
const config = require("../package.json");
const { Generator } = require("../dist");
const { run } = require("../dist");

program
.name("fhir-ts-codegen")
Expand All @@ -12,8 +11,7 @@ program
.parse(process.argv);

if (program.input && program.output) {
const generator = new Generator(program.input, program.output);
generator.run();
run(program.input, program.output);
} else {
program.help()
}
153 changes: 153 additions & 0 deletions packages/fhir-ts-codegen/lib/code-generater.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { pipe } from "fp-ts/lib/pipeable";
import { map, flatten, filter, uniq, sort, partition } from "fp-ts/lib/Array";
import { format } from "prettier";

import { ElementGroup, AnalyzedElement } from "./parser";
import { eqString } from "fp-ts/lib/Eq";
import { ordString } from "fp-ts/lib/Ord";

export const generateModule = ({
name,
groups
}: {
name: string;
groups: { [key: string]: ElementGroup };
}) =>
pipe(
Object.keys(groups),
map(key => groups[key]),
map(generateDeclarations),
declarations =>
combine({
name,
declarations,
imports: generateImports({ name, groups })
}),
formatTypescript,
code => ({ name, code })
);

const combine = ({
name,
imports,
declarations
}: {
name: string;
imports: string[];
declarations: string[];
}) => `
/**
* ${name} Module
*/
import * as primitives from "@tangdrew/primitives";
import * as t from "io-ts";
${imports.join("\n")}
${declarations.join("\n\n")}
`;

/**
* Generates TypeScript barrel code for the module
*/
export const generateBarrel = (modules: { name: string; code: string }[]) =>
pipe(
modules,
map(({ name }) => `export { ${name} } from "./${name}";`),
lines => lines.join("\n"),
code => ({
code,
name: "index"
})
);

const formatTypescript = (code: string) =>
format(code, { parser: "typescript" });

const generateDeclarations = (group: ElementGroup) =>
[generateInterface(group), generateCodec(group)].join("\n");

const generateImports = ({
name,
groups
}: {
name: string;
groups: { [key: string]: ElementGroup };
}) =>
pipe(
Object.keys(groups).map(key => groups[key]),
map(({ elements }) => elements),
flatten,
filter(
({ backbone, contentReference, primitive, recursive }) =>
!primitive && !backbone && !contentReference && !recursive
),
map(({ type }) => type),
// Ensure Element is imported
types => [...types, "Element"],
filter(type => type !== name),
uniq(eqString),
sort(ordString),
map(type => `import { ${type} } from "./${type}";`)
);

const generateInterface = ({ name, comment, elements }: ElementGroup) =>
pipe(
elements,
map(generateProperty),
properties =>
`/**
* ${comment}
*/
export interface ${name} {
${properties.join("\n")}
}`
);

const generateProperty = ({
array,
comment,
iotsType,
name,
required,
type
}: AnalyzedElement) =>
pipe(
["boolean", "string"].includes(type) ? type : iotsType,
typeName => `/** ${comment} */
${name}${required ? "" : "?"}: ${typeName}${array ? "[]" : ""};`
);

const generateCodec = ({ name, comment, elements }: ElementGroup) =>
pipe(
elements,
partition(element => element.required),
({ left, right }) => ({
optional: left.map(generateIotsProperty),
required: right.map(generateIotsProperty)
}),
({ optional, required }) =>
`/**
* ${comment}
*/
export const ${name}: t.Type<${name}> = t.recursion<${name}>(
"${name}",
() => t.intersection([
t.type({
${required.join(",\n")}
}),
t.partial({
${optional.join(",\n")}
}),
])
)`
);

const generateIotsProperty = ({
array,
comment,
name,
iotsType
}: AnalyzedElement) =>
`/** ${comment} */
${name}: ${array ? `t.array(${iotsType})` : iotsType}`;
38 changes: 11 additions & 27 deletions packages/fhir-ts-codegen/lib/conformance.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { NonEmptyArray } from "fp-ts/lib/NonEmptyArray";

/**
* FHIR Conformance Types
*/
Expand Down Expand Up @@ -25,33 +27,6 @@ export enum FHIRPrimitives {
xhtml = "xhtml"
}

/**
* Map from FHIR Primitive type name to FHIR io-ts primitive type
* TODO: Move this to primitives package
*/
export const FHIRPrimitivesTypes: { [index: string]: string } = {
base64Binary: "Base64BinaryType",
boolean: "BooleanType",
canonical: "CanonicalType",
code: "CodeType",
date: "DateType",
dateTime: "DateTimeType",
decimal: "DecimalType",
id: "IDType",
instant: "InstantType",
integer: "IntegerType",
markdown: "MarkdownType",
oid: "OIDType",
positiveInt: "PositiveIntegerType",
string: "StringType",
time: "TimeType",
unsignedInt: "UnsignedIntegerType",
uri: "URIType",
url: "URLType",
uuid: "UUIDType",
xhtml: "XHTMLType"
};

export interface StructureDefinition {
resourceType: string;
url: string;
Expand Down Expand Up @@ -87,6 +62,15 @@ export interface ElementDefinition {
contentReference?: string;
}

export type ChoiceElementDefinition = ElementDefinition & {
type: NonEmptyArray<{ code: string }>;
};

export type ContentReferenceElementDefinition = ElementDefinition & {
contentReference: string;
type: undefined;
};

/**
* Logical grouping of element definitions, e.g. a BackboneElement and it's elements
*/
Expand Down
Loading

0 comments on commit fc20dc3

Please sign in to comment.