From b0f1208194293808199b48d3d8b9484b49857346 Mon Sep 17 00:00:00 2001 From: Patrick Moody <4031292+patmood@users.noreply.github.com> Date: Mon, 27 Feb 2023 10:38:07 -0800 Subject: [PATCH] generate and export CollectionResponses (#51) * generate and export CollectionResponses * add workflow badges to readme * add new type to readme --- README.md | 9 ++++- dist/index.js | 41 +++++++++++++-------- package.json | 2 +- src/collections.ts | 33 +++++++++++++++++ src/constants.ts | 1 + src/lib.ts | 29 ++++----------- test/__snapshots__/collections.test.ts.snap | 22 +++++++++++ test/__snapshots__/fromJSON.test.ts.snap | 11 ++++++ test/__snapshots__/lib.test.ts.snap | 20 +++------- test/collections.test.ts | 26 +++++++++++++ test/lib.test.ts | 22 +---------- test/pocketbase-types-example.ts | 11 ++++++ 12 files changed, 153 insertions(+), 74 deletions(-) create mode 100644 src/collections.ts create mode 100644 test/__snapshots__/collections.test.ts.snap create mode 100644 test/collections.test.ts diff --git a/README.md b/README.md index f9b4224..a35fc96 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ The output is a typescript file `pocketbase-types.ts` ([example](./test/pocketba - `[CollectionName]Response` One response type for each collection (eg ProfilesResponse) which includes system fields. This is what is returned from the PocketBase API. - `[CollectionName][FieldName]Options` If the collection contains a select field with set values, an enum of the options will be generated. - `CollectionRecords` A type mapping each collection name to the record type. +- `CollectionResponses` A type mapping each collection name to the response type. ## Example Usage @@ -78,8 +79,8 @@ import { Collections, CommentsResponse, UserResponse } from "./pocketbase-types" /** type CommentsRecord = { text: string - metadata: null | Tmetadata - user: RecordIdString + metadata: null | Tmetadata // This is a json field + user: RecordIdString // This is a relation field } */ type Tmetadata = { @@ -95,3 +96,7 @@ const result = await pb // Now you can access the expanded relation with type safety and hints in your IDE result.expand?.user.username ``` + +## Status + +![](https://github.com/patmood/pocketbase-typegen/actions/workflows/test.yml/badge.svg?branch=main) ![](https://github.com/patmood/pocketbase-typegen/actions/workflows/integration.yml/badge.svg?branch=main) diff --git a/dist/index.js b/dist/index.js index 394fccb..8772524 100755 --- a/dist/index.js +++ b/dist/index.js @@ -43,6 +43,7 @@ var EXPORT_COMMENT = `/** */`; var RECORD_TYPE_COMMENT = `// Record types for each collection`; var RESPONSE_TYPE_COMMENT = `// Response types include system fields and match responses from the PocketBase API`; +var ALL_RECORD_RESPONSE_COMMENT = `// Types containing all Records and Responses, useful for creating typing helper functions`; var EXPAND_GENERIC_NAME = "expand"; var DATE_STRING_TYPE_NAME = `IsoDateString`; var RECORD_ID_STRING_NAME = `RecordIdString`; @@ -138,6 +139,27 @@ function getOptionValues(field) { return values.filter((val, i) => values.indexOf(val) === i); } +// src/collections.ts +function createCollectionEnum(collectionNames) { + const collections = collectionNames.map((name) => ` ${toPascalCase(name)} = "${name}",`).join("\n"); + const typeString = `export enum Collections { +${collections} +}`; + return typeString; +} +function createCollectionRecords(collectionNames) { + const nameRecordMap = collectionNames.map((name) => ` ${name}: ${toPascalCase(name)}Record`).join("\n"); + return `export type CollectionRecords = { +${nameRecordMap} +}`; +} +function createCollectionResponses(collectionNames) { + const nameRecordMap = collectionNames.map((name) => ` ${name}: ${toPascalCase(name)}Response`).join("\n"); + return `export type CollectionResponses = { +${nameRecordMap} +}`; +} + // src/fields.ts var pbSchemaTypescriptMap = { bool: "boolean", @@ -204,23 +226,12 @@ function generate(results) { RECORD_TYPE_COMMENT, ...recordTypes, responseTypes.join("\n"), - createCollectionRecords(sortedCollectionNames) + ALL_RECORD_RESPONSE_COMMENT, + createCollectionRecords(sortedCollectionNames), + createCollectionResponses(sortedCollectionNames) ]; return fileParts.join("\n\n"); } -function createCollectionEnum(collectionNames) { - const collections = collectionNames.map((name) => ` ${toPascalCase(name)} = "${name}",`).join("\n"); - const typeString = `export enum Collections { -${collections} -}`; - return typeString; -} -function createCollectionRecords(collectionNames) { - const nameRecordMap = collectionNames.map((name) => ` ${name}: ${toPascalCase(name)}Record`).join("\n"); - return `export type CollectionRecords = { -${nameRecordMap} -}`; -} function createRecordType(name, schema) { const selectOptionEnums = createSelectOptions(name, schema); const typeName = toPascalCase(name); @@ -267,7 +278,7 @@ async function main(options2) { import { program } from "commander"; // package.json -var version = "1.1.5"; +var version = "1.1.6"; // src/index.ts program.name("Pocketbase Typegen").version(version).description( diff --git a/package.json b/package.json index aa32184..c507565 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pocketbase-typegen", - "version": "1.1.5", + "version": "1.1.6", "description": "Generate pocketbase record types from your database", "main": "dist/index.js", "bin": { diff --git a/src/collections.ts b/src/collections.ts new file mode 100644 index 0000000..4a4d796 --- /dev/null +++ b/src/collections.ts @@ -0,0 +1,33 @@ +import { toPascalCase } from "./utils" + +export function createCollectionEnum(collectionNames: Array): string { + const collections = collectionNames + .map((name) => `\t${toPascalCase(name)} = "${name}",`) + .join("\n") + const typeString = `export enum Collections { +${collections} +}` + return typeString +} + +export function createCollectionRecords( + collectionNames: Array +): string { + const nameRecordMap = collectionNames + .map((name) => `\t${name}: ${toPascalCase(name)}Record`) + .join("\n") + return `export type CollectionRecords = { +${nameRecordMap} +}` +} + +export function createCollectionResponses( + collectionNames: Array +): string { + const nameRecordMap = collectionNames + .map((name) => `\t${name}: ${toPascalCase(name)}Response`) + .join("\n") + return `export type CollectionResponses = { +${nameRecordMap} +}` +} diff --git a/src/constants.ts b/src/constants.ts index 143b877..35352b4 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -3,6 +3,7 @@ export const EXPORT_COMMENT = `/** */` export const RECORD_TYPE_COMMENT = `// Record types for each collection` export const RESPONSE_TYPE_COMMENT = `// Response types include system fields and match responses from the PocketBase API` +export const ALL_RECORD_RESPONSE_COMMENT = `// Types containing all Records and Responses, useful for creating typing helper functions` export const EXPAND_GENERIC_NAME = "expand" export const DATE_STRING_TYPE_NAME = `IsoDateString` export const RECORD_ID_STRING_NAME = `RecordIdString` diff --git a/src/lib.ts b/src/lib.ts index dd492c9..5a6940b 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -1,5 +1,6 @@ import { ALIAS_TYPE_DEFINITIONS, + ALL_RECORD_RESPONSE_COMMENT, AUTH_SYSTEM_FIELDS_DEFINITION, BASE_SYSTEM_FIELDS_DEFINITION, EXPAND_GENERIC_NAME, @@ -14,6 +15,11 @@ import { getGenericArgStringForRecord, getGenericArgStringWithDefault, } from "./generics" +import { + createCollectionEnum, + createCollectionRecords, + createCollectionResponses, +} from "./collections" import { createSelectOptions, createTypeField } from "./fields" import { getSystemFields, toPascalCase } from "./utils" @@ -43,33 +49,14 @@ export function generate(results: Array): string { RECORD_TYPE_COMMENT, ...recordTypes, responseTypes.join("\n"), + ALL_RECORD_RESPONSE_COMMENT, createCollectionRecords(sortedCollectionNames), + createCollectionResponses(sortedCollectionNames), ] return fileParts.join("\n\n") } -export function createCollectionEnum(collectionNames: Array): string { - const collections = collectionNames - .map((name) => `\t${toPascalCase(name)} = "${name}",`) - .join("\n") - const typeString = `export enum Collections { -${collections} -}` - return typeString -} - -export function createCollectionRecords( - collectionNames: Array -): string { - const nameRecordMap = collectionNames - .map((name) => `\t${name}: ${toPascalCase(name)}Record`) - .join("\n") - return `export type CollectionRecords = { -${nameRecordMap} -}` -} - export function createRecordType( name: string, schema: Array diff --git a/test/__snapshots__/collections.test.ts.snap b/test/__snapshots__/collections.test.ts.snap new file mode 100644 index 0000000..a91701f --- /dev/null +++ b/test/__snapshots__/collections.test.ts.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`createCollectionEnum creates enum of collection names 1`] = ` +"export enum Collections { + Book = "book", + Magazine = "magazine", +}" +`; + +exports[`createCollectionRecords creates mapping of collection name to record type 1`] = ` +"export type CollectionRecords = { + book: BookRecord + magazine: MagazineRecord +}" +`; + +exports[`createCollectionResponses creates mapping of collection name to response type 1`] = ` +"export type CollectionResponses = { + book: BookResponse + magazine: MagazineResponse +}" +`; diff --git a/test/__snapshots__/fromJSON.test.ts.snap b/test/__snapshots__/fromJSON.test.ts.snap index f479a0e..974c4cf 100644 --- a/test/__snapshots__/fromJSON.test.ts.snap +++ b/test/__snapshots__/fromJSON.test.ts.snap @@ -106,6 +106,8 @@ export type MyViewResponse = MyViewRec export type PostsResponse = PostsRecord & BaseSystemFields export type UsersResponse = UsersRecord & AuthSystemFields +// Types containing all Records and Responses, useful for creating typing helper functions + export type CollectionRecords = { base: BaseRecord custom_auth: CustomAuthRecord @@ -113,5 +115,14 @@ export type CollectionRecords = { my_view: MyViewRecord posts: PostsRecord users: UsersRecord +} + +export type CollectionResponses = { + base: BaseResponse + custom_auth: CustomAuthResponse + everything: EverythingResponse + my_view: MyViewResponse + posts: PostsResponse + users: UsersResponse }" `; diff --git a/test/__snapshots__/lib.test.ts.snap b/test/__snapshots__/lib.test.ts.snap index bc7967d..daf51c7 100644 --- a/test/__snapshots__/lib.test.ts.snap +++ b/test/__snapshots__/lib.test.ts.snap @@ -1,19 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`createCollectionEnum creates enum of collection names 1`] = ` -"export enum Collections { - Book = "book", - Magazine = "magazine", -}" -`; - -exports[`createCollectionRecord creates mapping of collection name to record type 1`] = ` -"export type CollectionRecords = { - book: BookRecord - magazine: MagazineRecord -}" -`; - exports[`createRecordType creates type definition for a record 1`] = ` "export type BooksRecord = { title?: string @@ -81,7 +67,13 @@ export type BooksRecord = { // Response types include system fields and match responses from the PocketBase API export type BooksResponse = BooksRecord & BaseSystemFields +// Types containing all Records and Responses, useful for creating typing helper functions + export type CollectionRecords = { books: BooksRecord +} + +export type CollectionResponses = { + books: BooksResponse }" `; diff --git a/test/collections.test.ts b/test/collections.test.ts new file mode 100644 index 0000000..60b40e5 --- /dev/null +++ b/test/collections.test.ts @@ -0,0 +1,26 @@ +import { + createCollectionEnum, + createCollectionRecords, + createCollectionResponses, +} from "../src/collections" + +describe("createCollectionEnum", () => { + it("creates enum of collection names", () => { + const names = ["book", "magazine"] + expect(createCollectionEnum(names)).toMatchSnapshot() + }) +}) + +describe("createCollectionRecords", () => { + it("creates mapping of collection name to record type", () => { + const names = ["book", "magazine"] + expect(createCollectionRecords(names)).toMatchSnapshot() + }) +}) + +describe("createCollectionResponses", () => { + it("creates mapping of collection name to response type", () => { + const names = ["book", "magazine"] + expect(createCollectionResponses(names)).toMatchSnapshot() + }) +}) diff --git a/test/lib.test.ts b/test/lib.test.ts index 6fa2dce..4328a1c 100644 --- a/test/lib.test.ts +++ b/test/lib.test.ts @@ -1,11 +1,5 @@ import { CollectionRecord, FieldSchema } from "../src/types" -import { - createCollectionEnum, - createCollectionRecords, - createRecordType, - createResponseType, - generate, -} from "../src/lib" +import { createRecordType, createResponseType, generate } from "../src/lib" describe("generate", () => { it("generates correct output given db input", () => { @@ -38,20 +32,6 @@ describe("generate", () => { }) }) -describe("createCollectionEnum", () => { - it("creates enum of collection names", () => { - const names = ["book", "magazine"] - expect(createCollectionEnum(names)).toMatchSnapshot() - }) -}) - -describe("createCollectionRecord", () => { - it("creates mapping of collection name to record type", () => { - const names = ["book", "magazine"] - expect(createCollectionRecords(names)).toMatchSnapshot() - }) -}) - describe("createRecordType", () => { it("creates type definition for a record", () => { const name = "books" diff --git a/test/pocketbase-types-example.ts b/test/pocketbase-types-example.ts index 9cee008..3bba5fa 100644 --- a/test/pocketbase-types-example.ts +++ b/test/pocketbase-types-example.ts @@ -103,6 +103,8 @@ export type MyViewResponse = MyViewRec export type PostsResponse = PostsRecord & BaseSystemFields export type UsersResponse = UsersRecord & AuthSystemFields +// Types containing all Records and Responses, useful for creating typing helper functions + export type CollectionRecords = { base: BaseRecord custom_auth: CustomAuthRecord @@ -110,4 +112,13 @@ export type CollectionRecords = { my_view: MyViewRecord posts: PostsRecord users: UsersRecord +} + +export type CollectionResponses = { + base: BaseResponse + custom_auth: CustomAuthResponse + everything: EverythingResponse + my_view: MyViewResponse + posts: PostsResponse + users: UsersResponse } \ No newline at end of file