Skip to content

Commit

Permalink
Experiment
Browse files Browse the repository at this point in the history
  • Loading branch information
joshbwlng committed Dec 3, 2024
1 parent eb21ca0 commit 5056366
Show file tree
Hide file tree
Showing 2 changed files with 296 additions and 28 deletions.
54 changes: 34 additions & 20 deletions src/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,18 @@ type RequiredModelSubset = Pick<
'tables' | 'relationships' | 'synonyms'
>;

export type Options = {
noSerial?: boolean;
noMissingReferences?: boolean;
};

const trimNL = new TemplateTag(
replaceResultTransformer(/^[\r\n]*|[\r\n]*$/g, ''),
);

const replaceSerial = (s: string): string =>
s.replace(/^(?:big\s)?serial$/i, 'Integer');

const modelNameToCamelCaseName = (s: string): string =>
s
.split(/[ -]/)
Expand All @@ -34,6 +42,7 @@ const sqlTypeToTypescriptType = (
m: RequiredModelSubset,
f: AbstractSqlField,
mode: Mode,
opts?: Options,
): string => {
if (!['ForeignKey', 'ConceptType'].includes(f.dataType) && f.checks) {
const inChecks = f.checks.find(
Expand All @@ -47,20 +56,7 @@ const sqlTypeToTypescriptType = (
}
}

// Avoid exposing serial nature of Serial fields
let dataType = f.dataType;
if (
dataType === 'Serial' ||
(dataType === 'ForeignKey' &&
f.references != null &&
m.tables[f.references.resourceName].fields.find(
(field) => field.fieldName === f.references?.fieldName,
)?.dataType === 'Serial')
) {
dataType = 'Integer';
}

switch (dataType) {
switch (f.dataType) {
case 'ConceptType':
case 'ForeignKey': {
const referencedInterface = getReferencedInterface(
Expand All @@ -69,14 +65,24 @@ const sqlTypeToTypescriptType = (
);
const referencedFieldType = `${referencedInterface}['${f.references!.fieldName}']`;
if (mode === 'Write') {
if (opts?.noMissingReferences && f.references != null) {
const referencedField = m.tables[
f.references.resourceName
].fields.find(
({ fieldName }) => fieldName === f.references!.fieldName,
);
if (referencedField?.computed != null) {
return `Types['${opts?.noSerial ? replaceSerial(referencedField.dataType) : referencedField.dataType}']['${mode}']`;
}
}
return referencedFieldType;
}

const nullable = f.required ? '' : ' | []';
return `{ __id: ${referencedFieldType} } | [${referencedInterface}]${nullable}`;
}
default:
return `Types['${dataType}']['${mode}']`;
return `Types['${opts?.noSerial ? replaceSerial(f.dataType) : f.dataType}']['${mode}']`;
}
};

Expand All @@ -85,6 +91,7 @@ const fieldToInterfaceProps = (
m: RequiredModelSubset,
f: AbstractSqlField,
mode: Mode,
opts?: Options,
): string | undefined => {
if (mode === 'Write' && f.computed != null) {
// Computed terms cannot be written to
Expand All @@ -95,16 +102,18 @@ const fieldToInterfaceProps = (
m,
f,
mode,
opts,
)}${nullable};`;
};

const fieldsToInterfaceProps = (
m: RequiredModelSubset,
fields: AbstractSqlField[],
mode: Mode,
opts?: Options,
): string[] =>
fields
.map((f) => fieldToInterfaceProps(f.fieldName, m, f, mode))
.map((f) => fieldToInterfaceProps(f.fieldName, m, f, mode, opts))
.filter((f) => f != null);

const getSynonyms = (
Expand Down Expand Up @@ -221,11 +230,15 @@ const relationshipsToInterfaceProps = (
});
};

const tableToInterface = (m: RequiredModelSubset, table: AbstractSqlTable) => {
const tableToInterface = (
m: RequiredModelSubset,
table: AbstractSqlTable,
opts?: Options,
) => {
const writableFields =
table.definition != null
? []
: fieldsToInterfaceProps(m, table.fields, 'Write');
: fieldsToInterfaceProps(m, table.fields, 'Write', opts);
const writeType =
writableFields.length === 0
? // If there's a table definition then we cannot write anything
Expand All @@ -237,7 +250,7 @@ const tableToInterface = (m: RequiredModelSubset, table: AbstractSqlTable) => {
export interface ${modelNameToCamelCaseName(table.name)} {
Read: {
${[
...fieldsToInterfaceProps(m, table.fields, 'Read'),
...fieldsToInterfaceProps(m, table.fields, 'Read', opts),
...relationshipsToInterfaceProps(m, table, 'Read'),
].join('\n\t\t')}
};
Expand All @@ -250,6 +263,7 @@ type Mode = 'Read' | 'Write';

export const abstractSqlToTypescriptTypes = (
m: RequiredModelSubset,
opts?: Options,
): string => {
return trimNL`
// These types were generated by @balena/abstract-sql-to-typescript v${version}
Expand All @@ -259,7 +273,7 @@ import type { Types } from '@balena/abstract-sql-to-typescript';
${Object.keys(m.tables)
.map((tableName) => {
const t = m.tables[tableName];
return tableToInterface(m, t);
return tableToInterface(m, t, opts);
})
.join('\n\n')}
Expand Down
Loading

0 comments on commit 5056366

Please sign in to comment.