Skip to content

Commit

Permalink
Relational Fields (#3)
Browse files Browse the repository at this point in the history
* implement relational fields

* use intersection type instead of union type for relational fields
  • Loading branch information
maltejur authored Feb 20, 2022
1 parent f33bcd7 commit 101b171
Show file tree
Hide file tree
Showing 6 changed files with 475 additions and 915 deletions.
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
"build": "directus-extension build"
},
"devDependencies": {
"@directus/extensions-sdk": "9.0.0-rc.95",
"@types/prismjs": "^1.16.6",
"axios": "^0.21.4",
"sass": "^1.42.1",
"typescript": "^4.4.3",
"@directus/extensions-sdk": "9.5.0",
"@types/prismjs": "^1.26.0",
"axios": "^0.25.0",
"sass": "^1.49.0",
"typescript": "^4.5.5",
"vue": "^2.6.14"
},
"dependencies": {
"@directus/shared": "^9.0.0-rc.95",
"prismjs": "^1.25.0"
"@directus/shared": "^9.5.0",
"prismjs": "^1.26.0"
}
}
30 changes: 26 additions & 4 deletions src/lib/api.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { Collections } from "lib/types";
import type { Collections, Field } from "lib/types";
import {
Collection as DirectusCollection,
Field,
Relation,
} from "@directus/shared/types";
import type { AxiosResponse } from "axios";
import { warn } from "./console";

export async function getCollections(api) {
const collectionsRes: AxiosResponse<{ data: DirectusCollection[] }> =
Expand All @@ -20,11 +21,32 @@ export async function getCollections(api) {
const fields = fieldsRes.data.data;
fields.forEach((field) => {
if (!collections[field.collection]) {
console.warn(`generate-types:
${field.collection} not found`);
warn(`${field.collection} not found`);
return;
}
collections[field.collection].fields.push(field);
});
const relationsRes: AxiosResponse<{ data: Relation[] }> = await api.get(
"/relations?limit=-1"
);
const relations = relationsRes.data.data;
relations.forEach((relation) => {
const oneField = collections[relation.meta.one_collection].fields.find(
(field) => field.field === relation.meta.one_field
);
const manyField = collections[relation.meta.many_collection].fields.find(
(field) => field.field === relation.meta.many_field
);
if (oneField)
oneField.relation = {
type: "many",
collection: relation.meta.many_collection,
};
if (manyField)
manyField.relation = {
type: "one",
collection: relation.meta.one_collection,
};
});
return collections;
}
20 changes: 20 additions & 0 deletions src/lib/console.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export function log(str: string) {
console.log(
`%c[directus-extension-generate-types]%c\n${str}`,
"font-weight: bold;"
);
}

export function warn(str: string) {
console.warn(
`%c[directus-extension-generate-types]%c\n${str}`,
"font-weight: bold;"
);
}

export function error(str: string) {
console.error(
`%c[directus-extension-generate-types]%c\n${str}`,
"font-weight: bold;"
);
}
67 changes: 38 additions & 29 deletions src/lib/generateTypes/ts.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,53 @@
import { Field } from "lib/types";
import { getCollections } from "../api";

export default async function generateTsTypes(api) {
const collections = await getCollections(api);
let ret = "";
const types = [];

ret += Object.values(collections)
.map((collection) => {
const collectionName = collection.collection;
const typeName = collectionName
.split("_")
.map((x) => x.charAt(0).toUpperCase() + x.slice(1))
.join("");
types.push(`${collectionName}: ${typeName}`);
return `export type ${typeName} = {
${Object.values(collection.fields)
.map(
(x) =>
` ${x.field}${x.schema?.is_nullable ? "?" : ""}: ${getType(x.type)};`
)
.join("\n")}
};`;
})
.join("\n\n");
Object.values(collections).forEach((collection) => {
const collectionName = collection.collection;
const typeName = pascalCase(collectionName);
types.push(`${collectionName}: ${typeName}`);
ret += `export type ${typeName} = {\n`;
collection.fields.forEach((field) => {
ret += ` ${field.field}${
field.schema?.is_nullable ? "?" : ""
}: ${getType(field)};\n`;
});
ret += "};\n\n";
});

ret += "\n\n";

ret += `export type CustomDirectusTypes = {
${types.map((x) => ` ${x};`).join("\n")}
};`;
ret +=
"export type CustomDirectusTypes = {\n" +
types.map((x) => ` ${x};`).join("\n") +
"\n};";

ret += "\n";

return ret;
}

function getType(directusType: string) {
if (["integer", "bigInteger", "float", "decimal"].includes(directusType))
return "number";
if (["boolean"].includes(directusType)) return "boolean";
if (["json", "csv"].includes(directusType)) return "unknown";
return "string";
function pascalCase(str: string) {
return str
.split(" ")
.flatMap((x) => x.split("_"))
.map((x) => x.charAt(0).toUpperCase() + x.slice(1))
.join("");
}

function getType(field: Field) {
let type: string;
if (["integer", "bigInteger", "float", "decimal"].includes(field.type))
type = "number";
else if (["boolean"].includes(field.type)) type = "boolean";
else if (["json", "csv"].includes(field.type)) type = "unknown";
else type = "string";
if (field.relation) {
type += ` & ${pascalCase(field.relation.collection)}${
field.relation.type === "many" ? "[]" : ""
}`;
}
return type;
}
8 changes: 7 additions & 1 deletion src/lib/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import type {
Collection as DirectusCollection,
Field,
Field as DirectusField,
} from "@directus/shared/types";

export type Field = DirectusField & {
relation?: {
type: "many" | "one";
collection: string;
};
};
export type Collection = DirectusCollection & { fields: Field[] };
export type Collections = { [collection: string]: Collection };
Loading

0 comments on commit 101b171

Please sign in to comment.