Skip to content

Commit

Permalink
generate and export CollectionResponses (#51)
Browse files Browse the repository at this point in the history
* generate and export CollectionResponses

* add workflow badges to readme

* add new type to readme
  • Loading branch information
patmood authored Feb 27, 2023
1 parent 0812864 commit b0f1208
Show file tree
Hide file tree
Showing 12 changed files with 153 additions and 74 deletions.
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -78,8 +79,8 @@ import { Collections, CommentsResponse, UserResponse } from "./pocketbase-types"
/**
type CommentsRecord<Tmetadata = unknown> = {
text: string
metadata: null | Tmetadata
user: RecordIdString
metadata: null | Tmetadata // This is a json field
user: RecordIdString // This is a relation field
}
*/
type Tmetadata = {
Expand All @@ -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)
41 changes: 26 additions & 15 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`;
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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(
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.5",
"version": "1.1.6",
"description": "Generate pocketbase record types from your database",
"main": "dist/index.js",
"bin": {
Expand Down
33 changes: 33 additions & 0 deletions src/collections.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { toPascalCase } from "./utils"

export function createCollectionEnum(collectionNames: Array<string>): 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>
): string {
const nameRecordMap = collectionNames
.map((name) => `\t${name}: ${toPascalCase(name)}Record`)
.join("\n")
return `export type CollectionRecords = {
${nameRecordMap}
}`
}

export function createCollectionResponses(
collectionNames: Array<string>
): string {
const nameRecordMap = collectionNames
.map((name) => `\t${name}: ${toPascalCase(name)}Response`)
.join("\n")
return `export type CollectionResponses = {
${nameRecordMap}
}`
}
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
29 changes: 8 additions & 21 deletions src/lib.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
ALIAS_TYPE_DEFINITIONS,
ALL_RECORD_RESPONSE_COMMENT,
AUTH_SYSTEM_FIELDS_DEFINITION,
BASE_SYSTEM_FIELDS_DEFINITION,
EXPAND_GENERIC_NAME,
Expand All @@ -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"

Expand Down Expand Up @@ -43,33 +49,14 @@ export function generate(results: Array<CollectionRecord>): 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>): 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>
): 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<FieldSchema>
Expand Down
22 changes: 22 additions & 0 deletions test/__snapshots__/collections.test.ts.snap
Original file line number Diff line number Diff line change
@@ -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
}"
`;
11 changes: 11 additions & 0 deletions test/__snapshots__/fromJSON.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,23 @@ export type MyViewResponse<Tjson_field = unknown, Texpand = unknown> = 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
everything: EverythingRecord
my_view: MyViewRecord
posts: PostsRecord
users: UsersRecord
}
export type CollectionResponses = {
base: BaseResponse
custom_auth: CustomAuthResponse
everything: EverythingResponse
my_view: MyViewResponse
posts: PostsResponse
users: UsersResponse
}"
`;
20 changes: 6 additions & 14 deletions test/__snapshots__/lib.test.ts.snap
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
}"
`;
26 changes: 26 additions & 0 deletions test/collections.test.ts
Original file line number Diff line number Diff line change
@@ -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()
})
})
22 changes: 1 addition & 21 deletions test/lib.test.ts
Original file line number Diff line number Diff line change
@@ -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", () => {
Expand Down Expand Up @@ -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"
Expand Down
11 changes: 11 additions & 0 deletions test/pocketbase-types-example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,22 @@ export type MyViewResponse<Tjson_field = unknown, Texpand = unknown> = 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
everything: EverythingRecord
my_view: MyViewRecord
posts: PostsRecord
users: UsersRecord
}

export type CollectionResponses = {
base: BaseResponse
custom_auth: CustomAuthResponse
everything: EverythingResponse
my_view: MyViewResponse
posts: PostsResponse
users: UsersResponse
}

0 comments on commit b0f1208

Please sign in to comment.