mirror of
https://github.com/zhigang1992/pocketbase-typegen.git
synced 2026-04-29 04:35:20 +08:00
Generate typed PocketBase type (#82)
* generate typed pocketbase * update package-lock.json * flag for toggling sdk generation * fix typo * update documentation * add newline to end of output * fix pr comments
This commit is contained in:
19
README.md
19
README.md
@@ -25,7 +25,8 @@ Options:
|
||||
-e, --email <char> email for an admin pocketbase user. Use this with the --url option
|
||||
-p, --password <char> password for an admin pocketbase user. Use this with the --url option
|
||||
-o, --out <char> path to save the typescript output file (default: "pocketbase-types.ts")
|
||||
-e, --env flag to use environment variables for configuration, add PB_TYPEGEN_URL, PB_TYPEGEN_EMAIL, PB_TYPEGEN_PASSWORD to your .env file
|
||||
--no-sdk remove the pocketbase package dependency. A typed version of the SDK will not be generated.
|
||||
-e, --env [path] flag to use environment variables for configuration. Add PB_TYPEGEN_URL, PB_TYPEGEN_EMAIL, PB_TYPEGEN_PASSWORD to your .env file. Optionally provide a path to your .env file
|
||||
-h, --help display help for command
|
||||
```
|
||||
|
||||
@@ -71,15 +72,27 @@ The output is a typescript file `pocketbase-types.ts` ([example](./test/pocketba
|
||||
- `[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.
|
||||
- `TypedPocketBase` A type for usage with type asserted PocketBase instance.
|
||||
|
||||
## Example Usage
|
||||
|
||||
In [PocketBase SDK](https://github.com/pocketbase/js-sdk) v0.8+ you can use generic types when fetching records, eg:
|
||||
Using PocketBase SDK v0.18.3+, collections can be [automatically typed](https://github.com/pocketbase/js-sdk#specify-typescript-definitions) using the generated `TypedPocketBase` type:
|
||||
|
||||
```typescript
|
||||
import { TypedPocketBase } from "./pocketbase-types"
|
||||
|
||||
const pb = new PocketBase('http://127.0.0.1:8090') as TypedPocketBase
|
||||
|
||||
await pb.collection('tasks').getOne("RECORD_ID") // -> results in TaskResponse
|
||||
await pb.collection('posts').getOne("RECORD_ID") // -> results in PostResponse
|
||||
```
|
||||
|
||||
Alternatively, you can use generic types for each request, eg:
|
||||
|
||||
```typescript
|
||||
import { Collections, TasksResponse } from "./pocketbase-types"
|
||||
|
||||
pb.collection(Collections.Tasks).getOne<TasksResponse>("RECORD_ID") // -> results in Promise<TaskResponse>
|
||||
await pb.collection(Collections.Tasks).getOne<TasksResponse>("RECORD_ID") // -> results in TaskResponse
|
||||
```
|
||||
|
||||
## Example Advanced Usage
|
||||
|
||||
26
dist/index.js
vendored
26
dist/index.js
vendored
@@ -59,9 +59,13 @@ async function fromURL(url, email = "", password = "") {
|
||||
var EXPORT_COMMENT = `/**
|
||||
* This file was @generated using pocketbase-typegen
|
||||
*/`;
|
||||
var IMPORTS = `import type PocketBase from 'pocketbase'
|
||||
import { type RecordService } from 'pocketbase'`;
|
||||
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 TYPED_POCKETBASE_COMMENT = `// Type for usage with type asserted PocketBase instance
|
||||
// https://github.com/pocketbase/js-sdk#specify-typescript-definitions`;
|
||||
var EXPAND_GENERIC_NAME = "expand";
|
||||
var DATE_STRING_TYPE_NAME = `IsoDateString`;
|
||||
var RECORD_ID_STRING_NAME = `RecordIdString`;
|
||||
@@ -142,6 +146,12 @@ function createCollectionResponses(collectionNames) {
|
||||
${nameRecordMap}
|
||||
}`;
|
||||
}
|
||||
function createTypedPocketbase(collectionNames) {
|
||||
const nameRecordMap = collectionNames.map((name) => ` collection(idOrName: '${name}'): RecordService<${toPascalCase(name)}Response>`).join("\n");
|
||||
return `export type TypedPocketBase = PocketBase & {
|
||||
${nameRecordMap}
|
||||
}`;
|
||||
}
|
||||
|
||||
// src/generics.ts
|
||||
function fieldNameToGeneric(name) {
|
||||
@@ -217,7 +227,7 @@ function getSelectOptionEnumName(val) {
|
||||
}
|
||||
|
||||
// src/lib.ts
|
||||
function generate(results) {
|
||||
function generate(results, options2) {
|
||||
const collectionNames = [];
|
||||
const recordTypes = [];
|
||||
const responseTypes = [RESPONSE_TYPE_COMMENT];
|
||||
@@ -232,6 +242,7 @@ function generate(results) {
|
||||
const sortedCollectionNames = collectionNames;
|
||||
const fileParts = [
|
||||
EXPORT_COMMENT,
|
||||
options2.sdk && IMPORTS,
|
||||
createCollectionEnum(sortedCollectionNames),
|
||||
ALIAS_TYPE_DEFINITIONS,
|
||||
BASE_SYSTEM_FIELDS_DEFINITION,
|
||||
@@ -241,9 +252,11 @@ function generate(results) {
|
||||
responseTypes.join("\n"),
|
||||
ALL_RECORD_RESPONSE_COMMENT,
|
||||
createCollectionRecords(sortedCollectionNames),
|
||||
createCollectionResponses(sortedCollectionNames)
|
||||
createCollectionResponses(sortedCollectionNames),
|
||||
options2.sdk && TYPED_POCKETBASE_COMMENT,
|
||||
options2.sdk && createTypedPocketbase(sortedCollectionNames)
|
||||
];
|
||||
return fileParts.join("\n\n");
|
||||
return fileParts.filter(Boolean).join("\n\n") + "\n";
|
||||
}
|
||||
function createRecordType(name, schema) {
|
||||
const selectOptionEnums = createSelectOptions(name, schema);
|
||||
@@ -295,7 +308,9 @@ async function main(options2) {
|
||||
"Missing schema path. Check options: pocketbase-typegen --help"
|
||||
);
|
||||
}
|
||||
const typeString = generate(schema);
|
||||
const typeString = generate(schema, {
|
||||
sdk: options2.sdk ?? true
|
||||
});
|
||||
await saveFile(options2.out, typeString);
|
||||
return typeString;
|
||||
}
|
||||
@@ -325,6 +340,9 @@ program.name("Pocketbase Typegen").version(version).description(
|
||||
"-o, --out <char>",
|
||||
"path to save the typescript output file",
|
||||
"pocketbase-types.ts"
|
||||
).option(
|
||||
"--no-sdk",
|
||||
"remove the pocketbase package dependency. A typed version of the SDK will not be generated."
|
||||
).option(
|
||||
"-e, --env [path]",
|
||||
"flag to use environment variables for configuration. Add PB_TYPEGEN_URL, PB_TYPEGEN_EMAIL, PB_TYPEGEN_PASSWORD to your .env file. Optionally provide a path to your .env file"
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "pocketbase-typegen",
|
||||
"version": "1.1.10",
|
||||
"version": "1.1.13",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "pocketbase-typegen",
|
||||
"version": "1.1.10",
|
||||
"version": "1.1.13",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"commander": "^9.4.1",
|
||||
|
||||
@@ -35,7 +35,9 @@ export async function main(options: Options) {
|
||||
"Missing schema path. Check options: pocketbase-typegen --help"
|
||||
)
|
||||
}
|
||||
const typeString = generate(schema)
|
||||
const typeString = generate(schema, {
|
||||
sdk: options.sdk ?? true
|
||||
})
|
||||
await saveFile(options.out, typeString)
|
||||
return typeString
|
||||
}
|
||||
|
||||
@@ -31,3 +31,14 @@ export function createCollectionResponses(
|
||||
${nameRecordMap}
|
||||
}`
|
||||
}
|
||||
|
||||
export function createTypedPocketbase(
|
||||
collectionNames: Array<string>
|
||||
): string {
|
||||
const nameRecordMap = collectionNames
|
||||
.map((name) => `\tcollection(idOrName: '${name}'): RecordService<${toPascalCase(name)}Response>`)
|
||||
.join("\n")
|
||||
return `export type TypedPocketBase = PocketBase & {
|
||||
${nameRecordMap}
|
||||
}`
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
export const EXPORT_COMMENT = `/**
|
||||
* This file was @generated using pocketbase-typegen
|
||||
*/`
|
||||
export const IMPORTS = `import type PocketBase from 'pocketbase'
|
||||
import { type RecordService } from 'pocketbase'`
|
||||
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 TYPED_POCKETBASE_COMMENT = `// Type for usage with type asserted PocketBase instance\n// https://github.com/pocketbase/js-sdk#specify-typescript-definitions`
|
||||
export const EXPAND_GENERIC_NAME = "expand"
|
||||
export const DATE_STRING_TYPE_NAME = `IsoDateString`
|
||||
export const RECORD_ID_STRING_NAME = `RecordIdString`
|
||||
|
||||
@@ -33,6 +33,10 @@ program
|
||||
"path to save the typescript output file",
|
||||
"pocketbase-types.ts"
|
||||
)
|
||||
.option(
|
||||
"--no-sdk",
|
||||
"remove the pocketbase package dependency. A typed version of the SDK will not be generated."
|
||||
)
|
||||
.option(
|
||||
"-e, --env [path]",
|
||||
"flag to use environment variables for configuration. Add PB_TYPEGEN_URL, PB_TYPEGEN_EMAIL, PB_TYPEGEN_PASSWORD to your .env file. Optionally provide a path to your .env file"
|
||||
|
||||
16
src/lib.ts
16
src/lib.ts
@@ -1,18 +1,21 @@
|
||||
import {
|
||||
ALIAS_TYPE_DEFINITIONS,
|
||||
ALL_RECORD_RESPONSE_COMMENT,
|
||||
TYPED_POCKETBASE_COMMENT,
|
||||
AUTH_SYSTEM_FIELDS_DEFINITION,
|
||||
BASE_SYSTEM_FIELDS_DEFINITION,
|
||||
EXPAND_GENERIC_NAME,
|
||||
EXPORT_COMMENT,
|
||||
RECORD_TYPE_COMMENT,
|
||||
RESPONSE_TYPE_COMMENT,
|
||||
IMPORTS,
|
||||
} from "./constants"
|
||||
import { CollectionRecord, FieldSchema } from "./types"
|
||||
import {
|
||||
createCollectionEnum,
|
||||
createCollectionRecords,
|
||||
createCollectionResponses,
|
||||
createTypedPocketbase,
|
||||
} from "./collections"
|
||||
import { createSelectOptions, createTypeField } from "./fields"
|
||||
import {
|
||||
@@ -21,7 +24,11 @@ import {
|
||||
} from "./generics"
|
||||
import { getSystemFields, toPascalCase } from "./utils"
|
||||
|
||||
export function generate(results: Array<CollectionRecord>): string {
|
||||
type GenerateOptions = {
|
||||
sdk: boolean
|
||||
}
|
||||
|
||||
export function generate(results: Array<CollectionRecord>, options: GenerateOptions): string {
|
||||
const collectionNames: Array<string> = []
|
||||
const recordTypes: Array<string> = []
|
||||
const responseTypes: Array<string> = [RESPONSE_TYPE_COMMENT]
|
||||
@@ -39,6 +46,7 @@ export function generate(results: Array<CollectionRecord>): string {
|
||||
|
||||
const fileParts = [
|
||||
EXPORT_COMMENT,
|
||||
options.sdk && IMPORTS,
|
||||
createCollectionEnum(sortedCollectionNames),
|
||||
ALIAS_TYPE_DEFINITIONS,
|
||||
BASE_SYSTEM_FIELDS_DEFINITION,
|
||||
@@ -49,9 +57,13 @@ export function generate(results: Array<CollectionRecord>): string {
|
||||
ALL_RECORD_RESPONSE_COMMENT,
|
||||
createCollectionRecords(sortedCollectionNames),
|
||||
createCollectionResponses(sortedCollectionNames),
|
||||
options.sdk && TYPED_POCKETBASE_COMMENT,
|
||||
options.sdk && createTypedPocketbase(sortedCollectionNames)
|
||||
]
|
||||
|
||||
return fileParts.join("\n\n")
|
||||
return fileParts
|
||||
.filter(Boolean)
|
||||
.join("\n\n") + '\n'
|
||||
}
|
||||
|
||||
export function createRecordType(
|
||||
|
||||
@@ -5,6 +5,7 @@ export type Options = {
|
||||
json?: string
|
||||
email?: string
|
||||
password?: string
|
||||
sdk?: boolean
|
||||
env?: boolean | string
|
||||
}
|
||||
|
||||
|
||||
@@ -20,3 +20,10 @@ exports[`createCollectionResponses creates mapping of collection name to respons
|
||||
magazine: MagazineResponse
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`createTypedPocketBase creates typed variant of PocketBase client 1`] = `
|
||||
"export type TypedPocketBase = PocketBase & {
|
||||
collection(idOrName: 'book'): RecordService<BookResponse>
|
||||
collection(idOrName: 'magazine'): RecordService<MagazineResponse>
|
||||
}"
|
||||
`;
|
||||
|
||||
@@ -5,6 +5,9 @@ exports[`creates a type file from json schema 1`] = `
|
||||
* This file was @generated using pocketbase-typegen
|
||||
*/
|
||||
|
||||
import type PocketBase from 'pocketbase'
|
||||
import { type RecordService } from 'pocketbase'
|
||||
|
||||
export enum Collections {
|
||||
Base = "base",
|
||||
CustomAuth = "custom_auth",
|
||||
@@ -117,5 +120,18 @@ export type CollectionResponses = {
|
||||
my_view: MyViewResponse
|
||||
posts: PostsResponse
|
||||
users: UsersResponse
|
||||
}"
|
||||
}
|
||||
|
||||
// Type for usage with type asserted PocketBase instance
|
||||
// https://github.com/pocketbase/js-sdk#specify-typescript-definitions
|
||||
|
||||
export type TypedPocketBase = PocketBase & {
|
||||
collection(idOrName: 'base'): RecordService<BaseResponse>
|
||||
collection(idOrName: 'custom_auth'): RecordService<CustomAuthResponse>
|
||||
collection(idOrName: 'everything'): RecordService<EverythingResponse>
|
||||
collection(idOrName: 'my_view'): RecordService<MyViewResponse>
|
||||
collection(idOrName: 'posts'): RecordService<PostsResponse>
|
||||
collection(idOrName: 'users'): RecordService<UsersResponse>
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -25,6 +25,9 @@ exports[`generate generates correct output given db input 1`] = `
|
||||
* This file was @generated using pocketbase-typegen
|
||||
*/
|
||||
|
||||
import type PocketBase from 'pocketbase'
|
||||
import { type RecordService } from 'pocketbase'
|
||||
|
||||
export enum Collections {
|
||||
Books = "books",
|
||||
}
|
||||
@@ -68,5 +71,13 @@ export type CollectionRecords = {
|
||||
|
||||
export type CollectionResponses = {
|
||||
books: BooksResponse
|
||||
}"
|
||||
}
|
||||
|
||||
// Type for usage with type asserted PocketBase instance
|
||||
// https://github.com/pocketbase/js-sdk#specify-typescript-definitions
|
||||
|
||||
export type TypedPocketBase = PocketBase & {
|
||||
collection(idOrName: 'books'): RecordService<BooksResponse>
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
createCollectionEnum,
|
||||
createCollectionRecords,
|
||||
createCollectionResponses,
|
||||
createTypedPocketbase,
|
||||
} from "../src/collections"
|
||||
|
||||
describe("createCollectionEnum", () => {
|
||||
@@ -24,3 +25,10 @@ describe("createCollectionResponses", () => {
|
||||
expect(createCollectionResponses(names)).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
describe("createTypedPocketBase", () => {
|
||||
it("creates typed variant of PocketBase client", () => {
|
||||
const names = ["book", "magazine"]
|
||||
expect(createTypedPocketbase(names)).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -27,9 +27,38 @@ describe("generate", () => {
|
||||
viewRule: null,
|
||||
},
|
||||
]
|
||||
const result = generate(collections)
|
||||
const result = generate(collections, { sdk: true })
|
||||
expect(result).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it("skips generatic sdk if told not to", () => {
|
||||
const collections: Array<CollectionRecord> = [
|
||||
{
|
||||
createRule: null,
|
||||
deleteRule: null,
|
||||
id: "123",
|
||||
listRule: null,
|
||||
name: "books",
|
||||
schema: [
|
||||
{
|
||||
id: "xyz",
|
||||
name: "title",
|
||||
options: {},
|
||||
required: false,
|
||||
system: false,
|
||||
type: "text",
|
||||
unique: false,
|
||||
},
|
||||
],
|
||||
system: false,
|
||||
type: "base",
|
||||
updateRule: null,
|
||||
viewRule: null,
|
||||
},
|
||||
]
|
||||
const result = generate(collections, { sdk: false })
|
||||
expect(result).not.toMatch(/import .* from 'pocketbase'/)
|
||||
})
|
||||
})
|
||||
|
||||
describe("createRecordType", () => {
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
* This file was @generated using pocketbase-typegen
|
||||
*/
|
||||
|
||||
import type PocketBase from 'pocketbase'
|
||||
import { type RecordService } from 'pocketbase'
|
||||
|
||||
export enum Collections {
|
||||
Base = "base",
|
||||
CustomAuth = "custom_auth",
|
||||
@@ -114,4 +117,16 @@ export type CollectionResponses = {
|
||||
my_view: MyViewResponse
|
||||
posts: PostsResponse
|
||||
users: UsersResponse
|
||||
}
|
||||
}
|
||||
|
||||
// Type for usage with type asserted PocketBase instance
|
||||
// https://github.com/pocketbase/js-sdk#specify-typescript-definitions
|
||||
|
||||
export type TypedPocketBase = PocketBase & {
|
||||
collection(idOrName: 'base'): RecordService<BaseResponse>
|
||||
collection(idOrName: 'custom_auth'): RecordService<CustomAuthResponse>
|
||||
collection(idOrName: 'everything'): RecordService<EverythingResponse>
|
||||
collection(idOrName: 'my_view'): RecordService<MyViewResponse>
|
||||
collection(idOrName: 'posts'): RecordService<PostsResponse>
|
||||
collection(idOrName: 'users'): RecordService<UsersResponse>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user