From 7c6424ff354b09b28d3967c6fe15742e1b0c4944 Mon Sep 17 00:00:00 2001 From: Elliot DeNolf Date: Tue, 9 Nov 2021 20:00:21 -0500 Subject: [PATCH 01/16] chore: resolve conflicts --- package.json | 2 + src/config/build.ts | 14 ++++++- src/config/generateTypes.ts | 79 ++++++++++++++++++++++++++++++++++++ src/webpack/getBaseConfig.ts | 3 ++ 4 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 src/config/generateTypes.ts diff --git a/package.json b/package.json index 245c163279..85cdf193f9 100644 --- a/package.json +++ b/package.json @@ -107,6 +107,7 @@ "file-loader": "^6.2.0", "find-up": "5.0.0", "flatley": "^5.2.0", + "fs-extra": "^10.0.0", "graphql": "15.4.0", "graphql-playground-middleware-express": "^1.7.14", "graphql-query-complexity": "^0.7.0", @@ -118,6 +119,7 @@ "isomorphic-fetch": "^3.0.0", "jest": "^26.6.3", "joi": "^17.3.0", + "json-schema-to-typescript": "^10.1.5", "jsonwebtoken": "^8.5.1", "method-override": "^3.0.0", "micro-memoize": "^4.0.9", diff --git a/src/config/build.ts b/src/config/build.ts index d7d9a82f4c..2ac2c88ec6 100644 --- a/src/config/build.ts +++ b/src/config/build.ts @@ -1,5 +1,8 @@ +/* eslint-disable no-use-before-define */ +/* eslint-disable no-nested-ternary */ import { Config, SanitizedConfig } from './types'; import sanitize from './sanitize'; +import { generateTypes } from './generateTypes'; /** * @description Builds and validates Payload configuration @@ -8,8 +11,15 @@ import sanitize from './sanitize'; */ export function buildConfig(config: Config): SanitizedConfig { if (Array.isArray(config.plugins)) { - const configWithPlugins = config.plugins.reduce((updatedConfig, plugin) => plugin(updatedConfig), config); - return sanitize(configWithPlugins); + const configWithPlugins = config.plugins.reduce( + (updatedConfig, plugin) => plugin(updatedConfig), + config, + ); + const sanitizedConfig = sanitize(configWithPlugins); + if (typeof generateTypes === 'function') { + generateTypes(sanitizedConfig); + } + return sanitizedConfig; } return sanitize(config); diff --git a/src/config/generateTypes.ts b/src/config/generateTypes.ts new file mode 100644 index 0000000000..a35f87aaa3 --- /dev/null +++ b/src/config/generateTypes.ts @@ -0,0 +1,79 @@ +/* eslint-disable no-nested-ternary */ +import { compile } from 'json-schema-to-typescript'; +import { SanitizedConfig } from './types'; +import { SanitizedCollectionConfig } from '../collections/config/types'; + +/* eslint-disable no-use-before-define */ +export function generateTypes(sanitizedConfig: SanitizedConfig) { + console.log('compiling ts types'); + const jsonSchema = configToJsonSchema(sanitizedConfig.collections); + + compile(jsonSchema, 'Config', { + bannerComment: '// auto-generated by payload', + unreachableDefinitions: true, + }).then((compiled) => { + // fse.writeFileSync('generated-types.ts', compiled); + console.log('compiled', compiled); + }); +} + +function collectionToJsonSchema( + collection: SanitizedCollectionConfig, + slugToLabel: Record, +): any { + return { + title: collection.labels.singular, + type: 'object', + properties: Object.fromEntries( + collection.fields.map((field) => { + const type = field.type === 'number' + ? { type: 'integer' } + : field.type === 'relationship' + ? { + $ref: `#/definitions/${slugToLabel[field.relationTo as string]}`, + } + : { type: 'string' }; + const enum_ = field.type === 'select' ? { enum: field.options } : {}; + const default_ = field.defaultValue ? { default: field.defaultValue } : {}; + + return [ + field.name, + { + ...type, + ...enum_, + ...default_, + }, + ]; + }), + ), + required: collection.fields + .filter((field) => field.required === true) + .map((field) => field.name), + additionalProperties: false, + }; +} + +function configToJsonSchema(collections: SanitizedCollectionConfig[]): any { + const slugToLabel = Object.fromEntries( + collections.map((collection) => [ + collection.slug, + collection.labels.singular, + ]), + ); + return { + definitions: Object.fromEntries( + collections.map((collection) => [ + collection.labels.singular, + collectionToJsonSchema(collection, slugToLabel), + ]), + ), + additionalProperties: false, + }; +} + +// const result = await compile(jsonSchema, 'Config', { +// bannerComment: '// auto-generated by payload', +// unreachableDefinitions: true, +// }); + +// await fse.writeFile('generated-types.ts', result); diff --git a/src/webpack/getBaseConfig.ts b/src/webpack/getBaseConfig.ts index def6a2fa97..29a4da4b21 100644 --- a/src/webpack/getBaseConfig.ts +++ b/src/webpack/getBaseConfig.ts @@ -56,6 +56,9 @@ export default (config: SanitizedConfig): Configuration => ({ 'payload-user-css': config.admin.css, 'payload-scss-overrides': config.admin.scss, dotenv: mockDotENVPath, + _: [ + `./${path.resolve(__dirname, '../config/generateTypes')}`, + ], }, extensions: ['.ts', '.tsx', '.js', '.json'], }, From 291c193ad4a9ec8ce9310cc63c714eba10eca102 Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Wed, 10 Nov 2021 11:12:52 -0500 Subject: [PATCH 02/16] fix: updates field description type to include react nodes --- .../forms/FieldDescription/types.ts | 4 +-- .../forms/field-types/Array/types.ts | 3 +- .../forms/field-types/Blocks/types.ts | 3 +- .../forms/field-types/Password/types.ts | 3 +- src/fields/config/types.ts | 35 +++++++++---------- 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/admin/components/forms/FieldDescription/types.ts b/src/admin/components/forms/FieldDescription/types.ts index 98e8c0bdd7..ee90d42ee0 100644 --- a/src/admin/components/forms/FieldDescription/types.ts +++ b/src/admin/components/forms/FieldDescription/types.ts @@ -2,9 +2,9 @@ import React from 'react'; export type DescriptionFunction = (value: unknown) => string -export type DescriptionComponent = React.ComponentType<{value: unknown}> +export type DescriptionComponent = React.ComponentType<{ value: unknown }> -type Description = string | DescriptionFunction | DescriptionComponent +export type Description = string | DescriptionFunction | DescriptionComponent export type Props = { description?: Description diff --git a/src/admin/components/forms/field-types/Array/types.ts b/src/admin/components/forms/field-types/Array/types.ts index 067f45c811..007dbbbf4c 100644 --- a/src/admin/components/forms/field-types/Array/types.ts +++ b/src/admin/components/forms/field-types/Array/types.ts @@ -1,7 +1,8 @@ import { Data } from '../../Form/types'; -import { ArrayField, Labels, Field, Description } from '../../../../../fields/config/types'; +import { ArrayField, Labels, Field } from '../../../../../fields/config/types'; import { FieldTypes } from '..'; import { FieldPermissions } from '../../../../../auth/types'; +import { Description } from '../../FieldDescription/types'; export type Props = Omit & { path?: string diff --git a/src/admin/components/forms/field-types/Blocks/types.ts b/src/admin/components/forms/field-types/Blocks/types.ts index e12683df3a..ee687dee7b 100644 --- a/src/admin/components/forms/field-types/Blocks/types.ts +++ b/src/admin/components/forms/field-types/Blocks/types.ts @@ -1,7 +1,8 @@ import { Data } from '../../Form/types'; -import { BlockField, Labels, Block, Description } from '../../../../../fields/config/types'; +import { BlockField, Labels, Block } from '../../../../../fields/config/types'; import { FieldTypes } from '..'; import { FieldPermissions } from '../../../../../auth/types'; +import { Description } from '../../FieldDescription/types'; export type Props = Omit & { path?: string diff --git a/src/admin/components/forms/field-types/Password/types.ts b/src/admin/components/forms/field-types/Password/types.ts index 214b322c52..3814c63928 100644 --- a/src/admin/components/forms/field-types/Password/types.ts +++ b/src/admin/components/forms/field-types/Password/types.ts @@ -1,5 +1,6 @@ import React from 'react'; -import { Description, Validate } from '../../../../../fields/config/types'; +import { Validate } from '../../../../../fields/config/types'; +import { Description } from '../../FieldDescription/types'; export type Props = { autoComplete?: string diff --git a/src/fields/config/types.ts b/src/fields/config/types.ts index 91c4b4218d..801d69793f 100644 --- a/src/fields/config/types.ts +++ b/src/fields/config/types.ts @@ -4,6 +4,7 @@ import { Editor } from 'slate'; import { PayloadRequest } from '../../express/types'; import { Document } from '../../types'; import { ConditionalDateProps } from '../../admin/components/elements/DatePicker/types'; +import { Description } from '../../admin/components/forms/FieldDescription/types'; export type FieldHook = (args: { value?: unknown, @@ -40,8 +41,6 @@ type Admin = { hidden?: boolean } -export type Description = string | ((value: Record) => string); - export type Labels = { singular: string; plural: string; @@ -302,22 +301,22 @@ export type FieldAffectingData = | PointField export type NonPresentationalField = TextField -| NumberField -| EmailField -| TextareaField -| CheckboxField -| DateField -| BlockField -| GroupField -| RadioField -| RelationshipField -| ArrayField -| RichTextField -| SelectField -| UploadField -| CodeField -| PointField -| RowField; + | NumberField + | EmailField + | TextareaField + | CheckboxField + | DateField + | BlockField + | GroupField + | RadioField + | RelationshipField + | ArrayField + | RichTextField + | SelectField + | UploadField + | CodeField + | PointField + | RowField; export type FieldWithPath = Field & { path?: string From 5a965d2263d1b246149c5220d039a6d97953d5e8 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 16 Nov 2021 18:49:42 -0500 Subject: [PATCH 03/16] feat: baseline type generation --- package.json | 1 + src/bin/generateTypes.ts | 98 ++++++++++++ src/bin/index.ts | 7 + src/config/build.ts | 6 +- src/config/generateTypes.ts | 79 ---------- src/webpack/getBaseConfig.ts | 3 - yarn.lock | 281 ++++++++++++++++++++++++++++++++++- 7 files changed, 386 insertions(+), 89 deletions(-) create mode 100644 src/bin/generateTypes.ts delete mode 100644 src/config/generateTypes.ts diff --git a/package.json b/package.json index 06ce4e63e7..9b06c60497 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "build:watch": "nodemon --watch 'src/**' --ext 'ts,tsx' --exec 'yarn build:tsc'", "demo:build:analyze": "cross-env PAYLOAD_CONFIG_PATH=demo/payload.config.ts PAYLOAD_ANALYZE_BUNDLE=true node dist/bin/build", "demo:build": "cross-env PAYLOAD_CONFIG_PATH=demo/payload.config.ts node dist/bin/build", + "demo:generate:types": "cross-env PAYLOAD_CONFIG_PATH=demo/payload.config.ts node dist/bin/generateTypes", "dev": "cross-env PAYLOAD_CONFIG_PATH=demo/payload.config.ts nodemon", "test": "yarn test:int && yarn test:client", "pretest": "yarn build", diff --git a/src/bin/generateTypes.ts b/src/bin/generateTypes.ts new file mode 100644 index 0000000000..9e265ec005 --- /dev/null +++ b/src/bin/generateTypes.ts @@ -0,0 +1,98 @@ +/* eslint-disable no-nested-ternary */ +import { compile } from 'json-schema-to-typescript'; +import { fieldIsPresentationalOnly, fieldAffectsData } from '../fields/config/types'; +import { SanitizedCollectionConfig } from '../collections/config/types'; +import loadConfig from '../config/load'; + +function collectionToJsonSchema(collection: SanitizedCollectionConfig): any { + return { + title: collection.labels.singular, + type: 'object', + properties: Object.fromEntries( + collection.fields.reduce((properties, field) => { + let type; + + switch (field.type) { + case 'number': { + type = { type: 'integer ' }; + break; + } + + case 'text': { + type = { type: 'string' }; + break; + } + + default: { + break; + } + } + + let default_ = {}; + + if (!fieldIsPresentationalOnly(field)) { + default_ = { default: field.defaultValue }; + } + + if (type && fieldAffectsData(field)) { + return [ + ...properties, + [ + field.name, + { + ...type, + ...default_, + }, + ], + ]; + } + + return properties; + }, []), + ), + required: collection.fields + .filter((field) => fieldAffectsData(field) && field.required === true) + .map((field) => fieldAffectsData(field) && field.name), + additionalProperties: false, + }; +} + +function configToJsonSchema(collections: SanitizedCollectionConfig[]): any { + return { + definitions: Object.fromEntries( + collections.map((collection) => [ + collection.slug, + collectionToJsonSchema(collection), + ]), + ), + additionalProperties: false, + }; +} + +export function generateTypes(): void { + const config = loadConfig(); + + console.log('compiling ts types'); + const jsonSchema = configToJsonSchema(config.collections); + + compile(jsonSchema, 'Config', { + bannerComment: '// auto-generated by payload', + unreachableDefinitions: true, + }).then((compiled) => { + // fse.writeFileSync('generated-types.ts', compiled); + console.log('compiled', compiled); + }); +} + +// when build.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); diff --git a/src/bin/index.ts b/src/bin/index.ts index 05e9d5e568..5d8040e28a 100755 --- a/src/bin/index.ts +++ b/src/bin/index.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-var-requires */ import minimist from 'minimist'; +import { generateTypes } from './generateTypes'; import babelConfig from '../babel.config'; require('@babel/register')({ @@ -23,6 +24,12 @@ switch (script) { break; } + case 'generate:types': { + generateTypes(); + break; + } + + default: console.log(`Unknown script "${script}".`); break; diff --git a/src/config/build.ts b/src/config/build.ts index 2ac2c88ec6..4327eac466 100644 --- a/src/config/build.ts +++ b/src/config/build.ts @@ -2,7 +2,6 @@ /* eslint-disable no-nested-ternary */ import { Config, SanitizedConfig } from './types'; import sanitize from './sanitize'; -import { generateTypes } from './generateTypes'; /** * @description Builds and validates Payload configuration @@ -15,10 +14,9 @@ export function buildConfig(config: Config): SanitizedConfig { (updatedConfig, plugin) => plugin(updatedConfig), config, ); + const sanitizedConfig = sanitize(configWithPlugins); - if (typeof generateTypes === 'function') { - generateTypes(sanitizedConfig); - } + return sanitizedConfig; } diff --git a/src/config/generateTypes.ts b/src/config/generateTypes.ts deleted file mode 100644 index a35f87aaa3..0000000000 --- a/src/config/generateTypes.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* eslint-disable no-nested-ternary */ -import { compile } from 'json-schema-to-typescript'; -import { SanitizedConfig } from './types'; -import { SanitizedCollectionConfig } from '../collections/config/types'; - -/* eslint-disable no-use-before-define */ -export function generateTypes(sanitizedConfig: SanitizedConfig) { - console.log('compiling ts types'); - const jsonSchema = configToJsonSchema(sanitizedConfig.collections); - - compile(jsonSchema, 'Config', { - bannerComment: '// auto-generated by payload', - unreachableDefinitions: true, - }).then((compiled) => { - // fse.writeFileSync('generated-types.ts', compiled); - console.log('compiled', compiled); - }); -} - -function collectionToJsonSchema( - collection: SanitizedCollectionConfig, - slugToLabel: Record, -): any { - return { - title: collection.labels.singular, - type: 'object', - properties: Object.fromEntries( - collection.fields.map((field) => { - const type = field.type === 'number' - ? { type: 'integer' } - : field.type === 'relationship' - ? { - $ref: `#/definitions/${slugToLabel[field.relationTo as string]}`, - } - : { type: 'string' }; - const enum_ = field.type === 'select' ? { enum: field.options } : {}; - const default_ = field.defaultValue ? { default: field.defaultValue } : {}; - - return [ - field.name, - { - ...type, - ...enum_, - ...default_, - }, - ]; - }), - ), - required: collection.fields - .filter((field) => field.required === true) - .map((field) => field.name), - additionalProperties: false, - }; -} - -function configToJsonSchema(collections: SanitizedCollectionConfig[]): any { - const slugToLabel = Object.fromEntries( - collections.map((collection) => [ - collection.slug, - collection.labels.singular, - ]), - ); - return { - definitions: Object.fromEntries( - collections.map((collection) => [ - collection.labels.singular, - collectionToJsonSchema(collection, slugToLabel), - ]), - ), - additionalProperties: false, - }; -} - -// const result = await compile(jsonSchema, 'Config', { -// bannerComment: '// auto-generated by payload', -// unreachableDefinitions: true, -// }); - -// await fse.writeFile('generated-types.ts', result); diff --git a/src/webpack/getBaseConfig.ts b/src/webpack/getBaseConfig.ts index 29a4da4b21..def6a2fa97 100644 --- a/src/webpack/getBaseConfig.ts +++ b/src/webpack/getBaseConfig.ts @@ -56,9 +56,6 @@ export default (config: SanitizedConfig): Configuration => ({ 'payload-user-css': config.admin.css, 'payload-scss-overrides': config.admin.scss, dotenv: mockDotENVPath, - _: [ - `./${path.resolve(__dirname, '../config/generateTypes')}`, - ], }, extensions: ['.ts', '.tsx', '.js', '.json'], }, diff --git a/yarn.lock b/yarn.lock index 8a290dc178..1b30a5184b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,16 @@ # yarn lockfile v1 +"@apidevtools/json-schema-ref-parser@9.0.9": + version "9.0.9" + resolved "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz#d720f9256e3609621280584f2b47ae165359268b" + integrity sha512-GBD2Le9w2+lVFoc4vswGI/TjkNIZSVp7+9xPf+X3uidBfWnAeUWmquteSyt0+VCrhNMWj/FTABISQrD3Z/YA+w== + dependencies: + "@jsdevtools/ono" "^7.1.3" + "@types/json-schema" "^7.0.6" + call-me-maybe "^1.0.1" + js-yaml "^4.1.0" + "@babel/cli@^7.12.8": version "7.15.7" resolved "https://registry.npmjs.org/@babel/cli/-/cli-7.15.7.tgz#62658abedb786d09c1f70229224b11a65440d7a1" @@ -1395,6 +1405,11 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@jsdevtools/ono@^7.1.3": + version "7.1.3" + resolved "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" + integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== + "@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3": version "2.1.8-no-fsevents.3" resolved "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b" @@ -1856,6 +1871,14 @@ dependencies: "@types/webpack" "^4" +"@types/glob@*": + version "7.2.0" + resolved "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" + integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + "@types/graceful-fs@^4.1.2": version "4.1.5" resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" @@ -1966,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.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": 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== @@ -1995,6 +2018,11 @@ resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.175.tgz#b78dfa959192b01fae0ad90e166478769b215f45" integrity sha512-XmdEOrKQ8a1Y/yxQFOMbC47G/V2VDO1GvMRnl4O75M4GW/abC5tnfzadQYkqEveqRM1dEJGFFegfPNA2vvx2iw== +"@types/lodash@^4.14.168": + version "4.14.177" + resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.177.tgz#f70c0d19c30fab101cad46b52be60363c43c4578" + integrity sha512-0fDwydE2clKe9MNfvXHBHF9WEahRuj+msTuQqOmAApNORFvhMYZKNGGJdCzuhheVjMps/ti0Ak/iJPACMaevvw== + "@types/method-override@^0.0.31": version "0.0.31" resolved "https://registry.npmjs.org/@types/method-override/-/method-override-0.0.31.tgz#ec12b738c1b2a74a5d3d8af73c7d4952ad9c14d2" @@ -2021,6 +2049,11 @@ tapable "^2.2.0" webpack "^5" +"@types/minimatch@*": + version "3.0.5" + resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" + integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== + "@types/minimist@^1.2.0", "@types/minimist@^1.2.1": version "1.2.2" resolved "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" @@ -2181,6 +2214,11 @@ resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.1.tgz#e1303048d5389563e130f5bdd89d37a99acb75eb" integrity sha512-Fo79ojj3vdEZOHg3wR9ksAMRz4P3S5fDB5e/YWZiFnyFQI1WY2Vftu9XoXVVtJfxB7Bpce/QTqWSSntkz2Znrw== +"@types/prettier@^2.1.5": + version "2.4.2" + resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.2.tgz#4c62fae93eb479660c3bd93f9d24d561597a8281" + integrity sha512-ekoj4qOQYp7CvjX8ZDBgN86w3MqQhLE1hczEJbEIjgFEumDy+na/4AJAbLXfgEWFNB2pKadM5rPFtuSGMWK7xA== + "@types/prismjs@^1.16.2": version "1.16.6" resolved "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.16.6.tgz#377054f72f671b36dbe78c517ce2b279d83ecc40" @@ -2933,6 +2971,11 @@ ansi-styles@^5.0.0: resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + anymatch@*, anymatch@^3.0.0, anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" @@ -2969,6 +3012,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + args@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/args/-/args-5.0.1.tgz#4bf298df90a4799a09521362c579278cc2fdd761" @@ -3600,6 +3648,11 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" +call-me-maybe@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= + caller-callsite@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" @@ -3806,6 +3859,17 @@ cli-boxes@^2.2.1: resolved "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== +cli-color@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/cli-color/-/cli-color-2.0.1.tgz#93e3491308691f1e46beb78b63d0fb2585e42ba6" + integrity sha512-eBbxZF6fqPUNnf7CLAFOersUnyYzv83tHFLSlts+OAHsNendaqv2tHCq+/MO+b3Y+9JeoUlIvobyxG/Z8GNeOg== + dependencies: + d "^1.0.1" + es5-ext "^0.10.53" + es6-iterator "^2.0.3" + memoizee "^0.4.15" + timers-ext "^0.1.7" + cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -4630,6 +4694,14 @@ csstype@^3.0.2: resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.9.tgz#6410af31b26bd0520933d02cbc64fce9ce3fbf0b" integrity sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw== +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + damerau-levenshtein@^1.0.6: version "1.0.7" resolved "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.7.tgz#64368003512a1a6992593741a09a9d31a836f55d" @@ -5187,6 +5259,42 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: + version "0.10.53" + resolved "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-iterator@^2.0.3, es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +es6-weak-map@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" + integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== + dependencies: + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + es6-symbol "^3.1.1" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -5448,6 +5556,14 @@ etag@~1.8.1: resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk= + dependencies: + d "1" + es5-ext "~0.10.14" + events@^3.2.0: version "3.3.0" resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" @@ -5594,6 +5710,13 @@ express@^4.17.1: utils-merge "1.0.1" vary "~1.1.2" +ext@^1.1.2: + version "1.6.0" + resolved "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz#3871d50641e874cc172e2b53f919842d19db4c52" + integrity sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg== + dependencies: + type "^2.5.0" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -5934,6 +6057,15 @@ fs-constants@^1.0.0: resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== +fs-extra@^10.0.0: + version "10.0.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" + integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" @@ -6041,6 +6173,11 @@ get-stdin@^4.0.1: resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= +get-stdin@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" + integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== + get-stream@^4.0.0, get-stream@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -6141,6 +6278,13 @@ glob-parent@^5.0.0, glob-parent@^5.1.2, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" +glob-promise@^3.4.0: + version "3.4.0" + resolved "https://registry.npmjs.org/glob-promise/-/glob-promise-3.4.0.tgz#b6b8f084504216f702dc2ce8c9bc9ac8866fdb20" + integrity sha512-q08RJ6O+eJn+dVanerAndJwIcumgbDdYiUT7zFQl3Wm1xD6fBKtah7H8ZJChj4wP+8C+QfeVy8xautR7rdmKEw== + dependencies: + "@types/glob" "*" + glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" @@ -6244,7 +6388,7 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4: +graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.3, graceful-fs@^4.2.4: version "4.2.8" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== @@ -7098,6 +7242,11 @@ is-potential-custom-element-name@^1.0.1: resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== +is-promise@^2.2.2: + version "2.2.2" + resolved "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + is-regex@^1.0.4, is-regex@^1.1.1, is-regex@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -7736,6 +7885,13 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -7804,6 +7960,34 @@ json-parse-even-better-errors@^2.3.0: resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== +json-schema-ref-parser@^9.0.6: + version "9.0.9" + resolved "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz#66ea538e7450b12af342fa3d5b8458bc1e1e013f" + integrity sha512-qcP2lmGy+JUoQJ4DOQeLaZDqH9qSkeGCK3suKWxJXS82dg728Mn3j97azDMaOUmJAN4uCq91LdPx4K7E8F1a7Q== + dependencies: + "@apidevtools/json-schema-ref-parser" "9.0.9" + +json-schema-to-typescript@^10.1.5: + version "10.1.5" + resolved "https://registry.npmjs.org/json-schema-to-typescript/-/json-schema-to-typescript-10.1.5.tgz#9ac32808eb4d7c158684e270438ad07256c0cb1c" + integrity sha512-X8bNNksfCQo6LhEuqNxmZr4eZpPjXZajmimciuk8eWXzZlif9Brq7WuMGD/SOhBKcRKP2SGVDNZbC28WQqx9Rg== + dependencies: + "@types/json-schema" "^7.0.6" + "@types/lodash" "^4.14.168" + "@types/prettier" "^2.1.5" + cli-color "^2.0.0" + get-stdin "^8.0.0" + glob "^7.1.6" + glob-promise "^3.4.0" + is-glob "^4.0.1" + json-schema-ref-parser "^9.0.6" + json-stringify-safe "^5.0.1" + lodash "^4.17.20" + minimist "^1.2.5" + mkdirp "^1.0.4" + mz "^2.7.0" + prettier "^2.2.0" + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -7838,6 +8022,15 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" @@ -8169,6 +8362,13 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" + integrity sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM= + dependencies: + es5-ext "~0.10.2" + lz-string@^1.4.4: version "1.4.4" resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" @@ -8270,6 +8470,20 @@ memoize-one@^5.0.0, memoize-one@^5.1.1: resolved "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== +memoizee@^0.4.15: + version "0.4.15" + resolved "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz#e6f3d2da863f318d02225391829a6c5956555b72" + integrity sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ== + dependencies: + d "^1.0.1" + es5-ext "^0.10.53" + es6-weak-map "^2.0.3" + event-emitter "^0.3.5" + is-promise "^2.2.2" + lru-queue "^0.1.0" + next-tick "^1.1.0" + timers-ext "^0.1.7" + memory-pager@^1.0.2: version "1.5.0" resolved "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5" @@ -8729,6 +8943,15 @@ mute-stream@0.0.8: resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + nan@^2.13.2: version "2.15.0" resolved "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" @@ -8800,6 +9023,16 @@ new-github-release-url@1.0.0: dependencies: type-fest "^0.4.1" +next-tick@1, next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -9062,7 +9295,7 @@ oauth-sign@~0.9.0: resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -10328,6 +10561,11 @@ prepend-http@^2.0.0: resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= +prettier@^2.2.0: + version "2.4.1" + resolved "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz#671e11c89c14a4cfc876ce564106c4a6726c9f5c" + integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA== + pretty-error@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/pretty-error/-/pretty-error-3.0.4.tgz#94b1d54f76c1ed95b9c604b9de2194838e5b574e" @@ -12218,6 +12456,20 @@ text-table@^0.2.0: resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY= + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + throat@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" @@ -12243,6 +12495,14 @@ through@2, "through@>=2.2.7 <3", through@^2.3.6: resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= +timers-ext@^0.1.7: + version "0.1.7" + resolved "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6" + integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ== + dependencies: + es5-ext "~0.10.46" + next-tick "1" + timsort@^0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" @@ -12488,6 +12748,16 @@ type-is@~1.6.17, type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +type@^1.0.1: + version "1.2.0" + resolved "https://registry.npmjs.org/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.5.0: + version "2.5.0" + resolved "https://registry.npmjs.org/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" + integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== + typed-styles@^0.0.7: version "0.0.7" resolved "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz#93392a008794c4595119ff62dde6809dbc40a3d9" @@ -12606,6 +12876,11 @@ universalify@^0.1.2: resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" From 6dd1b0e0339490bf9e500f03f47ed59381644d85 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 16 Nov 2021 20:24:13 -0500 Subject: [PATCH 04/16] feat: adds field types to type generation --- .gitignore | 3 + .vscode/launch.json | 15 +- package.json | 1 + payload-types.ts | 541 +++++++++++++++++++++++++++++++++++++++ src/bin/generateTypes.ts | 149 ++++++++--- src/config/defaults.ts | 3 + src/config/schema.ts | 3 + src/config/types.ts | 3 + yarn.lock | 2 +- 9 files changed, 683 insertions(+), 37 deletions(-) create mode 100644 payload-types.ts diff --git a/.gitignore b/.gitignore index 09de291459..c5aa69d39f 100644 --- a/.gitignore +++ b/.gitignore @@ -228,3 +228,6 @@ build # Ignore built components components/index.js components/styles.css + +# Ignore generated types +./payload-types.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 4253a1d439..b31b4f0752 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -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/**" + ] + }, ] } diff --git a/package.json b/package.json index 9b06c60497..f8777217bb 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/payload-types.ts b/payload-types.ts new file mode 100644 index 0000000000..edc51ecd49 --- /dev/null +++ b/payload-types.ts @@ -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 {} diff --git a/src/bin/generateTypes.ts b/src/bin/generateTypes.ts index 9e265ec005..809df818be 100644 --- a/src/bin/generateTypes.ts +++ b/src/bin/generateTypes.ts @@ -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); diff --git a/src/config/defaults.ts b/src/config/defaults.ts index 0893a87381..784d1c389f 100644 --- a/src/config/defaults.ts +++ b/src/config/defaults.ts @@ -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, diff --git a/src/config/schema.ts b/src/config/schema.ts index 8c802a5418..a66f4a01d3 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -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({ diff --git a/src/config/types.ts b/src/config/types.ts index 96494d45bc..7ef49f3e0b 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -112,6 +112,9 @@ export type Config = { graphQL?: string; graphQLPlayground?: string; }; + typescript?: { + outputFile?: string + } debug?: boolean express?: { json?: { diff --git a/yarn.lock b/yarn.lock index 1b30a5184b..8386dda9fc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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== From 2ca76ba8ce6c61ee7b0a01a557b6f013ca483d6d Mon Sep 17 00:00:00 2001 From: James Date: Tue, 16 Nov 2021 21:01:57 -0500 Subject: [PATCH 05/16] feat: generates further field types --- payload-types.ts | 791 ++++++++++++++++++++++----------------- src/bin/generateTypes.ts | 154 +++++++- 2 files changed, 592 insertions(+), 353 deletions(-) diff --git a/payload-types.ts b/payload-types.ts index edc51ecd49..96a6a8afa7 100644 --- a/payload-types.ts +++ b/payload-types.ts @@ -8,6 +8,7 @@ export interface Config {} export interface NavigationArray { array?: { text?: string; + textarea?: string; id?: string; }[]; } @@ -17,317 +18,8 @@ export interface NavigationArray { */ 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; + relationship: (string | LocalizedPost)[]; + singleRelationship: string | LocalizedPost; } /** * This interface was referenced by `Config`'s JSON-Schema @@ -336,6 +28,7 @@ export interface Hook { export interface LocalizedPost { title: string; summary?: string; + description: string; priority: number; localizedGroup?: { text?: string; @@ -355,22 +48,117 @@ export interface LocalizedPost { } /** * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "localized-arrays". + * via the `definition` "blocks-global". */ -export interface LocalizedArray { - array: { - arrayText1: string; - arrayText2: string; - arrayText3?: string; - id?: string; - }[]; +export interface BlocksGlobal { + blocks?: ( + | { + author: string | PublicUser; + quote: string; + 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` "local-operations". + * via the `definition` "public-users". */ -export interface LocalOperation { - title: string; +export interface PublicUser { + email?: string; + resetPasswordToken?: string; + _verified?: boolean; + _verificationToken?: string; + loginAttempts?: number; + adminOnly?: string; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "admins". + */ +export interface Admin { + email?: string; + resetPasswordToken?: string; + enableAPIKey?: boolean; + apiKey?: string; + apiKeyIndex?: string; + loginAttempts?: number; + publicUser?: (string | PublicUser)[]; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "all-fields". + */ +export interface AllFields { + text: string; + descriptionText?: string; + descriptionFunction?: string; + image?: string | Media; + email?: string; + number?: number; + group?: { + nestedText1?: string; + nestedText2?: string; + }; + array?: { + arrayText1: string; + arrayText2: string; + arrayText3?: string; + checkbox?: boolean; + id?: string; + }[]; + blocks: ( + | { + testEmail: string; + id?: string; + blockName?: string; + blockType: 'email'; + } + | { + testNumber: number; + id?: string; + blockName?: string; + blockType: 'number'; + } + | { + author: string | PublicUser; + quote: string; + color: string; + id?: string; + blockName?: string; + blockType: 'quote'; + } + | { + label: string; + url: string; + id?: string; + blockName?: string; + blockType: 'cta'; + } + )[]; + relationship?: string | Conditions; + relationshipHasMany?: (string | LocalizedPost)[]; + relationshipMultipleCollections?: + | { + value: string | LocalizedPost; + relationTo: 'localized-posts'; + } + | { + value: string | Conditions; + relationTo: 'conditions'; + }; + textarea?: string; + slug: string; + checkbox?: boolean; } /** * This interface was referenced by `Config`'s JSON-Schema @@ -418,6 +206,287 @@ export interface Media { }; }; alt: string; + foundUploadSizes?: boolean; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "conditions". + */ +export interface Conditions { + title: string; + enableTest?: boolean; + 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'; + } + | { + author: string | PublicUser; + quote: string; + 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` "auto-label". + */ +export interface AutoLabel { + autoLabelField?: string; + noLabel?: string; + labelOverride?: string; + testRelationship?: string | AllFields; + 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 { + code: string; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "custom-components". + */ +export interface CustomComponent { + title: string; + description: 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; + owner: string | Admin; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "default-values". + */ +export interface DefaultValueTest { + text?: string; + image?: string | Media; + email?: string; + number?: number; + group?: { + nestedText1?: string; + nestedText2?: string; + }; + array?: { + arrayText1?: string; + arrayText2?: string; + arrayText3?: string; + checkbox?: boolean; + id?: string; + }[]; + blocks?: ( + | { + testEmail: string; + id?: string; + blockName?: string; + blockType: 'email'; + } + | { + testNumber: number; + id?: string; + blockName?: string; + blockType: 'number'; + } + | { + author: string | PublicUser; + quote: string; + color: string; + id?: string; + blockName?: string; + blockType: 'quote'; + } + | { + label: string; + url: string; + id?: string; + blockName?: string; + blockType: 'cta'; + } + )[]; + relationship?: string | Conditions; + relationshipHasMany?: (string | LocalizedPost)[]; + relationshipMultipleCollections?: + | { + value: string | LocalizedPost; + relationTo: 'localized-posts'; + } + | { + value: string | Conditions; + relationTo: 'conditions'; + }; + textarea?: string; + slug?: string; + checkbox?: boolean; +} +/** + * 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'; + } + | { + author: string | PublicUser; + quote: string; + 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'; + } + | { + author: string | PublicUser; + quote: string; + 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; + description: string; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "localized-arrays". + */ +export interface LocalizedArray { + array: { + allowPublicReadability?: boolean; + 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 @@ -444,28 +513,71 @@ export interface NestedArray { 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 {} +export interface RelationshipA { + post?: string | RelationshipB; + LocalizedPost?: (string | LocalizedPost)[]; + postLocalizedMultiple?: ( + | { + value: string | LocalizedPost; + relationTo: 'localized-posts'; + } + | { + value: string | AllFields; + relationTo: 'all-fields'; + } + | { + value: number | CustomID; + relationTo: 'custom-id'; + } + )[]; + postManyRelationships?: { + value: string | RelationshipB; + relationTo: 'relationship-b'; + }; + postMaxDepth?: string | RelationshipB; + customID?: (number | CustomID)[]; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "relationship-b". */ export interface RelationshipB { title?: string; + post?: (string | RelationshipA)[]; + postManyRelationships?: + | { + value: string | RelationshipA; + relationTo: 'relationship-a'; + } + | { + value: string | Media; + relationTo: 'media'; + }; + localizedPosts?: ( + | { + value: string | LocalizedPost; + relationTo: 'localized-posts'; + } + | { + value: string | PreviewablePost; + relationTo: 'previewable-post'; + } + )[]; + strictAccess?: string | StrictAccess; +} +/** + * 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 @@ -477,16 +589,6 @@ export interface RichText {} * 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". @@ -510,6 +612,7 @@ export interface Validation { */ export interface Unique { title: string; + description?: string; } /** * This interface was referenced by `Config`'s JSON-Schema diff --git a/src/bin/generateTypes.ts b/src/bin/generateTypes.ts index 809df818be..f330fd3fef 100644 --- a/src/bin/generateTypes.ts +++ b/src/bin/generateTypes.ts @@ -8,7 +8,22 @@ import { SanitizedConfig } from '../config/types'; import loadConfig from '../config/load'; import { SanitizedGlobalConfig } from '../globals/config/types'; -function generateFieldTypes(fields: Field[]): { +function getCollectionIDType(collections: SanitizedCollectionConfig[], slug): 'string' | 'number' { + const matchedCollection = collections.find((collection) => collection.slug === slug); + if (matchedCollection) { + const idField = matchedCollection.fields.find((field) => 'name' in field && field.name === 'id'); + + if (idField && idField.type === 'number') { + return 'number'; + } + + return 'string'; + } + + return undefined; +} + +function generateFieldTypes(config: SanitizedConfig, fields: Field[]): { properties: { [k: string]: JSONSchema4; } @@ -24,6 +39,8 @@ function generateFieldTypes(fields: Field[]): { switch (field.type) { case 'text': + case 'textarea': + case 'code': case 'email': { fieldSchema = { type: 'string' }; break; @@ -34,12 +51,131 @@ function generateFieldTypes(fields: Field[]): { break; } + case 'checkbox': { + fieldSchema = { type: 'boolean' }; + break; + } + + // TODO: + // Add enum types like Radio and Select + // Add point field type + + case 'relationship': { + if (Array.isArray(field.relationTo)) { + if (field.hasMany) { + fieldSchema = { + type: 'array', + items: { + oneOf: field.relationTo.map((relation) => { + const idFieldType = getCollectionIDType(config.collections, relation); + + return { + type: 'object', + additionalProperties: false, + properties: { + value: { + oneOf: [ + { + type: idFieldType, + }, + { + $ref: `#/definitions/${relation}`, + }, + ], + }, + relationTo: { + const: relation, + }, + }, + required: ['value', 'relationTo'], + }; + }), + }, + }; + } else { + fieldSchema = { + oneOf: field.relationTo.map((relation) => { + const idFieldType = getCollectionIDType(config.collections, relation); + + return { + type: 'object', + additionalProperties: false, + properties: { + value: { + oneOf: [ + { + type: idFieldType, + }, + { + $ref: `#/definitions/${relation}`, + }, + ], + }, + relationTo: { + const: relation, + }, + }, + required: ['value', 'relationTo'], + }; + }), + }; + } + } else { + const idFieldType = getCollectionIDType(config.collections, field.relationTo); + + if (field.hasMany) { + fieldSchema = { + type: 'array', + items: { + oneOf: [ + { + type: idFieldType, + }, + { + $ref: `#/definitions/${field.relationTo}`, + }, + ], + }, + }; + } else { + fieldSchema = { + oneOf: [ + { + type: idFieldType, + }, + { + $ref: `#/definitions/${field.relationTo}`, + }, + ], + }; + } + } + + break; + } + + case 'upload': { + const idFieldType = getCollectionIDType(config.collections, field.relationTo); + + fieldSchema = { + oneOf: [ + { + type: idFieldType, + }, + { + $ref: `#/definitions/${field.relationTo}`, + }, + ], + }; + break; + } + case 'blocks': { fieldSchema = { type: 'array', items: { oneOf: field.blocks.map((block) => { - const blockSchema = generateFieldTypes(block.fields); + const blockSchema = generateFieldTypes(config, block.fields); return { type: 'object', @@ -67,14 +203,14 @@ function generateFieldTypes(fields: Field[]): { items: { type: 'object', additionalProperties: false, - ...generateFieldTypes(field.fields), + ...generateFieldTypes(config, field.fields), }, }; break; } case 'row': { - const topLevelFields = generateFieldTypes(field.fields); + const topLevelFields = generateFieldTypes(config, field.fields); requiredTopLevelProps = requiredTopLevelProps.concat(topLevelFields.required); topLevelProps = topLevelProps.concat(Object.entries(topLevelFields.properties).map((prop) => prop)); break; @@ -84,7 +220,7 @@ function generateFieldTypes(fields: Field[]): { fieldSchema = { type: 'object', additionalProperties: false, - ...generateFieldTypes(field.fields), + ...generateFieldTypes(config, field.fields), }; break; } @@ -128,14 +264,14 @@ function generateFieldTypes(fields: Field[]): { }; } -function entityToJsonSchema(entity: SanitizedCollectionConfig | SanitizedGlobalConfig): any { +function entityToJsonSchema(config: SanitizedConfig, entity: SanitizedCollectionConfig | SanitizedGlobalConfig): any { const title = 'label' in entity ? entity.label : entity.labels.singular; return { title, type: 'object', additionalProperties: false, - ...generateFieldTypes(entity.fields), + ...generateFieldTypes(config, entity.fields), }; } @@ -145,11 +281,11 @@ function configToJsonSchema(config: SanitizedConfig): JSONSchema4 { [ ...config.globals.map((global) => [ global.slug, - entityToJsonSchema(global), + entityToJsonSchema(config, global), ]), ...config.collections.map((collection) => [ collection.slug, - entityToJsonSchema(collection), + entityToJsonSchema(config, collection), ]), ], ), From ed5a5ebe7e16d20577556ab233032a52bef6d2cb Mon Sep 17 00:00:00 2001 From: James Date: Tue, 16 Nov 2021 21:15:49 -0500 Subject: [PATCH 06/16] feat: finishes typing all fields --- payload-types.ts | 48 ++++++++++++++++++++++-- src/bin/generateTypes.ts | 79 ++++++++++++++++++++++++++++++++++------ 2 files changed, 112 insertions(+), 15 deletions(-) diff --git a/payload-types.ts b/payload-types.ts index 96a6a8afa7..1f8b81bc6b 100644 --- a/payload-types.ts +++ b/payload-types.ts @@ -29,6 +29,9 @@ export interface LocalizedPost { title: string; summary?: string; description: string; + richText?: { + [k: string]: unknown; + }[]; priority: number; localizedGroup?: { text?: string; @@ -41,6 +44,9 @@ export interface LocalizedPost { id?: string; }[]; richTextBlocks?: { + content?: { + [k: string]: unknown; + }[]; id?: string; blockName?: string; blockType: 'richTextBlock'; @@ -76,9 +82,11 @@ export interface BlocksGlobal { export interface PublicUser { email?: string; resetPasswordToken?: string; + resetPasswordExpiration?: string; _verified?: boolean; _verificationToken?: string; loginAttempts?: number; + lockUntil?: string; adminOnly?: string; } /** @@ -88,10 +96,13 @@ export interface PublicUser { export interface Admin { email?: string; resetPasswordToken?: string; + resetPasswordExpiration?: string; enableAPIKey?: boolean; apiKey?: string; apiKeyIndex?: string; loginAttempts?: number; + lockUntil?: string; + roles: ('admin' | 'editor' | 'moderator' | 'user' | 'viewer')[]; publicUser?: (string | PublicUser)[]; } /** @@ -103,6 +114,11 @@ export interface AllFields { descriptionText?: string; descriptionFunction?: string; image?: string | Media; + select: 'option-1' | 'option-2' | 'option-3' | 'option-4'; + selectMany: ('option-1' | 'option-2' | 'option-3' | 'option-4')[]; + dayOnlyDateFieldExample: string; + timeOnlyDateFieldExample?: string; + radioGroupExample: 'option-1' | 'option-2' | 'option-3'; email?: string; number?: number; group?: { @@ -157,8 +173,12 @@ export interface AllFields { relationTo: 'conditions'; }; textarea?: string; + richText: { + [k: string]: unknown; + }[]; slug: string; checkbox?: boolean; + dateFieldExample?: string; } /** * This interface was referenced by `Config`'s JSON-Schema @@ -321,6 +341,7 @@ export interface File { filename?: string; mimeType?: string; filesize?: number; + type: 'Type 1' | 'Type 2' | 'Type 3'; owner: string | Admin; } /** @@ -330,6 +351,9 @@ export interface File { export interface DefaultValueTest { text?: string; image?: string | Media; + select?: 'option-1' | 'option-2' | 'option-3' | 'option-4'; + selectMany?: ('option-1' | 'option-2' | 'option-3' | 'option-4')[]; + radioGroupExample?: 'option-1' | 'option-2' | 'option-3'; email?: string; number?: number; group?: { @@ -386,6 +410,9 @@ export interface DefaultValueTest { textarea?: string; slug?: string; checkbox?: boolean; + richText?: { + [k: string]: unknown; + }[]; } /** * This interface was referenced by `Config`'s JSON-Schema @@ -583,12 +610,24 @@ export interface StrictAccess { * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "rich-text". */ -export interface RichText {} +export interface RichText { + defaultRichText: { + [k: string]: unknown; + }[]; + customRichText: { + [k: string]: unknown; + }[]; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "select". */ -export interface Select {} +export interface Select { + Select: 'one' | 'two' | 'three'; + SelectHasMany: ('one' | 'two' | 'three')[]; + SelectJustStrings: ('blue' | 'green' | 'yellow')[]; + Radio: 'one' | 'two' | 'three'; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "validations". @@ -641,4 +680,7 @@ export interface UnstoredMedia { * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "geolocation". */ -export interface Geolocation {} +export interface Geolocation { + location?: [number, number]; + localizedPoint?: [number, number]; +} diff --git a/src/bin/generateTypes.ts b/src/bin/generateTypes.ts index f330fd3fef..22859efcd4 100644 --- a/src/bin/generateTypes.ts +++ b/src/bin/generateTypes.ts @@ -2,7 +2,7 @@ import fs from 'fs'; import type { JSONSchema4 } from 'json-schema'; import { compile } from 'json-schema-to-typescript'; -import { fieldIsPresentationalOnly, fieldAffectsData, Field } from '../fields/config/types'; +import { fieldAffectsData, Field, Option } from '../fields/config/types'; import { SanitizedCollectionConfig } from '../collections/config/types'; import { SanitizedConfig } from '../config/types'; import loadConfig from '../config/load'; @@ -23,6 +23,16 @@ function getCollectionIDType(collections: SanitizedCollectionConfig[], slug): 's return undefined; } +function returnOptionEnums(options: Option[]): string[] { + return options.map((option) => { + if (typeof option === 'object' && 'value' in option) { + return option.value; + } + + return option; + }); +} + function generateFieldTypes(config: SanitizedConfig, fields: Field[]): { properties: { [k: string]: JSONSchema4; @@ -41,7 +51,8 @@ function generateFieldTypes(config: SanitizedConfig, fields: Field[]): { case 'text': case 'textarea': case 'code': - case 'email': { + case 'email': + case 'date': { fieldSchema = { type: 'string' }; break; } @@ -56,9 +67,60 @@ function generateFieldTypes(config: SanitizedConfig, fields: Field[]): { break; } - // TODO: - // Add enum types like Radio and Select - // Add point field type + case 'richText': { + fieldSchema = { + type: 'array', + items: { + type: 'object', + }, + }; + + break; + } + + case 'radio': { + fieldSchema = { + type: 'string', + enum: returnOptionEnums(field.options), + }; + + break; + } + + case 'select': { + const selectType: JSONSchema4 = { + type: 'string', + enum: returnOptionEnums(field.options), + }; + + if (field.hasMany) { + fieldSchema = { + type: 'array', + items: selectType, + }; + } else { + fieldSchema = selectType; + } + + break; + } + + case 'point': { + fieldSchema = { + type: 'array', + minItems: 2, + maxItems: 2, + items: [ + { + type: 'number', + }, + { + type: 'number', + }, + ], + }; + break; + } case 'relationship': { if (Array.isArray(field.relationTo)) { @@ -230,12 +292,6 @@ function generateFieldTypes(config: SanitizedConfig, fields: Field[]): { } } - let default_ = {}; - - if (!fieldIsPresentationalOnly(field) && fieldAffectsData(field) && typeof field.defaultValue !== 'undefined') { - default_ = { default: field.defaultValue }; - } - if (fieldSchema && fieldAffectsData(field)) { return [ ...properties, @@ -243,7 +299,6 @@ function generateFieldTypes(config: SanitizedConfig, fields: Field[]): { field.name, { ...fieldSchema, - ...default_, }, ], ]; From b2fe27dda503b30ab10c3f32f5eb53b69ce7205b Mon Sep 17 00:00:00 2001 From: Tejas Ahluwalia <39881648+tejasahluwalia@users.noreply.github.com> Date: Sun, 21 Nov 2021 00:05:10 +0530 Subject: [PATCH 07/16] Include 'WebP' as image type This is called in the useThumbail hook. Adding webp support for thumbnails in the admin Thumbnail component. --- src/uploads/isImage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uploads/isImage.ts b/src/uploads/isImage.ts index 4728e6d58d..f91314031c 100644 --- a/src/uploads/isImage.ts +++ b/src/uploads/isImage.ts @@ -1,3 +1,3 @@ export default function isImage(mimeType: string): boolean { - return ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml'].indexOf(mimeType) > -1; + return ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/webp'].indexOf(mimeType) > -1; } From d9e1b5ede33616893f9ba6990eeccf5410b75ae1 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 23 Nov 2021 10:18:30 -0500 Subject: [PATCH 08/16] fix: issue with querying by id and using comma-separated values --- src/mongoose/sanitizeFormattedValue.ts | 31 ++++++++++++-------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/mongoose/sanitizeFormattedValue.ts b/src/mongoose/sanitizeFormattedValue.ts index 034e15d710..a151080be5 100644 --- a/src/mongoose/sanitizeFormattedValue.ts +++ b/src/mongoose/sanitizeFormattedValue.ts @@ -8,7 +8,7 @@ export const sanitizeQueryValue = (schemaType: SchemaType, path: string, operato // Disregard invalid _ids - if (path === '_id' && typeof val === 'string') { + if (path === '_id' && typeof val === 'string' && val.split(',').length === 1) { if (schemaType?.instance === 'ObjectID') { const isValid = mongoose.Types.ObjectId.isValid(val); @@ -69,27 +69,24 @@ export const sanitizeQueryValue = (schemaType: SchemaType, path: string, operato } } - if (['all', 'not_in'].includes(operator) && typeof formattedValue === 'string') { + if (['all', 'not_in', 'in'].includes(operator) && typeof formattedValue === 'string') { formattedValue = createArrayFromCommaDelineated(formattedValue); } - if (schemaOptions && (schemaOptions.ref || schemaOptions.refPath)) { - if (operator === 'in') { - if (typeof formattedValue === 'string') formattedValue = createArrayFromCommaDelineated(formattedValue); - if (Array.isArray(formattedValue)) { - formattedValue = formattedValue.reduce((formattedValues, inVal) => { - const newValues = [inVal]; - if (mongoose.Types.ObjectId.isValid(inVal)) newValues.push(new mongoose.Types.ObjectId(inVal)); + if (schemaOptions && (schemaOptions.ref || schemaOptions.refPath) && operator === 'in') { + if (Array.isArray(formattedValue)) { + formattedValue = formattedValue.reduce((formattedValues, inVal) => { + const newValues = [inVal]; + if (mongoose.Types.ObjectId.isValid(inVal)) newValues.push(new mongoose.Types.ObjectId(inVal)); - const parsedNumber = parseFloat(inVal); - if (!Number.isNaN(parsedNumber)) newValues.push(parsedNumber); + const parsedNumber = parseFloat(inVal); + if (!Number.isNaN(parsedNumber)) newValues.push(parsedNumber); - return [ - ...formattedValues, - ...newValues, - ]; - }, []); - } + return [ + ...formattedValues, + ...newValues, + ]; + }, []); } } From 91fae55d90f3b9e37faae39c312e12c004e94a06 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 23 Nov 2021 10:19:25 -0500 Subject: [PATCH 09/16] chore: updates type generation log message --- src/bin/generateTypes.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/generateTypes.ts b/src/bin/generateTypes.ts index 22859efcd4..98796b6042 100644 --- a/src/bin/generateTypes.ts +++ b/src/bin/generateTypes.ts @@ -351,11 +351,11 @@ function configToJsonSchema(config: SanitizedConfig): JSONSchema4 { export function generateTypes(): void { const config = loadConfig(); - console.log('compiling ts types'); + console.log(`Writing TypeScript types to ${config.typescript.outputFile}...`); + const jsonSchema = configToJsonSchema(config); compile(jsonSchema, 'Config', { - bannerComment: '// auto-generated by payload', unreachableDefinitions: true, }).then((compiled) => { fs.writeFileSync(config.typescript.outputFile, compiled); From 21a810c38c16e6907069d9076abf55718e439497 Mon Sep 17 00:00:00 2001 From: Elliot DeNolf Date: Tue, 23 Nov 2021 16:43:59 -0500 Subject: [PATCH 10/16] feat: add id fields to generated types --- demo/payload.config.ts | 3 +++ src/bin/generateTypes.ts | 31 +++++++++++++++++++------------ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/demo/payload.config.ts b/demo/payload.config.ts index ddddca5107..b5cc1c4d08 100644 --- a/demo/payload.config.ts +++ b/demo/payload.config.ts @@ -37,6 +37,9 @@ import UnstoredMedia from './collections/UnstoredMedia'; export default buildConfig({ cookiePrefix: 'payload', serverURL: 'http://localhost:3000', + typescript: { + outputFile: './generated-types.ts', + }, admin: { user: 'admins', indexHTML: path.resolve(__dirname, './client/index.html'), diff --git a/src/bin/generateTypes.ts b/src/bin/generateTypes.ts index 22859efcd4..41559bd737 100644 --- a/src/bin/generateTypes.ts +++ b/src/bin/generateTypes.ts @@ -2,25 +2,22 @@ import fs from 'fs'; import type { JSONSchema4 } from 'json-schema'; import { compile } from 'json-schema-to-typescript'; -import { fieldAffectsData, Field, Option } from '../fields/config/types'; +import payload from '..'; +import { fieldAffectsData, Field, Option, FieldAffectingData } 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 getCollectionIDType(collections: SanitizedCollectionConfig[], slug): 'string' | 'number' { +function getCollectionIDType(collections: SanitizedCollectionConfig[], slug: string): 'string' | 'number' { const matchedCollection = collections.find((collection) => collection.slug === slug); - if (matchedCollection) { - const idField = matchedCollection.fields.find((field) => 'name' in field && field.name === 'id'); + const customIdField = matchedCollection.fields.find((field) => 'name' in field && field.name === 'id'); - if (idField && idField.type === 'number') { - return 'number'; - } - - return 'string'; + if (customIdField && customIdField.type === 'number') { + return 'number'; } - return undefined; + return 'string'; } function returnOptionEnums(options: Option[]): string[] { @@ -319,9 +316,18 @@ function generateFieldTypes(config: SanitizedConfig, fields: Field[]): { }; } -function entityToJsonSchema(config: SanitizedConfig, entity: SanitizedCollectionConfig | SanitizedGlobalConfig): any { +function entityToJsonSchema(config: SanitizedConfig, entity: SanitizedCollectionConfig | SanitizedGlobalConfig): JSONSchema4 { const title = 'label' in entity ? entity.label : entity.labels.singular; + const idField: FieldAffectingData = { type: 'text', name: 'id', required: true }; + const customIdField = entity.fields.find((field) => fieldAffectsData(field) && field.name === 'id') as FieldAffectingData; + + if (customIdField) { + customIdField.required = true; + } else { + entity.fields.unshift(idField); + } + return { title, type: 'object', @@ -351,7 +357,7 @@ function configToJsonSchema(config: SanitizedConfig): JSONSchema4 { export function generateTypes(): void { const config = loadConfig(); - console.log('compiling ts types'); + payload.logger.info('Compiling TS types for Collections and Globals...'); const jsonSchema = configToJsonSchema(config); compile(jsonSchema, 'Config', { @@ -359,6 +365,7 @@ export function generateTypes(): void { unreachableDefinitions: true, }).then((compiled) => { fs.writeFileSync(config.typescript.outputFile, compiled); + payload.logger.info(`Types written to ${config.typescript.outputFile}`); }); } From 6fd5ac2c082a5a5e6f510d781b2a2e12b7b62cb9 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 24 Nov 2021 10:30:47 -0500 Subject: [PATCH 11/16] feat: azure cosmos compatibility --- src/collections/operations/find.ts | 20 +++++++++++++------- src/mongoose/buildSchema.ts | 5 +++++ src/mongoose/sanitizeFormattedValue.ts | 2 +- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/collections/operations/find.ts b/src/collections/operations/find.ts index b867ca8de8..983280d49a 100644 --- a/src/collections/operations/find.ts +++ b/src/collections/operations/find.ts @@ -98,22 +98,28 @@ async function find(incomingArgs: Arguments): Promise { // Find // ///////////////////////////////////// - let { sort } = args; + let sortParam: Record; - if (!sort) { + if (!args.sort) { if (collectionConfig.timestamps) { - sort = '-createdAt'; + sortParam = { createdAt: 'desc' }; } else { - sort = '-_id'; + sortParam = { _id: 'desc' }; } - } else if (sort === 'id' || sort === '-id') { - sort = sort.replace('id', '_id'); + } else if (args.sort.indexOf('-') === 0) { + sortParam = { + [args.sort.substring(1)]: 'desc', + }; + } else { + sortParam = { + [args.sort]: 'asc', + }; } const optionsToExecute = { page: page || 1, limit: limit || 10, - sort, + sort: sortParam, lean: true, leanWithId: true, useEstimatedCount, diff --git a/src/mongoose/buildSchema.ts b/src/mongoose/buildSchema.ts index e1b0af2428..8bc34c9922 100644 --- a/src/mongoose/buildSchema.ts +++ b/src/mongoose/buildSchema.ts @@ -107,6 +107,11 @@ const buildSchema = (config: SanitizedConfig, configFields: Field[], buildSchema } }); + if (buildSchemaOptions?.options?.timestamps) { + indexFields.push({ createdAt: 1 }); + indexFields.push({ updatedAt: 1 }); + } + const schema = new Schema(fields, options); indexFields.forEach((index) => { schema.index(index); diff --git a/src/mongoose/sanitizeFormattedValue.ts b/src/mongoose/sanitizeFormattedValue.ts index a151080be5..8e2d03695a 100644 --- a/src/mongoose/sanitizeFormattedValue.ts +++ b/src/mongoose/sanitizeFormattedValue.ts @@ -91,7 +91,7 @@ export const sanitizeQueryValue = (schemaType: SchemaType, path: string, operato } if (operator === 'like' && path !== '_id') { - formattedValue = { $regex: formattedValue, $options: '-i' }; + formattedValue = { $regex: formattedValue, $options: 'i' }; } if (operator === 'exists') { From bd373598b57e4777b3c61498fa0049d0e522162a Mon Sep 17 00:00:00 2001 From: James Date: Wed, 24 Nov 2021 10:45:51 -0500 Subject: [PATCH 12/16] chore: beta release --- CHANGELOG.md | 11 ++++++++++- package.json | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b37733886c..e1f34f0a8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,24 @@ -## [0.12.8-beta.0](https://github.com/payloadcms/payload/compare/v0.12.3...v0.12.8-beta.0) (2021-11-10) +## [0.12.9-beta.0](https://github.com/payloadcms/payload/compare/v0.12.3...v0.12.9-beta.0) (2021-11-24) ### Bug Fixes * [#351](https://github.com/payloadcms/payload/issues/351) ([94c2b8d](https://github.com/payloadcms/payload/commit/94c2b8d80b046c067057d4ad089ed6a2edd656cf)) * bug with relationship cell when no doc is available ([40b33d9](https://github.com/payloadcms/payload/commit/40b33d9f5e99285cb0de148dbe059259817fcad8)) +* ensures 'like' query param remains functional in all cases ([20d4e72](https://github.com/payloadcms/payload/commit/20d4e72a951dfcbf1cc301d0938e5095932436b9)) +* ensures buildQuery works with fields as well as simultaneous or / and ([72fc413](https://github.com/payloadcms/payload/commit/72fc413764c6c42ba64a45f01d99b68ad3bd46c4)) +* ensures relationship field search can return more than 10 options ([57c0346](https://github.com/payloadcms/payload/commit/57c0346a00286a3df695ea46e5c2630494183b5b)) * ensures richtext links retain proper formatting ([abf61d0](https://github.com/payloadcms/payload/commit/abf61d0734c09fd0fc5c5b827cb0631e11701f71)) +* ensures querying by relationship subpaths works ([37b21b0](https://github.com/payloadcms/payload/commit/37b21b07628e892e85c2cf979d9e2c8af0d291f7)) +* issue with querying by id and using comma-separated values ([d9e1b5e](https://github.com/payloadcms/payload/commit/d9e1b5ede33616893f9ba6990eeccf5410b75ae1)) +* updates field description type to include react nodes ([291c193](https://github.com/payloadcms/payload/commit/291c193ad4a9ec8ce9310cc63c714eba10eca102)) + ### Features +* :tada: builds a way to automatically generate types for collections and globals! docs coming soon. * adds relationship filter field ([463c4e6](https://github.com/payloadcms/payload/commit/463c4e60de8e647fca6268b826d826f9c6e45412)) +* azure cosmos compatibility ([6fd5ac2](https://github.com/payloadcms/payload/commit/6fd5ac2c082a5a5e6f510d781b2a2e12b7b62cb9)) * ensures update hooks have access to full original docs even in spite of access control ([b2c5b7e](https://github.com/payloadcms/payload/commit/b2c5b7e5752e829c7a53c054decceb43ec33065e)) * improves querying logic ([4c85747](https://github.com/payloadcms/payload/commit/4c8574784995b1cb1f939648f4d2158286089b3d)) diff --git a/package.json b/package.json index f8777217bb..4571d08c13 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "payload", - "version": "0.12.8-beta.0", + "version": "0.12.9-beta.0", "description": "Node, React and MongoDB Headless CMS and Application Framework", "license": "SEE LICENSE IN license.md", "author": { From 5d43262f42e0529a44572f398aa1ec5fd7858286 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 24 Nov 2021 11:02:54 -0500 Subject: [PATCH 13/16] feat: indexes filenames --- src/fields/baseFields/getBaseUploadFields.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fields/baseFields/getBaseUploadFields.ts b/src/fields/baseFields/getBaseUploadFields.ts index e2fc2b3fb6..fd4e549f8b 100644 --- a/src/fields/baseFields/getBaseUploadFields.ts +++ b/src/fields/baseFields/getBaseUploadFields.ts @@ -66,6 +66,7 @@ const getBaseUploadFields = ({ config, collection }: Options): Field[] => { name: 'filename', label: 'File Name', type: 'text', + index: true, admin: { readOnly: true, disabled: true, From 40ca3dae61f8ddf05363b6cad426deba4cde1e30 Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Wed, 24 Nov 2021 12:32:44 -0500 Subject: [PATCH 14/16] feat: migrates admin preview to async --- demo/collections/Preview.ts | 7 +++-- .../elements/PreviewButton/index.tsx | 29 ++++++++++++++++--- src/config/types.ts | 2 +- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/demo/collections/Preview.ts b/demo/collections/Preview.ts index 81c7c241fc..be8437e39a 100644 --- a/demo/collections/Preview.ts +++ b/demo/collections/Preview.ts @@ -8,10 +8,13 @@ const Preview: CollectionConfig = { }, admin: { useAsTitle: 'title', - preview: (doc, { token }) => { + preview: async (doc, { token }) => { const { title } = doc; if (title) { - return `http://localhost:3000/previewable-posts/${title}?preview=true&token=${token}`; + const mockAsyncReq = await fetch(`http://localhost:3000/api/previewable-post?depth=0`) + const mockJSON = await mockAsyncReq.json(); + const mockParam = mockJSON?.docs?.[0]?.title || ''; + return `http://localhost:3000/previewable-posts/${title}?preview=true&token=${token}&mockParam=${mockParam}`; } return null; diff --git a/src/admin/components/elements/PreviewButton/index.tsx b/src/admin/components/elements/PreviewButton/index.tsx index 25f360fa9e..7b1672eeaf 100644 --- a/src/admin/components/elements/PreviewButton/index.tsx +++ b/src/admin/components/elements/PreviewButton/index.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { useAuth } from '@payloadcms/config-provider'; import Button from '../Button'; import { Props } from './types'; @@ -6,13 +6,34 @@ import { useLocale } from '../../utilities/Locale'; const baseClass = 'preview-btn'; -const PreviewButton: React.FC = ({ generatePreviewURL, data }) => { +const PreviewButton: React.FC = (props) => { + const { + generatePreviewURL, + data + } = props; + + const [url, setUrl] = useState(undefined); + const locale = useLocale(); const { token } = useAuth(); - if (generatePreviewURL && typeof generatePreviewURL === 'function') { - const url = generatePreviewURL(data, { locale, token }); + useEffect(() => { + if (generatePreviewURL && typeof generatePreviewURL === 'function') { + const makeRequest = async () => { + const previewURL = await generatePreviewURL(data, { locale, token }); + setUrl(previewURL); + } + makeRequest(); + } + }, [ + generatePreviewURL, + locale, + token, + data + ]); + + if (url) { return (