From 7e04bf4f28575c864176ee3cc208355da993d3a9 Mon Sep 17 00:00:00 2001 From: Ivan Date: Tue, 24 Dec 2024 15:48:22 +0100 Subject: [PATCH 1/6] Add types for Create and Update --- dist/index.js | 169 ++++++++++++++++++++++++++++++++++++++--------- src/constants.ts | 28 ++++++++ src/fields.ts | 63 +++++++++++++++++- src/lib.ts | 89 ++++++++++++++++++++++--- src/utils.ts | 20 ++++++ 5 files changed, 324 insertions(+), 45 deletions(-) diff --git a/dist/index.js b/dist/index.js index d593635..89d6ae9 100755 --- a/dist/index.js +++ b/dist/index.js @@ -58,39 +58,6 @@ async function fromURL(url, email = "", password = "") { return collections; } -// src/constants.ts -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`; -var HTML_STRING_NAME = `HTMLString`; -var ALIAS_TYPE_DEFINITIONS = `// Alias types for improved usability -export type ${DATE_STRING_TYPE_NAME} = string -export type ${RECORD_ID_STRING_NAME} = string -export type ${HTML_STRING_NAME} = string`; -var BASE_SYSTEM_FIELDS_DEFINITION = `// System fields -export type BaseSystemFields = { - id: ${RECORD_ID_STRING_NAME} - collectionId: string - collectionName: Collections - expand?: T -}`; -var AUTH_SYSTEM_FIELDS_DEFINITION = `export type AuthSystemFields = { - email: string - emailVisibility: boolean - username: string - verified: boolean -} & BaseSystemFields`; - // src/utils.ts import { promises as fs2 } from "fs"; function toPascalCase(str) { @@ -117,6 +84,22 @@ function getSystemFields(type) { return "BaseSystemFields"; } } +function getSystemCreateFields(type) { + switch (type) { + case "auth": + return "AuthSystemCreateFields"; + default: + return "BaseSystemCreateFields"; + } +} +function getSystemUpdateFields(type) { + switch (type) { + case "auth": + return "AuthSystemUpdateFields"; + default: + return "BaseSystemUpdateFields"; + } +} function getOptionEnumName(recordName, fieldName) { return `${toPascalCase(recordName)}${toPascalCase(fieldName)}Options`; } @@ -158,6 +141,63 @@ ${nameRecordMap} }`; } +// src/constants.ts +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 CREATE_TYPE_COMMENT = `// Create types for each collection`; +var UPDATE_TYPE_COMMENT = `// Update 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`; +var HTML_STRING_NAME = `HTMLString`; +var ALIAS_TYPE_DEFINITIONS = `// Alias types for improved usability +export type ${DATE_STRING_TYPE_NAME} = string +export type ${RECORD_ID_STRING_NAME} = string +export type ${HTML_STRING_NAME} = string`; +var NOT_COMMON_COLLECTIONS = ["_authOrigins", "_externalAuths", "_mfas", "_otps"]; +var EXTRA_SYSTEM_FIELDS = ["created", "updated"]; +var BASE_SYSTEM_FIELDS_DEFINITION = `// System fields +export type BaseSystemFields = { + id: ${RECORD_ID_STRING_NAME} + collectionId: string + collectionName: Collections + expand?: T +}`; +var BASE_SYSTEM_CREATE_FIELDS_DEFINITION = `export type BaseSystemCreateFields = { + id?: ${RECORD_ID_STRING_NAME} +}`; +var BASE_SYSTEM_UPDATE_FIELDS_DEFINITION = `export type BaseSystemUpdateFields = never`; +var AUTH_SYSTEM_FIELDS_DEFINITION = `export type AuthSystemFields = { + email: string + emailVisibility: boolean + username: string + verified: boolean +} & BaseSystemFields`; +var AUTH_SYSTEM_CREATE_FIELDS_DEFINITION = `export type AuthSystemCreateFields = { + id?: ${RECORD_ID_STRING_NAME} + email: string + emailVisibility?: boolean + password: string + passwordConfirm: string + verified?: boolean +}`; +var AUTH_SYSTEM_UPDATE_FIELDS_DEFINITION = `export type AuthSystemUpdateFields = { + email?: string + emailVisibility?: boolean + oldPassword?: string + password?: string + passwordConfirm?: string + verified?: boolean +}`; + // src/generics.ts function fieldNameToGeneric(name) { return `T${name}`; @@ -194,6 +234,7 @@ var pbSchemaTypescriptMap = { password: "string", number: "number", file: (fieldSchema) => fieldSchema.maxSelect && fieldSchema.maxSelect > 1 ? "string[]" : "string", + _file: (fieldSchema) => fieldSchema.maxSelect && fieldSchema.maxSelect > 1 ? "File[]" : "File", json: (fieldSchema) => `null | ${fieldNameToGeneric(fieldSchema.name)}`, relation: (fieldSchema) => fieldSchema.maxSelect && fieldSchema.maxSelect === 1 ? RECORD_ID_STRING_NAME : `${RECORD_ID_STRING_NAME}[]`, select: (fieldSchema, collectionName) => { @@ -215,6 +256,33 @@ function createTypeField(collectionName, fieldSchema) { const required = fieldSchema.required ? "" : "?"; return ` ${fieldName}${required}: ${typeString}`; } +function createTypeCreateField(collectionName, fieldSchema) { + let typeStringOrFunc; + const fieldType = fieldSchema.type === "file" ? "_file" : fieldSchema.type; + if (!(fieldType in pbSchemaTypescriptMap)) { + console.log(`WARNING: unknown type "${fieldType}" found in schema`); + typeStringOrFunc = "unknown"; + } else { + typeStringOrFunc = pbSchemaTypescriptMap[fieldType]; + } + const typeString = typeof typeStringOrFunc === "function" ? typeStringOrFunc(fieldSchema, collectionName) : typeStringOrFunc; + const fieldName = sanitizeFieldName(fieldSchema.name); + const required = fieldSchema.required ? "" : "?"; + return ` ${fieldName}${required}: ${typeString}`; +} +function createTypeUpdateField(collectionName, fieldSchema) { + let typeStringOrFunc; + const fieldType = fieldSchema.type === "file" ? "_file" : fieldSchema.type; + if (!(fieldType in pbSchemaTypescriptMap)) { + console.log(`WARNING: unknown type "${fieldType}" found in schema`); + typeStringOrFunc = "unknown"; + } else { + typeStringOrFunc = pbSchemaTypescriptMap[fieldType]; + } + const typeString = typeof typeStringOrFunc === "function" ? typeStringOrFunc(fieldSchema, collectionName) : typeStringOrFunc; + const fieldName = sanitizeFieldName(fieldSchema.name); + return ` ${fieldName}?: ${typeString}`; +} function createSelectOptions(recordName, fields) { const selectFields = fields.filter((field) => field.type === "select"); const typestring = selectFields.map( @@ -237,12 +305,18 @@ function getSelectOptionEnumName(val) { function generate(results, options2) { const collectionNames = []; const recordTypes = []; + const createTypes = []; + const updateTypes = []; const responseTypes = [RESPONSE_TYPE_COMMENT]; results.sort((a, b) => a.name <= b.name ? -1 : 1).forEach((row) => { if (row.name) collectionNames.push(row.name); if (row.fields) { recordTypes.push(createRecordType(row.name, row.fields)); + if (!NOT_COMMON_COLLECTIONS.includes(row.name)) { + createTypes.push(createCreateType(row)); + updateTypes.push(createUpdateType(row)); + } responseTypes.push(createResponseType(row)); } }); @@ -253,9 +327,17 @@ function generate(results, options2) { createCollectionEnum(sortedCollectionNames), ALIAS_TYPE_DEFINITIONS, BASE_SYSTEM_FIELDS_DEFINITION, + BASE_SYSTEM_CREATE_FIELDS_DEFINITION, + BASE_SYSTEM_UPDATE_FIELDS_DEFINITION, AUTH_SYSTEM_FIELDS_DEFINITION, + AUTH_SYSTEM_CREATE_FIELDS_DEFINITION, + AUTH_SYSTEM_UPDATE_FIELDS_DEFINITION, RECORD_TYPE_COMMENT, ...recordTypes, + CREATE_TYPE_COMMENT, + ...createTypes, + UPDATE_TYPE_COMMENT, + ...updateTypes, responseTypes.join("\n"), ALL_RECORD_RESPONSE_COMMENT, createCollectionRecords(sortedCollectionNames), @@ -276,6 +358,27 @@ function createRecordType(name, schema) { ${fields} }` : "never"}`; } +function createCreateType(collectionSchemaEntry) { + const { name, fields, type } = collectionSchemaEntry; + const typeName = toPascalCase(name); + const systemFields = getSystemCreateFields(type); + const collectionFields = fields.filter((fieldSchema) => !fieldSchema.system && !EXTRA_SYSTEM_FIELDS.includes(fieldSchema.name)).map((fieldSchema) => createTypeCreateField(name, fieldSchema)).sort().join("\n"); + return `export type ${typeName}Create = ${collectionFields ? `{ +${collectionFields} +} & ${systemFields}` : systemFields}`; +} +function createUpdateType(collectionSchemaEntry) { + const { name, fields, type } = collectionSchemaEntry; + const typeName = toPascalCase(name); + if (name === "users") { + console.log(name, type, fields); + } + const systemFields = getSystemUpdateFields(type); + const collectionFields = fields.filter((fieldSchema) => !fieldSchema.system && !EXTRA_SYSTEM_FIELDS.includes(fieldSchema.name)).map((fieldSchema) => createTypeUpdateField(name, fieldSchema)).sort().join("\n"); + return `export type ${typeName}Update = ${collectionFields ? `{ +${collectionFields} +} & ${systemFields}` : systemFields}`; +} function createResponseType(collectionSchemaEntry) { const { name, fields, type } = collectionSchemaEntry; const pascaleName = toPascalCase(name); diff --git a/src/constants.ts b/src/constants.ts index 4f47537..38f623a 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -4,6 +4,8 @@ export const EXPORT_COMMENT = `/** 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 CREATE_TYPE_COMMENT = `// Create types for each collection` +export const UPDATE_TYPE_COMMENT = `// Update 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` @@ -15,6 +17,8 @@ export const ALIAS_TYPE_DEFINITIONS = `// Alias types for improved usability export type ${DATE_STRING_TYPE_NAME} = string export type ${RECORD_ID_STRING_NAME} = string export type ${HTML_STRING_NAME} = string` +export const NOT_COMMON_COLLECTIONS = ['_authOrigins', '_externalAuths', '_mfas', '_otps'] +export const EXTRA_SYSTEM_FIELDS = ['created', 'updated'] export const BASE_SYSTEM_FIELDS_DEFINITION = `// System fields export type BaseSystemFields = { @@ -24,9 +28,33 @@ export type BaseSystemFields = { \texpand?: T }` +export const BASE_SYSTEM_CREATE_FIELDS_DEFINITION = `export type BaseSystemCreateFields = { +\tid?: ${RECORD_ID_STRING_NAME} +}` + +export const BASE_SYSTEM_UPDATE_FIELDS_DEFINITION = `export type BaseSystemUpdateFields = never` + export const AUTH_SYSTEM_FIELDS_DEFINITION = `export type AuthSystemFields = { \temail: string \temailVisibility: boolean \tusername: string \tverified: boolean } & BaseSystemFields` + +export const AUTH_SYSTEM_CREATE_FIELDS_DEFINITION = `export type AuthSystemCreateFields = { +\tid?: ${RECORD_ID_STRING_NAME} +\temail: string +\temailVisibility?: boolean +\tpassword: string +\tpasswordConfirm: string +\tverified?: boolean +}` + +export const AUTH_SYSTEM_UPDATE_FIELDS_DEFINITION = `export type AuthSystemUpdateFields = { +\temail?: string +\temailVisibility?: boolean +\toldPassword?: string +\tpassword?: string +\tpasswordConfirm?: string +\tverified?: boolean +}` diff --git a/src/fields.ts b/src/fields.ts index 3e116bf..12d4508 100644 --- a/src/fields.ts +++ b/src/fields.ts @@ -5,8 +5,8 @@ import { } from "./constants" import { getOptionEnumName, getOptionValues, sanitizeFieldName } from "./utils" -import { FieldSchema } from "./types" import { fieldNameToGeneric } from "./generics" +import { FieldSchema } from "./types" /** * Convert the pocketbase field type to the equivalent typescript type @@ -26,6 +26,8 @@ export const pbSchemaTypescriptMap = { // Dependent on schema file: (fieldSchema: FieldSchema) => fieldSchema.maxSelect && fieldSchema.maxSelect > 1 ? "string[]" : "string", + _file: (fieldSchema: FieldSchema) => + fieldSchema.maxSelect && fieldSchema.maxSelect > 1 ? "File[]" : "File", json: (fieldSchema: FieldSchema) => `null | ${fieldNameToGeneric(fieldSchema.name)}`, relation: (fieldSchema: FieldSchema) => @@ -78,6 +80,65 @@ export function createTypeField( return `\t${fieldName}${required}: ${typeString}` } +export function createTypeCreateField( + collectionName: string, + fieldSchema: FieldSchema, +): string { + let typeStringOrFunc: + | string + | ((fieldSchema: FieldSchema, collectionName: string) => string) + + const fieldType = fieldSchema.type === 'file' ? '_file' : fieldSchema.type; + if (!(fieldType in pbSchemaTypescriptMap)) { + console.log(`WARNING: unknown type "${fieldType}" found in schema`) + typeStringOrFunc = "unknown" + } else { + typeStringOrFunc = + pbSchemaTypescriptMap[ + fieldType as keyof typeof pbSchemaTypescriptMap + ] + } + + const typeString = + typeof typeStringOrFunc === "function" + ? typeStringOrFunc(fieldSchema, collectionName) + : typeStringOrFunc + + const fieldName = sanitizeFieldName(fieldSchema.name) + const required = fieldSchema.required ? "" : "?" + + return `\t${fieldName}${required}: ${typeString}` +} + +export function createTypeUpdateField( + collectionName: string, + fieldSchema: FieldSchema, +): string { + let typeStringOrFunc: + | string + | ((fieldSchema: FieldSchema, collectionName: string) => string) + + const fieldType = fieldSchema.type === 'file' ? '_file' : fieldSchema.type; + if (!(fieldType in pbSchemaTypescriptMap)) { + console.log(`WARNING: unknown type "${fieldType}" found in schema`) + typeStringOrFunc = "unknown" + } else { + typeStringOrFunc = + pbSchemaTypescriptMap[ + fieldType as keyof typeof pbSchemaTypescriptMap + ] + } + + const typeString = + typeof typeStringOrFunc === "function" + ? typeStringOrFunc(fieldSchema, collectionName) + : typeStringOrFunc + + const fieldName = sanitizeFieldName(fieldSchema.name) + + return `\t${fieldName}?: ${typeString}` +} + export function createSelectOptions( recordName: string, fields: Array diff --git a/src/lib.ts b/src/lib.ts index dc818ed..fede003 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -1,28 +1,36 @@ +import { + createCollectionEnum, + createCollectionRecords, + createCollectionResponses, + createTypedPocketbase, +} from "./collections" import { ALIAS_TYPE_DEFINITIONS, ALL_RECORD_RESPONSE_COMMENT, - TYPED_POCKETBASE_COMMENT, + AUTH_SYSTEM_CREATE_FIELDS_DEFINITION, AUTH_SYSTEM_FIELDS_DEFINITION, + AUTH_SYSTEM_UPDATE_FIELDS_DEFINITION, + BASE_SYSTEM_CREATE_FIELDS_DEFINITION, BASE_SYSTEM_FIELDS_DEFINITION, + BASE_SYSTEM_UPDATE_FIELDS_DEFINITION, + CREATE_TYPE_COMMENT, EXPAND_GENERIC_NAME, EXPORT_COMMENT, + EXTRA_SYSTEM_FIELDS, + IMPORTS, + NOT_COMMON_COLLECTIONS, RECORD_TYPE_COMMENT, RESPONSE_TYPE_COMMENT, - IMPORTS, + TYPED_POCKETBASE_COMMENT, + UPDATE_TYPE_COMMENT, } from "./constants" -import { CollectionRecord, FieldSchema } from "./types" -import { - createCollectionEnum, - createCollectionRecords, - createCollectionResponses, - createTypedPocketbase, -} from "./collections" -import { createSelectOptions, createTypeField } from "./fields" +import { createSelectOptions, createTypeCreateField, createTypeField, createTypeUpdateField } from "./fields" import { getGenericArgStringForRecord, getGenericArgStringWithDefault, } from "./generics" -import { getSystemFields, toPascalCase } from "./utils" +import { CollectionRecord, FieldSchema } from "./types" +import { getSystemCreateFields, getSystemFields, getSystemUpdateFields, toPascalCase } from "./utils" type GenerateOptions = { sdk: boolean @@ -34,6 +42,8 @@ export function generate( ): string { const collectionNames: Array = [] const recordTypes: Array = [] + const createTypes: Array = [] + const updateTypes: Array = [] const responseTypes: Array = [RESPONSE_TYPE_COMMENT] results @@ -42,6 +52,10 @@ export function generate( if (row.name) collectionNames.push(row.name) if (row.fields) { recordTypes.push(createRecordType(row.name, row.fields)) + if (!NOT_COMMON_COLLECTIONS.includes(row.name)) { + createTypes.push(createCreateType(row)) + updateTypes.push(createUpdateType(row)) + } responseTypes.push(createResponseType(row)) } }) @@ -53,9 +67,17 @@ export function generate( createCollectionEnum(sortedCollectionNames), ALIAS_TYPE_DEFINITIONS, BASE_SYSTEM_FIELDS_DEFINITION, + BASE_SYSTEM_CREATE_FIELDS_DEFINITION, + BASE_SYSTEM_UPDATE_FIELDS_DEFINITION, AUTH_SYSTEM_FIELDS_DEFINITION, + AUTH_SYSTEM_CREATE_FIELDS_DEFINITION, + AUTH_SYSTEM_UPDATE_FIELDS_DEFINITION, RECORD_TYPE_COMMENT, ...recordTypes, + CREATE_TYPE_COMMENT, + ...createTypes, + UPDATE_TYPE_COMMENT, + ...updateTypes, responseTypes.join("\n"), ALL_RECORD_RESPONSE_COMMENT, createCollectionRecords(sortedCollectionNames), @@ -90,6 +112,51 @@ ${fields} }` } +export function createCreateType( + collectionSchemaEntry: CollectionRecord +): string { + const { name, fields, type } = collectionSchemaEntry + const typeName = toPascalCase(name) + const systemFields = getSystemCreateFields(type) + const collectionFields = fields + .filter((fieldSchema: FieldSchema) => !fieldSchema.system && !EXTRA_SYSTEM_FIELDS.includes(fieldSchema.name)) + .map((fieldSchema: FieldSchema) => createTypeCreateField(name, fieldSchema)) + .sort() + .join("\n") + + return `export type ${typeName}Create = ${ + collectionFields + ? `{ +${collectionFields} +} & ${systemFields}` + : systemFields + }` +} + +export function createUpdateType( + collectionSchemaEntry: CollectionRecord +): string { + const { name, fields, type } = collectionSchemaEntry + const typeName = toPascalCase(name) + if (name === 'users') { + console.log(name, type, fields); + } + const systemFields = getSystemUpdateFields(type) + const collectionFields = fields + .filter((fieldSchema: FieldSchema) => !fieldSchema.system && !EXTRA_SYSTEM_FIELDS.includes(fieldSchema.name)) + .map((fieldSchema: FieldSchema) => createTypeUpdateField(name, fieldSchema)) + .sort() + .join("\n") + + return `export type ${typeName}Update = ${ + collectionFields + ? `{ +${collectionFields} +} & ${systemFields}` + : systemFields + }` +} + export function createResponseType( collectionSchemaEntry: CollectionRecord ): string { diff --git a/src/utils.ts b/src/utils.ts index d6bac5e..69198eb 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -35,6 +35,26 @@ export function getSystemFields(type: CollectionRecord["type"]) { } } +export function getSystemCreateFields(type: CollectionRecord["type"]) { + switch (type) { + case "auth": + return "AuthSystemCreateFields" + default: + // `view` and `base` collection types share the same system fields (for now) + return "BaseSystemCreateFields" + } +} + +export function getSystemUpdateFields(type: CollectionRecord["type"]) { + switch (type) { + case "auth": + return "AuthSystemUpdateFields" + default: + // `view` and `base` collection types share the same system fields (for now) + return "BaseSystemUpdateFields" + } +} + export function getOptionEnumName(recordName: string, fieldName: string) { return `${toPascalCase(recordName)}${toPascalCase(fieldName)}Options` } From 639f2f67573bb2283d64f242c05312d6f25650a5 Mon Sep 17 00:00:00 2001 From: Ivan Date: Tue, 24 Dec 2024 15:55:27 +0100 Subject: [PATCH 2/6] Create collections type with all Create and Update types --- dist/index.js | 128 +++++++++++++++++++++++++-------------------- src/collections.ts | 25 +++++++++ src/lib.ts | 4 ++ 3 files changed, 100 insertions(+), 57 deletions(-) diff --git a/dist/index.js b/dist/index.js index 89d6ae9..4573118 100755 --- a/dist/index.js +++ b/dist/index.js @@ -58,6 +58,63 @@ async function fromURL(url, email = "", password = "") { return collections; } +// src/constants.ts +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 CREATE_TYPE_COMMENT = `// Create types for each collection`; +var UPDATE_TYPE_COMMENT = `// Update 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`; +var HTML_STRING_NAME = `HTMLString`; +var ALIAS_TYPE_DEFINITIONS = `// Alias types for improved usability +export type ${DATE_STRING_TYPE_NAME} = string +export type ${RECORD_ID_STRING_NAME} = string +export type ${HTML_STRING_NAME} = string`; +var NOT_COMMON_COLLECTIONS = ["_authOrigins", "_externalAuths", "_mfas", "_otps"]; +var EXTRA_SYSTEM_FIELDS = ["created", "updated"]; +var BASE_SYSTEM_FIELDS_DEFINITION = `// System fields +export type BaseSystemFields = { + id: ${RECORD_ID_STRING_NAME} + collectionId: string + collectionName: Collections + expand?: T +}`; +var BASE_SYSTEM_CREATE_FIELDS_DEFINITION = `export type BaseSystemCreateFields = { + id?: ${RECORD_ID_STRING_NAME} +}`; +var BASE_SYSTEM_UPDATE_FIELDS_DEFINITION = `export type BaseSystemUpdateFields = never`; +var AUTH_SYSTEM_FIELDS_DEFINITION = `export type AuthSystemFields = { + email: string + emailVisibility: boolean + username: string + verified: boolean +} & BaseSystemFields`; +var AUTH_SYSTEM_CREATE_FIELDS_DEFINITION = `export type AuthSystemCreateFields = { + id?: ${RECORD_ID_STRING_NAME} + email: string + emailVisibility?: boolean + password: string + passwordConfirm: string + verified?: boolean +}`; +var AUTH_SYSTEM_UPDATE_FIELDS_DEFINITION = `export type AuthSystemUpdateFields = { + email?: string + emailVisibility?: boolean + oldPassword?: string + password?: string + passwordConfirm?: string + verified?: boolean +}`; + // src/utils.ts import { promises as fs2 } from "fs"; function toPascalCase(str) { @@ -124,6 +181,18 @@ function createCollectionRecords(collectionNames) { ${nameRecordMap} }`; } +function createCollectionCreates(collectionNames) { + const nameRecordMap = collectionNames.filter((name) => !NOT_COMMON_COLLECTIONS.includes(name)).map((name) => ` ${name}: ${toPascalCase(name)}Create`).join("\n"); + return `export type CollectionCreates = { +${nameRecordMap} +}`; +} +function createCollectionUpdates(collectionNames) { + const nameRecordMap = collectionNames.filter((name) => !NOT_COMMON_COLLECTIONS.includes(name)).map((name) => ` ${name}: ${toPascalCase(name)}Update`).join("\n"); + return `export type CollectionUpdates = { +${nameRecordMap} +}`; +} function createCollectionResponses(collectionNames) { const nameRecordMap = collectionNames.map((name) => ` ${name}: ${toPascalCase(name)}Response`).join("\n"); return `export type CollectionResponses = { @@ -141,63 +210,6 @@ ${nameRecordMap} }`; } -// src/constants.ts -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 CREATE_TYPE_COMMENT = `// Create types for each collection`; -var UPDATE_TYPE_COMMENT = `// Update 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`; -var HTML_STRING_NAME = `HTMLString`; -var ALIAS_TYPE_DEFINITIONS = `// Alias types for improved usability -export type ${DATE_STRING_TYPE_NAME} = string -export type ${RECORD_ID_STRING_NAME} = string -export type ${HTML_STRING_NAME} = string`; -var NOT_COMMON_COLLECTIONS = ["_authOrigins", "_externalAuths", "_mfas", "_otps"]; -var EXTRA_SYSTEM_FIELDS = ["created", "updated"]; -var BASE_SYSTEM_FIELDS_DEFINITION = `// System fields -export type BaseSystemFields = { - id: ${RECORD_ID_STRING_NAME} - collectionId: string - collectionName: Collections - expand?: T -}`; -var BASE_SYSTEM_CREATE_FIELDS_DEFINITION = `export type BaseSystemCreateFields = { - id?: ${RECORD_ID_STRING_NAME} -}`; -var BASE_SYSTEM_UPDATE_FIELDS_DEFINITION = `export type BaseSystemUpdateFields = never`; -var AUTH_SYSTEM_FIELDS_DEFINITION = `export type AuthSystemFields = { - email: string - emailVisibility: boolean - username: string - verified: boolean -} & BaseSystemFields`; -var AUTH_SYSTEM_CREATE_FIELDS_DEFINITION = `export type AuthSystemCreateFields = { - id?: ${RECORD_ID_STRING_NAME} - email: string - emailVisibility?: boolean - password: string - passwordConfirm: string - verified?: boolean -}`; -var AUTH_SYSTEM_UPDATE_FIELDS_DEFINITION = `export type AuthSystemUpdateFields = { - email?: string - emailVisibility?: boolean - oldPassword?: string - password?: string - passwordConfirm?: string - verified?: boolean -}`; - // src/generics.ts function fieldNameToGeneric(name) { return `T${name}`; @@ -341,6 +353,8 @@ function generate(results, options2) { responseTypes.join("\n"), ALL_RECORD_RESPONSE_COMMENT, createCollectionRecords(sortedCollectionNames), + createCollectionCreates(sortedCollectionNames), + createCollectionUpdates(sortedCollectionNames), createCollectionResponses(sortedCollectionNames), options2.sdk && TYPED_POCKETBASE_COMMENT, options2.sdk && createTypedPocketbase(sortedCollectionNames) diff --git a/src/collections.ts b/src/collections.ts index 121de2c..1adfba5 100644 --- a/src/collections.ts +++ b/src/collections.ts @@ -1,3 +1,4 @@ +import { NOT_COMMON_COLLECTIONS } from "./constants" import { toPascalCase } from "./utils" export function createCollectionEnum(collectionNames: Array): string { @@ -21,6 +22,30 @@ ${nameRecordMap} }` } +export function createCollectionCreates( + collectionNames: Array +): string { + const nameRecordMap = collectionNames + .filter((name) => !NOT_COMMON_COLLECTIONS.includes(name)) + .map((name) => `\t${name}: ${toPascalCase(name)}Create`) + .join("\n") + return `export type CollectionCreates = { +${nameRecordMap} +}` +} + +export function createCollectionUpdates( + collectionNames: Array +): string { + const nameRecordMap = collectionNames + .filter((name) => !NOT_COMMON_COLLECTIONS.includes(name)) + .map((name) => `\t${name}: ${toPascalCase(name)}Update`) + .join("\n") + return `export type CollectionUpdates = { +${nameRecordMap} +}` +} + export function createCollectionResponses( collectionNames: Array ): string { diff --git a/src/lib.ts b/src/lib.ts index fede003..a153cf3 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -1,7 +1,9 @@ import { + createCollectionCreates, createCollectionEnum, createCollectionRecords, createCollectionResponses, + createCollectionUpdates, createTypedPocketbase, } from "./collections" import { @@ -81,6 +83,8 @@ export function generate( responseTypes.join("\n"), ALL_RECORD_RESPONSE_COMMENT, createCollectionRecords(sortedCollectionNames), + createCollectionCreates(sortedCollectionNames), + createCollectionUpdates(sortedCollectionNames), createCollectionResponses(sortedCollectionNames), options.sdk && TYPED_POCKETBASE_COMMENT, options.sdk && createTypedPocketbase(sortedCollectionNames), From c1dd78e970b4b2ee7d2ea5eacc0a3e31625c5ab6 Mon Sep 17 00:00:00 2001 From: Ivan Date: Tue, 24 Dec 2024 16:13:42 +0100 Subject: [PATCH 3/6] Add generic args to Create and Update types Also updated tests snapshot --- dist/index.js | 10 +- src/lib.ts | 13 ++- test/__snapshots__/fromJSON.test.ts.snap | 140 +++++++++++++++++++++++ test/__snapshots__/lib.test.ts.snap | 44 +++++++ test/pocketbase-types-example.ts | 140 +++++++++++++++++++++++ 5 files changed, 340 insertions(+), 7 deletions(-) diff --git a/dist/index.js b/dist/index.js index 4573118..94dabc2 100755 --- a/dist/index.js +++ b/dist/index.js @@ -375,21 +375,27 @@ ${fields} function createCreateType(collectionSchemaEntry) { const { name, fields, type } = collectionSchemaEntry; const typeName = toPascalCase(name); + const genericArgs = getGenericArgStringWithDefault(fields, { + includeExpand: false + }); const systemFields = getSystemCreateFields(type); const collectionFields = fields.filter((fieldSchema) => !fieldSchema.system && !EXTRA_SYSTEM_FIELDS.includes(fieldSchema.name)).map((fieldSchema) => createTypeCreateField(name, fieldSchema)).sort().join("\n"); - return `export type ${typeName}Create = ${collectionFields ? `{ + return `export type ${typeName}Create${genericArgs} = ${collectionFields ? `{ ${collectionFields} } & ${systemFields}` : systemFields}`; } function createUpdateType(collectionSchemaEntry) { const { name, fields, type } = collectionSchemaEntry; const typeName = toPascalCase(name); + const genericArgs = getGenericArgStringWithDefault(fields, { + includeExpand: false + }); if (name === "users") { console.log(name, type, fields); } const systemFields = getSystemUpdateFields(type); const collectionFields = fields.filter((fieldSchema) => !fieldSchema.system && !EXTRA_SYSTEM_FIELDS.includes(fieldSchema.name)).map((fieldSchema) => createTypeUpdateField(name, fieldSchema)).sort().join("\n"); - return `export type ${typeName}Update = ${collectionFields ? `{ + return `export type ${typeName}Update${genericArgs} = ${collectionFields ? `{ ${collectionFields} } & ${systemFields}` : systemFields}`; } diff --git a/src/lib.ts b/src/lib.ts index a153cf3..fb02956 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -121,6 +121,9 @@ export function createCreateType( ): string { const { name, fields, type } = collectionSchemaEntry const typeName = toPascalCase(name) + const genericArgs = getGenericArgStringWithDefault(fields, { + includeExpand: false, + }) const systemFields = getSystemCreateFields(type) const collectionFields = fields .filter((fieldSchema: FieldSchema) => !fieldSchema.system && !EXTRA_SYSTEM_FIELDS.includes(fieldSchema.name)) @@ -128,7 +131,7 @@ export function createCreateType( .sort() .join("\n") - return `export type ${typeName}Create = ${ + return `export type ${typeName}Create${genericArgs} = ${ collectionFields ? `{ ${collectionFields} @@ -142,9 +145,9 @@ export function createUpdateType( ): string { const { name, fields, type } = collectionSchemaEntry const typeName = toPascalCase(name) - if (name === 'users') { - console.log(name, type, fields); - } + const genericArgs = getGenericArgStringWithDefault(fields, { + includeExpand: false, + }) const systemFields = getSystemUpdateFields(type) const collectionFields = fields .filter((fieldSchema: FieldSchema) => !fieldSchema.system && !EXTRA_SYSTEM_FIELDS.includes(fieldSchema.name)) @@ -152,7 +155,7 @@ export function createUpdateType( .sort() .join("\n") - return `export type ${typeName}Update = ${ + return `export type ${typeName}Update${genericArgs} = ${ collectionFields ? `{ ${collectionFields} diff --git a/test/__snapshots__/fromJSON.test.ts.snap b/test/__snapshots__/fromJSON.test.ts.snap index 302c368..ab1acf8 100644 --- a/test/__snapshots__/fromJSON.test.ts.snap +++ b/test/__snapshots__/fromJSON.test.ts.snap @@ -35,6 +35,12 @@ export type BaseSystemFields = { expand?: T } +export type BaseSystemCreateFields = { + id?: RecordIdString +} + +export type BaseSystemUpdateFields = never + export type AuthSystemFields = { email: string emailVisibility: boolean @@ -42,6 +48,24 @@ export type AuthSystemFields = { verified: boolean } & BaseSystemFields +export type AuthSystemCreateFields = { + id?: RecordIdString + email: string + emailVisibility?: boolean + password: string + passwordConfirm: string + verified?: boolean +} + +export type AuthSystemUpdateFields = { + email?: string + emailVisibility?: boolean + oldPassword?: string + password?: string + passwordConfirm?: string + verified?: boolean +} + // Record types for each collection export type AuthoriginsRecord = { @@ -171,6 +195,102 @@ export type UsersRecord = { verified?: boolean } +// Create types for each collection + +export type SuperusersCreate = AuthSystemCreateFields + +export type BaseCreate = { + field?: string +} & BaseSystemCreateFields + +export type CustomAuthCreate = { + custom_field?: string +} & AuthSystemCreateFields + +export type EverythingCreate = { + another_json_field?: null | Tanother_json_field + bool_field?: boolean + custom_relation_field?: RecordIdString[] + date_field?: IsoDateString + email_field?: string + file_field?: File + json_field?: null | Tjson_field + number_field?: number + post_relation_field?: RecordIdString + rich_editor_field?: HTMLString + select_field?: EverythingSelectFieldOptions + select_field_no_values?: string + text_field?: string + three_files_field?: File[] + url_field?: string + user_relation_field?: RecordIdString +} & BaseSystemCreateFields + +export type MyViewCreate = { + json_field?: null | Tjson_field + post_relation_field?: RecordIdString + text_field?: string +} & BaseSystemCreateFields + +export type PostsCreate = { + field1?: number + nonempty_bool: boolean + nonempty_field: string +} & BaseSystemCreateFields + +export type UsersCreate = { + avatar?: File + name?: string +} & AuthSystemCreateFields + +// Update types for each collection + +export type SuperusersUpdate = AuthSystemUpdateFields + +export type BaseUpdate = { + field?: string +} & BaseSystemUpdateFields + +export type CustomAuthUpdate = { + custom_field?: string +} & AuthSystemUpdateFields + +export type EverythingUpdate = { + another_json_field?: null | Tanother_json_field + bool_field?: boolean + custom_relation_field?: RecordIdString[] + date_field?: IsoDateString + email_field?: string + file_field?: File + json_field?: null | Tjson_field + number_field?: number + post_relation_field?: RecordIdString + rich_editor_field?: HTMLString + select_field?: EverythingSelectFieldOptions + select_field_no_values?: string + text_field?: string + three_files_field?: File[] + url_field?: string + user_relation_field?: RecordIdString +} & BaseSystemUpdateFields + +export type MyViewUpdate = { + json_field?: null | Tjson_field + post_relation_field?: RecordIdString + text_field?: string +} & BaseSystemUpdateFields + +export type PostsUpdate = { + field1?: number + nonempty_bool?: boolean + nonempty_field?: string +} & BaseSystemUpdateFields + +export type UsersUpdate = { + avatar?: File + name?: string +} & AuthSystemUpdateFields + // Response types include system fields and match responses from the PocketBase API export type AuthoriginsResponse = Required & BaseSystemFields export type ExternalauthsResponse = Required & BaseSystemFields @@ -200,6 +320,26 @@ export type CollectionRecords = { users: UsersRecord } +export type CollectionCreates = { + _superusers: SuperusersCreate + base: BaseCreate + custom_auth: CustomAuthCreate + everything: EverythingCreate + my_view: MyViewCreate + posts: PostsCreate + users: UsersCreate +} + +export type CollectionUpdates = { + _superusers: SuperusersUpdate + base: BaseUpdate + custom_auth: CustomAuthUpdate + everything: EverythingUpdate + my_view: MyViewUpdate + posts: PostsUpdate + users: UsersUpdate +} + export type CollectionResponses = { _authOrigins: AuthoriginsResponse _externalAuths: ExternalauthsResponse diff --git a/test/__snapshots__/lib.test.ts.snap b/test/__snapshots__/lib.test.ts.snap index e16d4b3..09a27e8 100644 --- a/test/__snapshots__/lib.test.ts.snap +++ b/test/__snapshots__/lib.test.ts.snap @@ -45,6 +45,12 @@ export type BaseSystemFields = { expand?: T } +export type BaseSystemCreateFields = { + id?: RecordIdString +} + +export type BaseSystemUpdateFields = never + export type AuthSystemFields = { email: string emailVisibility: boolean @@ -52,12 +58,42 @@ export type AuthSystemFields = { verified: boolean } & BaseSystemFields +export type AuthSystemCreateFields = { + id?: RecordIdString + email: string + emailVisibility?: boolean + password: string + passwordConfirm: string + verified?: boolean +} + +export type AuthSystemUpdateFields = { + email?: string + emailVisibility?: boolean + oldPassword?: string + password?: string + passwordConfirm?: string + verified?: boolean +} + // Record types for each collection export type BooksRecord = { title?: string } +// Create types for each collection + +export type BooksCreate = { + title?: string +} & BaseSystemCreateFields + +// Update types for each collection + +export type BooksUpdate = { + title?: string +} & BaseSystemUpdateFields + // Response types include system fields and match responses from the PocketBase API export type BooksResponse = Required & BaseSystemFields @@ -67,6 +103,14 @@ export type CollectionRecords = { books: BooksRecord } +export type CollectionCreates = { + books: BooksCreate +} + +export type CollectionUpdates = { + books: BooksUpdate +} + export type CollectionResponses = { books: BooksResponse } diff --git a/test/pocketbase-types-example.ts b/test/pocketbase-types-example.ts index 82bbf3d..321d57a 100644 --- a/test/pocketbase-types-example.ts +++ b/test/pocketbase-types-example.ts @@ -32,6 +32,12 @@ export type BaseSystemFields = { expand?: T } +export type BaseSystemCreateFields = { + id?: RecordIdString +} + +export type BaseSystemUpdateFields = never + export type AuthSystemFields = { email: string emailVisibility: boolean @@ -39,6 +45,24 @@ export type AuthSystemFields = { verified: boolean } & BaseSystemFields +export type AuthSystemCreateFields = { + id?: RecordIdString + email: string + emailVisibility?: boolean + password: string + passwordConfirm: string + verified?: boolean +} + +export type AuthSystemUpdateFields = { + email?: string + emailVisibility?: boolean + oldPassword?: string + password?: string + passwordConfirm?: string + verified?: boolean +} + // Record types for each collection export type AuthoriginsRecord = { @@ -168,6 +192,102 @@ export type UsersRecord = { verified?: boolean } +// Create types for each collection + +export type SuperusersCreate = AuthSystemCreateFields + +export type BaseCreate = { + field?: string +} & BaseSystemCreateFields + +export type CustomAuthCreate = { + custom_field?: string +} & AuthSystemCreateFields + +export type EverythingCreate = { + another_json_field?: null | Tanother_json_field + bool_field?: boolean + custom_relation_field?: RecordIdString[] + date_field?: IsoDateString + email_field?: string + file_field?: File + json_field?: null | Tjson_field + number_field?: number + post_relation_field?: RecordIdString + rich_editor_field?: HTMLString + select_field?: EverythingSelectFieldOptions + select_field_no_values?: string + text_field?: string + three_files_field?: File[] + url_field?: string + user_relation_field?: RecordIdString +} & BaseSystemCreateFields + +export type MyViewCreate = { + json_field?: null | Tjson_field + post_relation_field?: RecordIdString + text_field?: string +} & BaseSystemCreateFields + +export type PostsCreate = { + field1?: number + nonempty_bool: boolean + nonempty_field: string +} & BaseSystemCreateFields + +export type UsersCreate = { + avatar?: File + name?: string +} & AuthSystemCreateFields + +// Update types for each collection + +export type SuperusersUpdate = AuthSystemUpdateFields + +export type BaseUpdate = { + field?: string +} & BaseSystemUpdateFields + +export type CustomAuthUpdate = { + custom_field?: string +} & AuthSystemUpdateFields + +export type EverythingUpdate = { + another_json_field?: null | Tanother_json_field + bool_field?: boolean + custom_relation_field?: RecordIdString[] + date_field?: IsoDateString + email_field?: string + file_field?: File + json_field?: null | Tjson_field + number_field?: number + post_relation_field?: RecordIdString + rich_editor_field?: HTMLString + select_field?: EverythingSelectFieldOptions + select_field_no_values?: string + text_field?: string + three_files_field?: File[] + url_field?: string + user_relation_field?: RecordIdString +} & BaseSystemUpdateFields + +export type MyViewUpdate = { + json_field?: null | Tjson_field + post_relation_field?: RecordIdString + text_field?: string +} & BaseSystemUpdateFields + +export type PostsUpdate = { + field1?: number + nonempty_bool?: boolean + nonempty_field?: string +} & BaseSystemUpdateFields + +export type UsersUpdate = { + avatar?: File + name?: string +} & AuthSystemUpdateFields + // Response types include system fields and match responses from the PocketBase API export type AuthoriginsResponse = Required & BaseSystemFields export type ExternalauthsResponse = Required & BaseSystemFields @@ -197,6 +317,26 @@ export type CollectionRecords = { users: UsersRecord } +export type CollectionCreates = { + _superusers: SuperusersCreate + base: BaseCreate + custom_auth: CustomAuthCreate + everything: EverythingCreate + my_view: MyViewCreate + posts: PostsCreate + users: UsersCreate +} + +export type CollectionUpdates = { + _superusers: SuperusersUpdate + base: BaseUpdate + custom_auth: CustomAuthUpdate + everything: EverythingUpdate + my_view: MyViewUpdate + posts: PostsUpdate + users: UsersUpdate +} + export type CollectionResponses = { _authOrigins: AuthoriginsResponse _externalAuths: ExternalauthsResponse From 1c118b7fab0a64097bfd228b3f4513eca4eaabb2 Mon Sep 17 00:00:00 2001 From: Ivan Date: Tue, 24 Dec 2024 17:13:33 +0100 Subject: [PATCH 4/6] Improve type generation for file fields For Update requests file fields can be nullable --- dist/index.js | 13 ++++++------- src/constants.ts | 3 ++- src/fields.ts | 8 +++++--- test/__snapshots__/fromJSON.test.ts.snap | 7 ++++--- test/__snapshots__/lib.test.ts.snap | 1 + test/pocketbase-types-example.ts | 7 ++++--- 6 files changed, 22 insertions(+), 17 deletions(-) diff --git a/dist/index.js b/dist/index.js index 94dabc2..e8be7e1 100755 --- a/dist/index.js +++ b/dist/index.js @@ -78,7 +78,8 @@ var HTML_STRING_NAME = `HTMLString`; var ALIAS_TYPE_DEFINITIONS = `// Alias types for improved usability export type ${DATE_STRING_TYPE_NAME} = string export type ${RECORD_ID_STRING_NAME} = string -export type ${HTML_STRING_NAME} = string`; +export type ${HTML_STRING_NAME} = string +export type Nullable = T | null | ''`; var NOT_COMMON_COLLECTIONS = ["_authOrigins", "_externalAuths", "_mfas", "_otps"]; var EXTRA_SYSTEM_FIELDS = ["created", "updated"]; var BASE_SYSTEM_FIELDS_DEFINITION = `// System fields @@ -246,7 +247,8 @@ var pbSchemaTypescriptMap = { password: "string", number: "number", file: (fieldSchema) => fieldSchema.maxSelect && fieldSchema.maxSelect > 1 ? "string[]" : "string", - _file: (fieldSchema) => fieldSchema.maxSelect && fieldSchema.maxSelect > 1 ? "File[]" : "File", + file_create: (fieldSchema) => fieldSchema.maxSelect && fieldSchema.maxSelect > 1 ? "File[]" : "File", + file_update: (fieldSchema) => fieldSchema.maxSelect && fieldSchema.maxSelect > 1 ? "Nullable" : "Nullable", json: (fieldSchema) => `null | ${fieldNameToGeneric(fieldSchema.name)}`, relation: (fieldSchema) => fieldSchema.maxSelect && fieldSchema.maxSelect === 1 ? RECORD_ID_STRING_NAME : `${RECORD_ID_STRING_NAME}[]`, select: (fieldSchema, collectionName) => { @@ -270,7 +272,7 @@ function createTypeField(collectionName, fieldSchema) { } function createTypeCreateField(collectionName, fieldSchema) { let typeStringOrFunc; - const fieldType = fieldSchema.type === "file" ? "_file" : fieldSchema.type; + const fieldType = fieldSchema.type === "file" ? "file_create" : fieldSchema.type; if (!(fieldType in pbSchemaTypescriptMap)) { console.log(`WARNING: unknown type "${fieldType}" found in schema`); typeStringOrFunc = "unknown"; @@ -284,7 +286,7 @@ function createTypeCreateField(collectionName, fieldSchema) { } function createTypeUpdateField(collectionName, fieldSchema) { let typeStringOrFunc; - const fieldType = fieldSchema.type === "file" ? "_file" : fieldSchema.type; + const fieldType = fieldSchema.type === "file" ? "file_update" : fieldSchema.type; if (!(fieldType in pbSchemaTypescriptMap)) { console.log(`WARNING: unknown type "${fieldType}" found in schema`); typeStringOrFunc = "unknown"; @@ -390,9 +392,6 @@ function createUpdateType(collectionSchemaEntry) { const genericArgs = getGenericArgStringWithDefault(fields, { includeExpand: false }); - if (name === "users") { - console.log(name, type, fields); - } const systemFields = getSystemUpdateFields(type); const collectionFields = fields.filter((fieldSchema) => !fieldSchema.system && !EXTRA_SYSTEM_FIELDS.includes(fieldSchema.name)).map((fieldSchema) => createTypeUpdateField(name, fieldSchema)).sort().join("\n"); return `export type ${typeName}Update${genericArgs} = ${collectionFields ? `{ diff --git a/src/constants.ts b/src/constants.ts index 38f623a..f43b989 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -16,7 +16,8 @@ export const HTML_STRING_NAME = `HTMLString` export const ALIAS_TYPE_DEFINITIONS = `// Alias types for improved usability export type ${DATE_STRING_TYPE_NAME} = string export type ${RECORD_ID_STRING_NAME} = string -export type ${HTML_STRING_NAME} = string` +export type ${HTML_STRING_NAME} = string +export type Nullable = T | null | ''` export const NOT_COMMON_COLLECTIONS = ['_authOrigins', '_externalAuths', '_mfas', '_otps'] export const EXTRA_SYSTEM_FIELDS = ['created', 'updated'] diff --git a/src/fields.ts b/src/fields.ts index 12d4508..3d6fe4a 100644 --- a/src/fields.ts +++ b/src/fields.ts @@ -26,8 +26,10 @@ export const pbSchemaTypescriptMap = { // Dependent on schema file: (fieldSchema: FieldSchema) => fieldSchema.maxSelect && fieldSchema.maxSelect > 1 ? "string[]" : "string", - _file: (fieldSchema: FieldSchema) => + file_create: (fieldSchema: FieldSchema) => fieldSchema.maxSelect && fieldSchema.maxSelect > 1 ? "File[]" : "File", + file_update: (fieldSchema: FieldSchema) => + fieldSchema.maxSelect && fieldSchema.maxSelect > 1 ? "Nullable" : "Nullable", json: (fieldSchema: FieldSchema) => `null | ${fieldNameToGeneric(fieldSchema.name)}`, relation: (fieldSchema: FieldSchema) => @@ -88,7 +90,7 @@ export function createTypeCreateField( | string | ((fieldSchema: FieldSchema, collectionName: string) => string) - const fieldType = fieldSchema.type === 'file' ? '_file' : fieldSchema.type; + const fieldType = fieldSchema.type === 'file' ? 'file_create' : fieldSchema.type; if (!(fieldType in pbSchemaTypescriptMap)) { console.log(`WARNING: unknown type "${fieldType}" found in schema`) typeStringOrFunc = "unknown" @@ -118,7 +120,7 @@ export function createTypeUpdateField( | string | ((fieldSchema: FieldSchema, collectionName: string) => string) - const fieldType = fieldSchema.type === 'file' ? '_file' : fieldSchema.type; + const fieldType = fieldSchema.type === 'file' ? 'file_update' : fieldSchema.type; if (!(fieldType in pbSchemaTypescriptMap)) { console.log(`WARNING: unknown type "${fieldType}" found in schema`) typeStringOrFunc = "unknown" diff --git a/test/__snapshots__/fromJSON.test.ts.snap b/test/__snapshots__/fromJSON.test.ts.snap index ab1acf8..b9c8d10 100644 --- a/test/__snapshots__/fromJSON.test.ts.snap +++ b/test/__snapshots__/fromJSON.test.ts.snap @@ -26,6 +26,7 @@ export enum Collections { export type IsoDateString = string export type RecordIdString = string export type HTMLString = string +export type Nullable = T | null | '' // System fields export type BaseSystemFields = { @@ -261,7 +262,7 @@ export type EverythingUpdate json_field?: null | Tjson_field number_field?: number post_relation_field?: RecordIdString @@ -269,7 +270,7 @@ export type EverythingUpdate url_field?: string user_relation_field?: RecordIdString } & BaseSystemUpdateFields @@ -287,7 +288,7 @@ export type PostsUpdate = { } & BaseSystemUpdateFields export type UsersUpdate = { - avatar?: File + avatar?: Nullable name?: string } & AuthSystemUpdateFields diff --git a/test/__snapshots__/lib.test.ts.snap b/test/__snapshots__/lib.test.ts.snap index 09a27e8..a355a10 100644 --- a/test/__snapshots__/lib.test.ts.snap +++ b/test/__snapshots__/lib.test.ts.snap @@ -36,6 +36,7 @@ export enum Collections { export type IsoDateString = string export type RecordIdString = string export type HTMLString = string +export type Nullable = T | null | '' // System fields export type BaseSystemFields = { diff --git a/test/pocketbase-types-example.ts b/test/pocketbase-types-example.ts index 321d57a..2ff51d3 100644 --- a/test/pocketbase-types-example.ts +++ b/test/pocketbase-types-example.ts @@ -23,6 +23,7 @@ export enum Collections { export type IsoDateString = string export type RecordIdString = string export type HTMLString = string +export type Nullable = T | null | '' // System fields export type BaseSystemFields = { @@ -258,7 +259,7 @@ export type EverythingUpdate json_field?: null | Tjson_field number_field?: number post_relation_field?: RecordIdString @@ -266,7 +267,7 @@ export type EverythingUpdate url_field?: string user_relation_field?: RecordIdString } & BaseSystemUpdateFields @@ -284,7 +285,7 @@ export type PostsUpdate = { } & BaseSystemUpdateFields export type UsersUpdate = { - avatar?: File + avatar?: Nullable name?: string } & AuthSystemUpdateFields From ec26ee2ab13965a7fcc76fcc1aadf2a5913eb0e2 Mon Sep 17 00:00:00 2001 From: Ivan Date: Wed, 25 Dec 2024 02:13:39 +0100 Subject: [PATCH 5/6] Fix BaseSystemUpdateFields type --- dist/index.js | 2 +- src/constants.ts | 2 +- test/__snapshots__/fromJSON.test.ts.snap | 2 +- test/__snapshots__/lib.test.ts.snap | 2 +- test/pocketbase-types-example.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dist/index.js b/dist/index.js index e8be7e1..6f685fb 100755 --- a/dist/index.js +++ b/dist/index.js @@ -92,7 +92,7 @@ export type BaseSystemFields = { var BASE_SYSTEM_CREATE_FIELDS_DEFINITION = `export type BaseSystemCreateFields = { id?: ${RECORD_ID_STRING_NAME} }`; -var BASE_SYSTEM_UPDATE_FIELDS_DEFINITION = `export type BaseSystemUpdateFields = never`; +var BASE_SYSTEM_UPDATE_FIELDS_DEFINITION = `export type BaseSystemUpdateFields = unknown`; var AUTH_SYSTEM_FIELDS_DEFINITION = `export type AuthSystemFields = { email: string emailVisibility: boolean diff --git a/src/constants.ts b/src/constants.ts index f43b989..143916f 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -33,7 +33,7 @@ export const BASE_SYSTEM_CREATE_FIELDS_DEFINITION = `export type BaseSystemCreat \tid?: ${RECORD_ID_STRING_NAME} }` -export const BASE_SYSTEM_UPDATE_FIELDS_DEFINITION = `export type BaseSystemUpdateFields = never` +export const BASE_SYSTEM_UPDATE_FIELDS_DEFINITION = `export type BaseSystemUpdateFields = unknown` export const AUTH_SYSTEM_FIELDS_DEFINITION = `export type AuthSystemFields = { \temail: string diff --git a/test/__snapshots__/fromJSON.test.ts.snap b/test/__snapshots__/fromJSON.test.ts.snap index b9c8d10..5c2e15c 100644 --- a/test/__snapshots__/fromJSON.test.ts.snap +++ b/test/__snapshots__/fromJSON.test.ts.snap @@ -40,7 +40,7 @@ export type BaseSystemCreateFields = { id?: RecordIdString } -export type BaseSystemUpdateFields = never +export type BaseSystemUpdateFields = unknown export type AuthSystemFields = { email: string diff --git a/test/__snapshots__/lib.test.ts.snap b/test/__snapshots__/lib.test.ts.snap index a355a10..e288527 100644 --- a/test/__snapshots__/lib.test.ts.snap +++ b/test/__snapshots__/lib.test.ts.snap @@ -50,7 +50,7 @@ export type BaseSystemCreateFields = { id?: RecordIdString } -export type BaseSystemUpdateFields = never +export type BaseSystemUpdateFields = unknown export type AuthSystemFields = { email: string diff --git a/test/pocketbase-types-example.ts b/test/pocketbase-types-example.ts index 2ff51d3..d1c58f0 100644 --- a/test/pocketbase-types-example.ts +++ b/test/pocketbase-types-example.ts @@ -37,7 +37,7 @@ export type BaseSystemCreateFields = { id?: RecordIdString } -export type BaseSystemUpdateFields = never +export type BaseSystemUpdateFields = unknown export type AuthSystemFields = { email: string From 679829114ce112deb0ee5cd6d0c14972bee8b62c Mon Sep 17 00:00:00 2001 From: Ivan Date: Fri, 27 Dec 2024 01:19:53 +0100 Subject: [PATCH 6/6] Improve type generation for autodate fields --- dist/index.js | 5 ++--- src/constants.ts | 1 - src/lib.ts | 7 +++---- test/__snapshots__/fromJSON.test.ts.snap | 20 ++++++++++++++++++++ test/pocketbase-types-example.ts | 20 ++++++++++++++++++++ 5 files changed, 45 insertions(+), 8 deletions(-) diff --git a/dist/index.js b/dist/index.js index 6f685fb..704c589 100755 --- a/dist/index.js +++ b/dist/index.js @@ -81,7 +81,6 @@ export type ${RECORD_ID_STRING_NAME} = string export type ${HTML_STRING_NAME} = string export type Nullable = T | null | ''`; var NOT_COMMON_COLLECTIONS = ["_authOrigins", "_externalAuths", "_mfas", "_otps"]; -var EXTRA_SYSTEM_FIELDS = ["created", "updated"]; var BASE_SYSTEM_FIELDS_DEFINITION = `// System fields export type BaseSystemFields = { id: ${RECORD_ID_STRING_NAME} @@ -381,7 +380,7 @@ function createCreateType(collectionSchemaEntry) { includeExpand: false }); const systemFields = getSystemCreateFields(type); - const collectionFields = fields.filter((fieldSchema) => !fieldSchema.system && !EXTRA_SYSTEM_FIELDS.includes(fieldSchema.name)).map((fieldSchema) => createTypeCreateField(name, fieldSchema)).sort().join("\n"); + const collectionFields = fields.filter((fieldSchema) => !fieldSchema.system).map((fieldSchema) => createTypeCreateField(name, fieldSchema)).sort().join("\n"); return `export type ${typeName}Create${genericArgs} = ${collectionFields ? `{ ${collectionFields} } & ${systemFields}` : systemFields}`; @@ -393,7 +392,7 @@ function createUpdateType(collectionSchemaEntry) { includeExpand: false }); const systemFields = getSystemUpdateFields(type); - const collectionFields = fields.filter((fieldSchema) => !fieldSchema.system && !EXTRA_SYSTEM_FIELDS.includes(fieldSchema.name)).map((fieldSchema) => createTypeUpdateField(name, fieldSchema)).sort().join("\n"); + const collectionFields = fields.filter((fieldSchema) => !fieldSchema.system).map((fieldSchema) => createTypeUpdateField(name, fieldSchema)).sort().join("\n"); return `export type ${typeName}Update${genericArgs} = ${collectionFields ? `{ ${collectionFields} } & ${systemFields}` : systemFields}`; diff --git a/src/constants.ts b/src/constants.ts index 143916f..d55077d 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -19,7 +19,6 @@ export type ${RECORD_ID_STRING_NAME} = string export type ${HTML_STRING_NAME} = string export type Nullable = T | null | ''` export const NOT_COMMON_COLLECTIONS = ['_authOrigins', '_externalAuths', '_mfas', '_otps'] -export const EXTRA_SYSTEM_FIELDS = ['created', 'updated'] export const BASE_SYSTEM_FIELDS_DEFINITION = `// System fields export type BaseSystemFields = { diff --git a/src/lib.ts b/src/lib.ts index fb02956..dc1e220 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -18,13 +18,12 @@ import { CREATE_TYPE_COMMENT, EXPAND_GENERIC_NAME, EXPORT_COMMENT, - EXTRA_SYSTEM_FIELDS, IMPORTS, NOT_COMMON_COLLECTIONS, RECORD_TYPE_COMMENT, RESPONSE_TYPE_COMMENT, TYPED_POCKETBASE_COMMENT, - UPDATE_TYPE_COMMENT, + UPDATE_TYPE_COMMENT } from "./constants" import { createSelectOptions, createTypeCreateField, createTypeField, createTypeUpdateField } from "./fields" import { @@ -126,7 +125,7 @@ export function createCreateType( }) const systemFields = getSystemCreateFields(type) const collectionFields = fields - .filter((fieldSchema: FieldSchema) => !fieldSchema.system && !EXTRA_SYSTEM_FIELDS.includes(fieldSchema.name)) + .filter((fieldSchema: FieldSchema) => !fieldSchema.system) .map((fieldSchema: FieldSchema) => createTypeCreateField(name, fieldSchema)) .sort() .join("\n") @@ -150,7 +149,7 @@ export function createUpdateType( }) const systemFields = getSystemUpdateFields(type) const collectionFields = fields - .filter((fieldSchema: FieldSchema) => !fieldSchema.system && !EXTRA_SYSTEM_FIELDS.includes(fieldSchema.name)) + .filter((fieldSchema: FieldSchema) => !fieldSchema.system) .map((fieldSchema: FieldSchema) => createTypeUpdateField(name, fieldSchema)) .sort() .join("\n") diff --git a/test/__snapshots__/fromJSON.test.ts.snap b/test/__snapshots__/fromJSON.test.ts.snap index 5c2e15c..c2138ce 100644 --- a/test/__snapshots__/fromJSON.test.ts.snap +++ b/test/__snapshots__/fromJSON.test.ts.snap @@ -201,16 +201,21 @@ export type UsersRecord = { export type SuperusersCreate = AuthSystemCreateFields export type BaseCreate = { + created?: IsoDateString field?: string + updated?: IsoDateString } & BaseSystemCreateFields export type CustomAuthCreate = { + created?: IsoDateString custom_field?: string + updated?: IsoDateString } & AuthSystemCreateFields export type EverythingCreate = { another_json_field?: null | Tanother_json_field bool_field?: boolean + created?: IsoDateString custom_relation_field?: RecordIdString[] date_field?: IsoDateString email_field?: string @@ -223,6 +228,7 @@ export type EverythingCreate = { } & BaseSystemCreateFields export type PostsCreate = { + created?: IsoDateString field1?: number nonempty_bool: boolean nonempty_field: string + updated?: IsoDateString } & BaseSystemCreateFields export type UsersCreate = { avatar?: File + created?: IsoDateString name?: string + updated?: IsoDateString } & AuthSystemCreateFields // Update types for each collection @@ -249,16 +259,21 @@ export type UsersCreate = { export type SuperusersUpdate = AuthSystemUpdateFields export type BaseUpdate = { + created?: IsoDateString field?: string + updated?: IsoDateString } & BaseSystemUpdateFields export type CustomAuthUpdate = { + created?: IsoDateString custom_field?: string + updated?: IsoDateString } & AuthSystemUpdateFields export type EverythingUpdate = { another_json_field?: null | Tanother_json_field bool_field?: boolean + created?: IsoDateString custom_relation_field?: RecordIdString[] date_field?: IsoDateString email_field?: string @@ -271,6 +286,7 @@ export type EverythingUpdate + updated?: IsoDateString url_field?: string user_relation_field?: RecordIdString } & BaseSystemUpdateFields @@ -282,14 +298,18 @@ export type MyViewUpdate = { } & BaseSystemUpdateFields export type PostsUpdate = { + created?: IsoDateString field1?: number nonempty_bool?: boolean nonempty_field?: string + updated?: IsoDateString } & BaseSystemUpdateFields export type UsersUpdate = { avatar?: Nullable + created?: IsoDateString name?: string + updated?: IsoDateString } & AuthSystemUpdateFields // Response types include system fields and match responses from the PocketBase API diff --git a/test/pocketbase-types-example.ts b/test/pocketbase-types-example.ts index d1c58f0..d6d54fc 100644 --- a/test/pocketbase-types-example.ts +++ b/test/pocketbase-types-example.ts @@ -198,16 +198,21 @@ export type UsersRecord = { export type SuperusersCreate = AuthSystemCreateFields export type BaseCreate = { + created?: IsoDateString field?: string + updated?: IsoDateString } & BaseSystemCreateFields export type CustomAuthCreate = { + created?: IsoDateString custom_field?: string + updated?: IsoDateString } & AuthSystemCreateFields export type EverythingCreate = { another_json_field?: null | Tanother_json_field bool_field?: boolean + created?: IsoDateString custom_relation_field?: RecordIdString[] date_field?: IsoDateString email_field?: string @@ -220,6 +225,7 @@ export type EverythingCreate = { } & BaseSystemCreateFields export type PostsCreate = { + created?: IsoDateString field1?: number nonempty_bool: boolean nonempty_field: string + updated?: IsoDateString } & BaseSystemCreateFields export type UsersCreate = { avatar?: File + created?: IsoDateString name?: string + updated?: IsoDateString } & AuthSystemCreateFields // Update types for each collection @@ -246,16 +256,21 @@ export type UsersCreate = { export type SuperusersUpdate = AuthSystemUpdateFields export type BaseUpdate = { + created?: IsoDateString field?: string + updated?: IsoDateString } & BaseSystemUpdateFields export type CustomAuthUpdate = { + created?: IsoDateString custom_field?: string + updated?: IsoDateString } & AuthSystemUpdateFields export type EverythingUpdate = { another_json_field?: null | Tanother_json_field bool_field?: boolean + created?: IsoDateString custom_relation_field?: RecordIdString[] date_field?: IsoDateString email_field?: string @@ -268,6 +283,7 @@ export type EverythingUpdate + updated?: IsoDateString url_field?: string user_relation_field?: RecordIdString } & BaseSystemUpdateFields @@ -279,14 +295,18 @@ export type MyViewUpdate = { } & BaseSystemUpdateFields export type PostsUpdate = { + created?: IsoDateString field1?: number nonempty_bool?: boolean nonempty_field?: string + updated?: IsoDateString } & BaseSystemUpdateFields export type UsersUpdate = { avatar?: Nullable + created?: IsoDateString name?: string + updated?: IsoDateString } & AuthSystemUpdateFields // Response types include system fields and match responses from the PocketBase API