feat: adds field types to type generation

This commit is contained in:
James
2021-11-16 20:24:13 -05:00
parent 5a965d2263
commit 6dd1b0e033
9 changed files with 683 additions and 37 deletions

3
.gitignore vendored
View File

@@ -228,3 +228,6 @@ build
# Ignore built components
components/index.js
components/styles.css
# Ignore generated types
./payload-types.ts

15
.vscode/launch.json vendored
View File

@@ -65,6 +65,19 @@
"name": "Launch Chrome against Localhost",
"url": "http://localhost:3000/admin",
"webRoot": "${workspaceFolder}"
}
},
{
"type": "node",
"request": "launch",
"name": "Debug Payload Generate Types",
"program": "${workspaceFolder}/src/bin/generateTypes.ts",
"env": {
"PAYLOAD_CONFIG_PATH": "demo/payload.config.ts",
},
"outFiles": [
"${workspaceFolder}/dist/**/*.js",
"!**/node_modules/**"
]
},
]
}

View File

@@ -205,6 +205,7 @@
"@types/isomorphic-fetch": "^0.0.35",
"@types/jest": "^26.0.15",
"@types/joi": "^14.3.4",
"@types/json-schema": "^7.0.9",
"@types/jsonwebtoken": "^8.5.0",
"@types/method-override": "^0.0.31",
"@types/mini-css-extract-plugin": "^1.2.1",

541
payload-types.ts Normal file
View File

@@ -0,0 +1,541 @@
// auto-generated by payload
export interface Config {}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "navigation-array".
*/
export interface NavigationArray {
array?: {
text?: string;
id?: string;
}[];
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "global-with-access".
*/
export interface GlobalWithStrictAccess {
title: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "blocks-global".
*/
export interface BlocksGlobal {
blocks?: (
| {
color: string;
id?: string;
blockName?: string;
blockType: 'quote';
}
| {
label: string;
url: string;
id?: string;
blockName?: string;
blockType: 'cta';
}
)[];
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "admins".
*/
export interface Admin {
email?: string;
resetPasswordToken?: string;
apiKey?: string;
apiKeyIndex?: string;
loginAttempts?: number;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "all-fields".
*/
export interface AllFields {
text: string;
descriptionText?: string;
descriptionFunction?: string;
email?: string;
number?: number;
group?: {
nestedText1?: string;
nestedText2?: string;
};
array?: {
arrayText1: string;
arrayText2: string;
arrayText3?: string;
id?: string;
}[];
blocks: (
| {
testEmail: string;
id?: string;
blockName?: string;
blockType: 'email';
}
| {
testNumber: number;
id?: string;
blockName?: string;
blockType: 'number';
}
| {
color: string;
id?: string;
blockName?: string;
blockType: 'quote';
}
| {
label: string;
url: string;
id?: string;
blockName?: string;
blockType: 'cta';
}
)[];
slug: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "auto-label".
*/
export interface AutoLabel {
autoLabelField?: string;
noLabel?: string;
labelOverride?: string;
specialBlock?: {
testNumber?: number;
id?: string;
blockName?: string;
blockType: 'number';
}[];
noLabelBlock?: {
testNumber?: number;
id?: string;
blockName?: string;
blockType: 'number';
}[];
items?: {
itemName?: string;
id?: string;
}[];
noLabelArray?: {
textField?: string;
id?: string;
}[];
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "code".
*/
export interface Code {}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "conditions".
*/
export interface Conditions {
title: string;
number?: number;
simpleCondition: string;
orCondition: string;
nestedConditions?: string;
blocks: (
| {
testEmail: string;
id?: string;
blockName?: string;
blockType: 'email';
}
| {
testNumber: number;
id?: string;
blockName?: string;
blockType: 'number';
}
| {
color: string;
id?: string;
blockName?: string;
blockType: 'quote';
}
| {
label: string;
url: string;
id?: string;
blockName?: string;
blockType: 'cta';
}
)[];
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "custom-components".
*/
export interface CustomComponent {
title: string;
componentDescription?: string;
array?: {
nestedArrayCustomField?: string;
id?: string;
}[];
group?: {
nestedGroupCustomField?: string;
};
nestedText1?: string;
nestedText2?: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "custom-id".
*/
export interface CustomID {
id?: number;
name: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "files".
*/
export interface File {
url?: string;
filename?: string;
mimeType?: string;
filesize?: number;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "default-values".
*/
export interface DefaultValueTest {
text?: string;
email?: string;
number?: number;
group?: {
nestedText1?: string;
nestedText2?: string;
};
array?: {
arrayText1?: string;
arrayText2?: string;
arrayText3?: string;
id?: string;
}[];
blocks?: (
| {
testEmail: string;
id?: string;
blockName?: string;
blockType: 'email';
}
| {
testNumber: number;
id?: string;
blockName?: string;
blockType: 'number';
}
| {
color: string;
id?: string;
blockName?: string;
blockType: 'quote';
}
| {
label: string;
url: string;
id?: string;
blockName?: string;
blockType: 'cta';
}
)[];
slug?: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "blocks".
*/
export interface Blocks {
layout: (
| {
testEmail: string;
id?: string;
blockName?: string;
blockType: 'email';
}
| {
testNumber: number;
id?: string;
blockName?: string;
blockType: 'number';
}
| {
color: string;
id?: string;
blockName?: string;
blockType: 'quote';
}
| {
label: string;
url: string;
id?: string;
blockName?: string;
blockType: 'cta';
}
)[];
nonLocalizedLayout: (
| {
testEmail: string;
id?: string;
blockName?: string;
blockType: 'email';
}
| {
testNumber: number;
id?: string;
blockName?: string;
blockType: 'number';
}
| {
color: string;
id?: string;
blockName?: string;
blockType: 'quote';
}
| {
label: string;
url: string;
id?: string;
blockName?: string;
blockType: 'cta';
}
)[];
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "hidden-fields".
*/
export interface HiddenFields {
title: string;
hiddenAdmin: string;
hiddenAPI: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "hooks".
*/
export interface Hook {
title: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "localized-posts".
*/
export interface LocalizedPost {
title: string;
summary?: string;
priority: number;
localizedGroup?: {
text?: string;
};
nonLocalizedGroup?: {
text?: string;
};
nonLocalizedArray?: {
localizedEmbeddedText?: string;
id?: string;
}[];
richTextBlocks?: {
id?: string;
blockName?: string;
blockType: 'richTextBlock';
}[];
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "localized-arrays".
*/
export interface LocalizedArray {
array: {
arrayText1: string;
arrayText2: string;
arrayText3?: string;
id?: string;
}[];
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "local-operations".
*/
export interface LocalOperation {
title: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "media".
*/
export interface Media {
url?: string;
filename?: string;
mimeType?: string;
filesize?: number;
width?: number;
height?: number;
sizes?: {
maintainedAspectRatio?: {
url?: string;
width?: number;
height?: number;
mimeType?: string;
filesize?: number;
filename?: string;
};
tablet?: {
url?: string;
width?: number;
height?: number;
mimeType?: string;
filesize?: number;
filename?: string;
};
mobile?: {
url?: string;
width?: number;
height?: number;
mimeType?: string;
filesize?: number;
filename?: string;
};
icon?: {
url?: string;
width?: number;
height?: number;
mimeType?: string;
filesize?: number;
filename?: string;
};
};
alt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "nested-arrays".
*/
export interface NestedArray {
array: {
parentIdentifier: string;
nestedArray: {
childIdentifier: string;
deeplyNestedArray: {
grandchildIdentifier?: string;
id?: string;
}[];
id?: string;
}[];
id?: string;
}[];
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "previewable-post".
*/
export interface PreviewablePost {
title: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "public-users".
*/
export interface PublicUser {
email?: string;
resetPasswordToken?: string;
_verificationToken?: string;
loginAttempts?: number;
adminOnly?: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "relationship-a".
*/
export interface RelationshipA {}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "relationship-b".
*/
export interface RelationshipB {
title?: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "rich-text".
*/
export interface RichText {}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "select".
*/
export interface Select {}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "strict-access".
*/
export interface StrictAccess {
address: string;
city: string;
state: string;
zip: number;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "validations".
*/
export interface Validation {
text: string;
lessThan10: number;
greaterThan10LessThan50: number;
atLeast3Rows: {
greaterThan30: number;
id?: string;
}[];
array: {
lessThan20: number;
id?: string;
}[];
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "uniques".
*/
export interface Unique {
title: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "unstored-media".
*/
export interface UnstoredMedia {
url?: string;
filename?: string;
mimeType?: string;
filesize?: number;
width?: number;
height?: number;
sizes?: {
tablet?: {
url?: string;
width?: number;
height?: number;
mimeType?: string;
filesize?: number;
filename?: string;
};
};
alt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "geolocation".
*/
export interface Geolocation {}

View File

@@ -1,25 +1,91 @@
/* eslint-disable no-nested-ternary */
import fs from 'fs';
import type { JSONSchema4 } from 'json-schema';
import { compile } from 'json-schema-to-typescript';
import { fieldIsPresentationalOnly, fieldAffectsData } from '../fields/config/types';
import { fieldIsPresentationalOnly, fieldAffectsData, Field } from '../fields/config/types';
import { SanitizedCollectionConfig } from '../collections/config/types';
import { SanitizedConfig } from '../config/types';
import loadConfig from '../config/load';
import { SanitizedGlobalConfig } from '../globals/config/types';
function generateFieldTypes(fields: Field[]): {
properties: {
[k: string]: JSONSchema4;
}
required: string[]
} {
let topLevelProps = [];
let requiredTopLevelProps = [];
function collectionToJsonSchema(collection: SanitizedCollectionConfig): any {
return {
title: collection.labels.singular,
type: 'object',
properties: Object.fromEntries(
collection.fields.reduce((properties, field) => {
let type;
fields.reduce((properties, field) => {
let fieldSchema: JSONSchema4;
switch (field.type) {
case 'number': {
type = { type: 'integer ' };
case 'text':
case 'email': {
fieldSchema = { type: 'string' };
break;
}
case 'text': {
type = { type: 'string' };
case 'number': {
fieldSchema = { type: 'number' };
break;
}
case 'blocks': {
fieldSchema = {
type: 'array',
items: {
oneOf: field.blocks.map((block) => {
const blockSchema = generateFieldTypes(block.fields);
return {
type: 'object',
additionalProperties: false,
properties: {
...blockSchema.properties,
blockType: {
const: block.slug,
},
},
required: [
'blockType',
...blockSchema.required,
],
};
}),
},
};
break;
}
case 'array': {
fieldSchema = {
type: 'array',
items: {
type: 'object',
additionalProperties: false,
...generateFieldTypes(field.fields),
},
};
break;
}
case 'row': {
const topLevelFields = generateFieldTypes(field.fields);
requiredTopLevelProps = requiredTopLevelProps.concat(topLevelFields.required);
topLevelProps = topLevelProps.concat(Object.entries(topLevelFields.properties).map((prop) => prop));
break;
}
case 'group': {
fieldSchema = {
type: 'object',
additionalProperties: false,
...generateFieldTypes(field.fields),
};
break;
}
@@ -30,40 +96,62 @@ function collectionToJsonSchema(collection: SanitizedCollectionConfig): any {
let default_ = {};
if (!fieldIsPresentationalOnly(field)) {
if (!fieldIsPresentationalOnly(field) && fieldAffectsData(field) && typeof field.defaultValue !== 'undefined') {
default_ = { default: field.defaultValue };
}
if (type && fieldAffectsData(field)) {
if (fieldSchema && fieldAffectsData(field)) {
return [
...properties,
[
field.name,
{
...type,
...fieldSchema,
...default_,
},
],
];
}
return properties;
return [
...properties,
...topLevelProps,
];
}, []),
),
required: collection.fields
.filter((field) => fieldAffectsData(field) && field.required === true)
.map((field) => fieldAffectsData(field) && field.name),
additionalProperties: false,
required: [
...fields
.filter((field) => fieldAffectsData(field) && field.required === true)
.map((field) => (fieldAffectsData(field) ? field.name : '')),
...requiredTopLevelProps,
],
};
}
function configToJsonSchema(collections: SanitizedCollectionConfig[]): any {
function entityToJsonSchema(entity: SanitizedCollectionConfig | SanitizedGlobalConfig): any {
const title = 'label' in entity ? entity.label : entity.labels.singular;
return {
title,
type: 'object',
additionalProperties: false,
...generateFieldTypes(entity.fields),
};
}
function configToJsonSchema(config: SanitizedConfig): JSONSchema4 {
return {
definitions: Object.fromEntries(
collections.map((collection) => [
collection.slug,
collectionToJsonSchema(collection),
]),
[
...config.globals.map((global) => [
global.slug,
entityToJsonSchema(global),
]),
...config.collections.map((collection) => [
collection.slug,
entityToJsonSchema(collection),
]),
],
),
additionalProperties: false,
};
@@ -73,26 +161,17 @@ export function generateTypes(): void {
const config = loadConfig();
console.log('compiling ts types');
const jsonSchema = configToJsonSchema(config.collections);
const jsonSchema = configToJsonSchema(config);
compile(jsonSchema, 'Config', {
bannerComment: '// auto-generated by payload',
unreachableDefinitions: true,
}).then((compiled) => {
// fse.writeFileSync('generated-types.ts', compiled);
console.log('compiled', compiled);
fs.writeFileSync(config.typescript.outputFile, compiled);
});
}
// when build.js is launched directly
// when generateTypes.js is launched directly
if (module.id === require.main.id) {
generateTypes();
}
// const result = await compile(jsonSchema, 'Config', {
// bannerComment: '// auto-generated by payload',
// unreachableDefinitions: true,
// });
// await fse.writeFile('generated-types.ts', result);

View File

@@ -19,6 +19,9 @@ export const defaults = {
scss: path.resolve(__dirname, '../admin/scss/overrides.scss'),
dateFormat: 'MMMM do yyyy, h:mm a',
},
typescript: {
outputFile: `${typeof process?.cwd === 'function' ? process.cwd() : ''}/payload-types.ts`,
},
upload: {},
graphQL: {
maxComplexity: 1000,

View File

@@ -29,6 +29,9 @@ export default joi.object({
graphQL: joi.string(),
graphQLPlayground: joi.string(),
}),
typescript: joi.object({
outputFile: joi.string(),
}),
collections: joi.array(),
globals: joi.array(),
admin: joi.object({

View File

@@ -112,6 +112,9 @@ export type Config = {
graphQL?: string;
graphQLPlayground?: string;
};
typescript?: {
outputFile?: string
}
debug?: boolean
express?: {
json?: {

View File

@@ -1989,7 +1989,7 @@
resolved "https://registry.npmjs.org/@types/joi/-/joi-14.3.4.tgz#eed1e14cbb07716079c814138831a520a725a1e0"
integrity sha512-1TQNDJvIKlgYXGNIABfgFp9y0FziDpuGrd799Q5RcnsDu+krD+eeW/0Fs5PHARvWWFelOhIG2OPCo6KbadBM4A==
"@types/json-schema@*", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8":
"@types/json-schema@*", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
version "7.0.9"
resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==