Skip to content

Commit

Permalink
Add support for view collection type (#50)
Browse files Browse the repository at this point in the history
* add view collection type
  • Loading branch information
patmood authored Feb 26, 2023
1 parent b3f95a8 commit 0812864
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 47 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Dockerfile to run e2e integration tests against a test PocketBase server
FROM node:16-alpine3.16

ARG POCKETBASE_VERSION=0.12.2
ARG POCKETBASE_VERSION=0.13.0

WORKDIR /app/output/
WORKDIR /app/
Expand Down
84 changes: 46 additions & 38 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ var AUTH_SYSTEM_FIELDS_DEFINITION = `export type AuthSystemFields<T = never> = {
username: string
verified: boolean
} & BaseSystemFields<T>`;
var VIEW_SYSTEM_FIELDS_DEFINITION = `export type ViewSystemFields<T = never> = {
id: ${RECORD_ID_STRING_NAME}
collectionId: string
collectionName: Collections
expand?: T
}`;

// src/generics.ts
function fieldNameToGeneric(name) {
Expand Down Expand Up @@ -113,7 +119,14 @@ async function saveFile(outPath, typeString) {
console.log(`Created typescript definitions at ${outPath}`);
}
function getSystemFields(type) {
return type === "auth" ? "AuthSystemFields" : "BaseSystemFields";
switch (type) {
case "auth":
return "AuthSystemFields";
case "view":
return "ViewSystemFields";
default:
return "BaseSystemFields";
}
}
function getOptionEnumName(recordName, fieldName) {
return `${toPascalCase(recordName)}${toPascalCase(fieldName)}Options`;
Expand All @@ -125,37 +138,54 @@ function getOptionValues(field) {
return values.filter((val, i) => values.indexOf(val) === i);
}

// src/lib.ts
// src/fields.ts
var pbSchemaTypescriptMap = {
bool: "boolean",
date: DATE_STRING_TYPE_NAME,
editor: HTML_STRING_NAME,
email: "string",
text: "string",
url: "string",
number: "number",
file: (fieldSchema) => fieldSchema.options.maxSelect && fieldSchema.options.maxSelect > 1 ? "string[]" : "string",
json: (fieldSchema) => `null | ${fieldNameToGeneric(fieldSchema.name)}`,
number: "number",
relation: (fieldSchema) => fieldSchema.options.maxSelect && fieldSchema.options.maxSelect === 1 ? RECORD_ID_STRING_NAME : `${RECORD_ID_STRING_NAME}[]`,
select: (fieldSchema, collectionName) => {
const valueType = fieldSchema.options.values ? getOptionEnumName(collectionName, fieldSchema.name) : "string";
return fieldSchema.options.maxSelect && fieldSchema.options.maxSelect > 1 ? `${valueType}[]` : valueType;
},
text: "string",
url: "string",
user: (fieldSchema) => fieldSchema.options.maxSelect && fieldSchema.options.maxSelect > 1 ? `${RECORD_ID_STRING_NAME}[]` : RECORD_ID_STRING_NAME
};
function createTypeField(collectionName, fieldSchema) {
let typeStringOrFunc;
if (!(fieldSchema.type in pbSchemaTypescriptMap)) {
console.log(`WARNING: unknown type "${fieldSchema.type}" found in schema`);
typeStringOrFunc = "unknown";
} else {
typeStringOrFunc = pbSchemaTypescriptMap[fieldSchema.type];
}
const typeString = typeof typeStringOrFunc === "function" ? typeStringOrFunc(fieldSchema, collectionName) : typeStringOrFunc;
const fieldName = sanitizeFieldName(fieldSchema.name);
const required = fieldSchema.required ? "" : "?";
return ` ${fieldName}${required}: ${typeString}`;
}
function createSelectOptions(recordName, schema) {
const selectFields = schema.filter((field) => field.type === "select");
const typestring = selectFields.map(
(field) => `export enum ${getOptionEnumName(recordName, field.name)} {
${getOptionValues(field).map((val) => ` "${val}" = "${val}",`).join("\n")}
}
`
).join("\n");
return typestring;
}

// src/lib.ts
function generate(results) {
const collectionNames = [];
const recordTypes = [];
const responseTypes = [RESPONSE_TYPE_COMMENT];
results.sort((a, b) => {
if (a.name < b.name) {
return -1;
}
if (a.name > b.name) {
return 1;
}
return 0;
}).forEach((row) => {
results.sort((a, b) => a.name <= b.name ? -1 : 1).forEach((row) => {
if (row.name)
collectionNames.push(row.name);
if (row.schema) {
Expand All @@ -170,6 +200,7 @@ function generate(results) {
ALIAS_TYPE_DEFINITIONS,
BASE_SYSTEM_FIELDS_DEFINITION,
AUTH_SYSTEM_FIELDS_DEFINITION,
VIEW_SYSTEM_FIELDS_DEFINITION,
RECORD_TYPE_COMMENT,
...recordTypes,
responseTypes.join("\n"),
Expand Down Expand Up @@ -212,29 +243,6 @@ function createResponseType(collectionSchemaEntry) {
const expandArgString = canExpand(schema) ? `<T${EXPAND_GENERIC_NAME}>` : "";
return `export type ${pascaleName}Response${genericArgsWithDefaults} = ${pascaleName}Record${genericArgsForRecord} & ${systemFields}${expandArgString}`;
}
function createTypeField(collectionName, fieldSchema) {
let typeStringOrFunc;
if (!(fieldSchema.type in pbSchemaTypescriptMap)) {
console.log(`WARNING: unknown type "${fieldSchema.type}" found in schema`);
typeStringOrFunc = "unknown";
} else {
typeStringOrFunc = pbSchemaTypescriptMap[fieldSchema.type];
}
const typeString = typeof typeStringOrFunc === "function" ? typeStringOrFunc(fieldSchema, collectionName) : typeStringOrFunc;
const fieldName = sanitizeFieldName(fieldSchema.name);
const required = fieldSchema.required ? "" : "?";
return ` ${fieldName}${required}: ${typeString}`;
}
function createSelectOptions(recordName, schema) {
const selectFields = schema.filter((field) => field.type === "select");
const typestring = selectFields.map(
(field) => `export enum ${getOptionEnumName(recordName, field.name)} {
${getOptionValues(field).map((val) => ` "${val}" = "${val}",`).join("\n")}
}
`
).join("\n");
return typestring;
}

// src/cli.ts
async function main(options2) {
Expand All @@ -259,7 +267,7 @@ async function main(options2) {
import { program } from "commander";

// package.json
var version = "1.1.4";
var version = "1.1.5";

// src/index.ts
program.name("Pocketbase Typegen").version(version).description(
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pocketbase-typegen",
"version": "1.1.4",
"version": "1.1.5",
"description": "Generate pocketbase record types from your database",
"main": "dist/index.js",
"bin": {
Expand Down
7 changes: 7 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,10 @@ export const AUTH_SYSTEM_FIELDS_DEFINITION = `export type AuthSystemFields<T = n
\tusername: string
\tverified: boolean
} & BaseSystemFields<T>`

export const VIEW_SYSTEM_FIELDS_DEFINITION = `export type ViewSystemFields<T = never> = {
\tid: ${RECORD_ID_STRING_NAME}
\tcollectionId: string
\tcollectionName: Collections
\texpand?: T
}`
2 changes: 2 additions & 0 deletions src/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
EXPORT_COMMENT,
RECORD_TYPE_COMMENT,
RESPONSE_TYPE_COMMENT,
VIEW_SYSTEM_FIELDS_DEFINITION,
} from "./constants"
import { CollectionRecord, FieldSchema } from "./types"
import {
Expand Down Expand Up @@ -38,6 +39,7 @@ export function generate(results: Array<CollectionRecord>): string {
ALIAS_TYPE_DEFINITIONS,
BASE_SYSTEM_FIELDS_DEFINITION,
AUTH_SYSTEM_FIELDS_DEFINITION,
VIEW_SYSTEM_FIELDS_DEFINITION,
RECORD_TYPE_COMMENT,
...recordTypes,
responseTypes.join("\n"),
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export type FieldSchema = {

export type CollectionRecord = {
id: string
type: "base" | "auth"
type: "base" | "auth" | "view"
name: string
system: boolean
listRule: string | null
Expand Down
9 changes: 8 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@ export async function saveFile(outPath: string, typeString: string) {
}

export function getSystemFields(type: CollectionRecord["type"]) {
return type === "auth" ? "AuthSystemFields" : "BaseSystemFields"
switch (type) {
case "auth":
return "AuthSystemFields"
case "view":
return "ViewSystemFields"
default:
return "BaseSystemFields"
}
}

export function getOptionEnumName(recordName: string, fieldName: string) {
Expand Down
16 changes: 16 additions & 0 deletions test/__snapshots__/fromJSON.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export enum Collections {
Base = "base",
CustomAuth = "custom_auth",
Everything = "everything",
MyView = "my_view",
Posts = "posts",
Users = "users",
}
Expand All @@ -35,6 +36,13 @@ export type AuthSystemFields<T = never> = {
verified: boolean
} & BaseSystemFields<T>
export type ViewSystemFields<T = never> = {
id: RecordIdString
collectionId: string
collectionName: Collections
expand?: T
}
// Record types for each collection
export type BaseRecord = {
Expand Down Expand Up @@ -72,6 +80,12 @@ export type EverythingRecord<Tanother_json_field = unknown, Tjson_field = unknow
rich_editor_field?: HTMLString
}
export type MyViewRecord<Tjson_field = unknown> = {
post_relation_field?: RecordIdString
text_field?: string
json_field?: null | Tjson_field
}
export type PostsRecord = {
field?: string
nonempty_field: string
Expand All @@ -88,13 +102,15 @@ export type UsersRecord = {
export type BaseResponse = BaseRecord & BaseSystemFields
export type CustomAuthResponse = CustomAuthRecord & AuthSystemFields
export type EverythingResponse<Tanother_json_field = unknown, Tjson_field = unknown, Texpand = unknown> = EverythingRecord<Tanother_json_field, Tjson_field> & BaseSystemFields<Texpand>
export type MyViewResponse<Tjson_field = unknown, Texpand = unknown> = MyViewRecord<Tjson_field> & ViewSystemFields<Texpand>
export type PostsResponse = PostsRecord & BaseSystemFields
export type UsersResponse = UsersRecord & AuthSystemFields
export type CollectionRecords = {
base: BaseRecord
custom_auth: CustomAuthRecord
everything: EverythingRecord
my_view: MyViewRecord
posts: PostsRecord
users: UsersRecord
}"
Expand Down
7 changes: 7 additions & 0 deletions test/__snapshots__/lib.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ export type AuthSystemFields<T = never> = {
verified: boolean
} & BaseSystemFields<T>
export type ViewSystemFields<T = never> = {
id: RecordIdString
collectionId: string
collectionName: Collections
expand?: T
}
// Record types for each collection
export type BooksRecord = {
Expand Down
66 changes: 61 additions & 5 deletions test/pb_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@
"options": {
"collectionId": "_pb_users_auth_",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": null
}
Expand All @@ -221,6 +222,7 @@
"options": {
"collectionId": "rs7hepu8zl6kr8e",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 5,
"displayFields": null
}
Expand All @@ -235,6 +237,7 @@
"options": {
"collectionId": "z6b9mssubo9megi",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": null
}
Expand Down Expand Up @@ -323,11 +326,11 @@
}
}
],
"listRule": null,
"viewRule": null,
"createRule": null,
"updateRule": null,
"deleteRule": null,
"listRule": "",
"viewRule": "",
"createRule": "",
"updateRule": "",
"deleteRule": "",
"options": {}
},
{
Expand Down Expand Up @@ -392,5 +395,58 @@
"updateRule": null,
"deleteRule": null,
"options": {}
},
{
"id": "ngpunwfmpl9x50r",
"name": "my_view",
"type": "view",
"system": false,
"schema": [
{
"id": "iwh5jvyg",
"name": "post_relation_field",
"type": "relation",
"system": false,
"required": false,
"unique": false,
"options": {
"collectionId": "z6b9mssubo9megi",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": null
}
},
{
"id": "ze7zu2ji",
"name": "text_field",
"type": "text",
"system": false,
"required": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"id": "pbwoyo77",
"name": "json_field",
"type": "json",
"system": false,
"required": false,
"unique": false,
"options": {}
}
],
"listRule": "",
"viewRule": "",
"createRule": null,
"updateRule": null,
"deleteRule": null,
"options": {
"query": "select id, post_relation_field, text_field, json_field from everything"
}
}
]
Loading

0 comments on commit 0812864

Please sign in to comment.