@lbu/code-gen
Version:
Generate various boring parts of your server
198 lines (172 loc) • 5.33 kB
JavaScript
import { ObjectType } from "../../builders/ObjectType.js";
import { upperCaseFirst } from "../../utils.js";
import { js } from "../tag/index.js";
import { getTypeNameForType } from "../types.js";
import {
getPrimaryKeyWithType,
getQueryEnabledObjects,
getSortedKeysForType,
} from "./utils.js";
/**
* Creates a where type and assigns in to the object type
*
* @param {CodeGenContext} context
*/
export function createPartialTypes(context) {
for (const type of getQueryEnabledObjects(context)) {
if (type.queryOptions.isView) {
continue;
}
const fieldsArray = [];
const insertPartial = new ObjectType(
type.group,
`${type.name}InsertPartial`,
).build();
insertPartial.uniqueName = `${upperCaseFirst(
insertPartial.group,
)}${upperCaseFirst(insertPartial.name)}`;
const updatePartial = new ObjectType(
type.group,
`${type.name}UpdatePartial`,
).build();
updatePartial.uniqueName = `${upperCaseFirst(
updatePartial.group,
)}${upperCaseFirst(updatePartial.name)}`;
for (const key of getSortedKeysForType(type)) {
let fieldType = type.keys[key];
if (fieldType.reference) {
fieldType = fieldType.reference;
}
if (fieldType?.sql?.primary) {
// Primary keys have some special handling in insertValues, but are completely skipped in updateSet.
// However, it is handled inline and thus not put in to the fieldsArray
insertPartial.keys[key] = {
...fieldType,
isOptional: true,
};
continue;
}
// Default value is the edge case here.
// We also support setting a default value for the reference, so fields can be
// reused but only have a default value on the reference.
fieldsArray.push({
key,
isJsonb:
["number", "boolean", "string", "date", "uuid"].indexOf(
fieldType.type,
) === -1,
defaultValue: fieldType.defaultValue ?? type.keys[key].defaultValue,
});
insertPartial.keys[key] = {
...fieldType,
};
updatePartial.keys[key] = {
...fieldType,
isOptional: true,
};
}
type.partial = {
insertType: getTypeNameForType(context, insertPartial, "", {
useDefaults: false,
}),
updateType: getTypeNameForType(context, updatePartial, "", {
useDefaults: false,
}),
fields: fieldsArray,
};
}
}
/**
* Adds builder to reuse inserts
* @param {CodeGenContext} context
* @param {CodeGenObjectType} type
*/
export function getInsertPartial(context, type) {
const { key: primaryKey } = getPrimaryKeyWithType(type);
return js`
/**
* Build 'VALUES ' part for ${type.name}
* @param {${type.partial.insertType}|${type.partial.insertType}[]} insert
* @param {{ includePrimaryKey: boolean }} [options={}]
* @returns {QueryPart}
*/
export function ${type.name}InsertValues(insert, options = {}) {
if (!Array.isArray(insert)) {
insert = [ insert ];
}
const q = query\`\`;
for (let i = 0; i < insert.length; ++i) {
const it = insert[i];
checkFieldsInSet("${type.name}", "insert", ${type.name}FieldSet, it);
q.append(query\`(
$\{options?.includePrimaryKey ? query\`$\{it.${primaryKey}}, \` : undefined}
$\{${type.partial.fields
.map((it) => {
if (it.isJsonb) {
return `JSON.stringify(it.${it.key} ?? ${
it.defaultValue ?? "null"
})`;
}
return `it.${it.key} ?? ${it.defaultValue ?? "null"}`;
})
.join("}, ${")}}
)\`);
if (i !== insert.length - 1) {
q.append(query\`, \`);
}
}
return q;
}
`;
}
/**
* Adds builder to reuse updates
* @param {CodeGenContext} context
* @param {CodeGenObjectType} type
*/
export function getUpdatePartial(context, type) {
const partials = [];
for (const field of type.partial.fields) {
if (type.queryOptions.withDates || type.queryOptions.withSoftDeletes) {
if (field.key === "updatedAt") {
partials.push(`
strings.push(\`, "${field.key}" = \`);
values.push(new Date());
`);
continue;
}
}
partials.push(js`
if (update.${field.key} !== undefined) {
strings.push(\`, "${field.key}" = \`);
${() => {
if (field.isJsonb) {
return `values.push(JSON.stringify(update.${field.key} ?? ${
field.defaultValue ?? "null"
}));`;
}
return `values.push(update.${field.key} ?? ${
field.defaultValue ?? "null"
});`;
}}
}
`);
}
return js`
/**
* Build 'SET ' part for ${type.name}
* @param {${type.partial.updateType}} update
* @returns {QueryPart}
*/
export function ${type.name}UpdateSet(update) {
const strings = [];
const values = [];
checkFieldsInSet("${type.name}", "update", ${type.name}FieldSet, update);
${partials}
// Remove the comma suffix
strings[0] = strings[0].substring(2);
strings.push("");
return query(strings, ...values);
}
`;
}