@compas/code-gen
Version:
Generate various boring parts of your server
148 lines (122 loc) • 3.85 kB
JavaScript
import { isNil } from "@compas/stdlib";
import { structureModels } from "./models.js";
import { structureResolveReference } from "./structure.js";
/**
* Sort relations, forcing a consistent logical output
*
* @param {import("../generate.js").GenerateContext} generateContext
*/
export function modelSortAllRelations(generateContext) {
for (const model of structureModels(generateContext)) {
model.relations = model.relations.sort((a, b) => {
const aType = a.subType;
const bType = b.subType;
// If both types are equal, we sort them based on alphabetic order of their own
// keys.
if (aType === bType) {
return a.ownKey.localeCompare(b.ownKey);
}
if (aType === "oneToOne") {
return -1;
}
if (bType === "oneToOne") {
return 1;
}
if (aType === "manyToOne") {
return -1;
}
if (bType === "manyToOne") {
return 1;
}
if (aType === "oneToOneReverse") {
return -1;
}
if (bType === "oneToOneReverse") {
return 1;
}
if (aType === "oneToMany") {
return -1;
}
if (bType === "oneToMany") {
return 1;
}
return -1;
});
}
}
/**
* Sort relations, forcing a consistent logical output
*
* @param {import("../generate.js").GenerateContext} generateContext
*/
export function modelSortAllKeys(generateContext) {
const typeOrder = {
boolean: 0,
number: 1,
uuid: 2,
string: 3,
date: 4,
};
for (const model of structureModels(generateContext)) {
const keys = model.keys;
model.keys = {};
// Calculated sorted keys without generated date fields
const sortedKeys = Object.keys(keys)
.filter(
(it) => it !== "createdAt" && it !== "updatedAt" && it !== "deletedAt",
)
.sort((a, b) => {
const keyA = keys[a];
const keyB = keys[b];
const refA =
keyA.type === "reference" ?
structureResolveReference(generateContext.structure, keyA)
: undefined;
const refB =
keyB.type === "reference" ?
structureResolveReference(generateContext.structure, keyB)
: undefined;
// Primary key should be sorted first
if (keyA.sql?.primary || refA?.sql?.primary) {
return -1;
}
// Primary key should be sorted first
if (keyB.sql?.primary || refB?.sql?.primary) {
return 1;
}
// If either key is optional, it should be sorted after
// the non optional field
const keyAOptional = keyA.isOptional || refA?.isOptional;
const keyBOptional = keyB.isOptional || refB?.isOptional;
const keyADefault = refA?.defaultValue ?? keyA.defaultValue;
const keyBDefault = refB?.defaultValue ?? keyB.defaultValue;
if (!keyAOptional && keyBOptional && isNil(keyBDefault)) {
return -1;
} else if (!keyBOptional && keyAOptional && isNil(keyADefault)) {
return 1;
}
// Both fields are either optional or not optional, prefer
// primitive types
const typeAIndex = typeOrder[keyA.type] ?? typeOrder[refA?.type] ?? 9;
const typeBIndex = typeOrder[keyB.type] ?? typeOrder[refB?.type] ?? 9;
if (typeAIndex !== typeBIndex) {
return typeAIndex - typeBIndex;
}
// Keys are created equal, use alphabetic sort.
return a.localeCompare(b);
});
// Add date fields if they existed originally
if (keys.createdAt) {
sortedKeys.push("createdAt");
}
if (keys.updatedAt) {
sortedKeys.push("updatedAt");
}
if (keys.deletedAt) {
sortedKeys.push("deletedAt");
}
for (const key of sortedKeys) {
model.keys[key] = keys[key];
}
}
}