feat: improve typescript comments (#1467)

This commit is contained in:
Thomas Ghysels
2022-11-29 11:10:30 -04:00
committed by GitHub
parent a90a1a9e19
commit 5bd86571ca
10 changed files with 462 additions and 106 deletions

View File

@@ -9,13 +9,30 @@ const ProcessingContext = createContext(false);
const ModifiedContext = createContext(false);
const FormFieldsContext = createSelectorContext<FormFieldsContextType>([{}, () => null]);
/**
* Get the state of the form, can be used to submit & validate the form.
*
* @see https://payloadcms.com/docs/admin/hooks#useform
*/
const useForm = (): Context => useContext(FormContext);
const useWatchForm = (): Context => useContext(FormWatchContext);
const useFormSubmitted = (): boolean => useContext(SubmittedContext);
const useFormProcessing = (): boolean => useContext(ProcessingContext);
const useFormModified = (): boolean => useContext(ModifiedContext);
/**
* Get and set the value of a form field based on a selector
*
* @see https://payloadcms.com/docs/admin/hooks#useformfields
*/
const useFormFields = <Value = unknown>(selector: (context: FormFieldsContextType) => Value): Value => useContextSelector(FormFieldsContext, selector);
/**
* Get the state of all form fields.
*
* @see https://payloadcms.com/docs/admin/hooks#useallformfields
*/
const useAllFormFields = (): FormFieldsContextType => useFullContext(FormFieldsContext);
export {

View File

@@ -8,6 +8,11 @@ import { useOperation } from '../../utilities/OperationProvider';
import useThrottledEffect from '../../../hooks/useThrottledEffect';
import { UPDATE } from '../Form/types';
/**
* Get and set the value of a form field.
*
* @see https://payloadcms.com/docs/admin/hooks#usefield
*/
const useField = <T extends unknown>(options: Options): FieldType<T> => {
const {
path,

View File

@@ -203,6 +203,7 @@ export type CollectionAdminOptions = {
preview?: GeneratePreviewURL
}
/** Manage all aspects of a data collection */
export type CollectionConfig = {
slug: string;
/**
@@ -276,10 +277,22 @@ export type CollectionConfig = {
*/
auth?: IncomingAuthType | boolean;
/**
* Upload options
* Customize the handling of incoming file uploads
*
* @default false // disable uploads
*/
upload?: IncomingUploadType | boolean;
/**
* Customize the handling of incoming file uploads
*
* @default false // disable versioning
*/
versions?: IncomingCollectionVersions | boolean;
/**
* Add `createdAt` and `updatedAt` fields
*
* @default true
*/
timestamps?: boolean
};

View File

@@ -10,7 +10,11 @@ import React from 'react';
import { LoggerOptions } from 'pino';
import type { InitOptions as i18nInitOptions } from 'i18next';
import { Payload } from '..';
import { AfterErrorHook, CollectionConfig, SanitizedCollectionConfig } from '../collections/config/types';
import {
AfterErrorHook,
CollectionConfig,
SanitizedCollectionConfig,
} from '../collections/config/types';
import { GlobalConfig, SanitizedGlobalConfig } from '../globals/config/types';
import { PayloadRequest } from '../express/types';
import { Where } from '../types';
@@ -20,17 +24,20 @@ type Email = {
fromName: string;
fromAddress: string;
logMockCredentials?: boolean;
}
};
// eslint-disable-next-line no-use-before-define
export type Plugin = (config: Config) => Config;
type GeneratePreviewURLOptions = {
locale: string
token: string
}
locale: string;
token: string;
};
export type GeneratePreviewURL = (doc: Record<string, unknown>, options: GeneratePreviewURLOptions) => Promise<string> | string
export type GeneratePreviewURL = (
doc: Record<string, unknown>,
options: GeneratePreviewURLOptions
) => Promise<string> | string;
export type EmailTransport = Email & {
transport: Transporter;
@@ -48,7 +55,9 @@ export type EmailOptions = EmailTransport | EmailTransportOptions | Email;
* type guard for EmailOptions
* @param emailConfig
*/
export function hasTransport(emailConfig: EmailOptions): emailConfig is EmailTransport {
export function hasTransport(
emailConfig: EmailOptions,
): emailConfig is EmailTransport {
return (emailConfig as EmailTransport).transport !== undefined;
}
@@ -56,15 +65,18 @@ export function hasTransport(emailConfig: EmailOptions): emailConfig is EmailTra
* type guard for EmailOptions
* @param emailConfig
*/
export function hasTransportOptions(emailConfig: EmailOptions): emailConfig is EmailTransportOptions {
export function hasTransportOptions(
emailConfig: EmailOptions,
): emailConfig is EmailTransportOptions {
return (emailConfig as EmailTransportOptions).transportOptions !== undefined;
}
export type InitOptions = {
/** Express app for Payload to use */
express?: Express;
/** Mongo connection URL, starts with `mongo` */
mongoURL: string | false;
/** Extra configuration options that will be passed to Mongo */
mongoOptions?: ConnectOptions;
/** Secure string that Payload will use for any encryption workflows */
@@ -73,7 +85,7 @@ export type InitOptions = {
/**
* Configuration for Payload's email functionality
*
* https://payloadcms.com/docs/email/overview
* @see https://payloadcms.com/docs/email/overview
*/
email?: EmailOptions;
@@ -98,158 +110,442 @@ export type InitOptions = {
loggerOptions?: LoggerOptions;
};
/**
* This result is calculated on the server
* and then sent to the client allowing the dashboard to show accessible data and actions.
*
* If the result is `true`, the user has access.
* If the result is an object, it is interpreted as a Mongo query.
*
* @example `{ createdBy: { equals: id } }`
*
* @example `{ tenant: { in: tenantIds } }`
*
* @see https://payloadcms.com/docs/access-control/overview
*/
export type AccessResult = boolean | Where;
type AccessArgs<T = any, U = any> = {
req: PayloadRequest<U>
id?: string | number
data?: T
/** The original request that requires an access check */
req: PayloadRequest<U>;
/** ID of the resource being accessed */
id?: string | number;
/**
* The relevant resource that is being accessed.
*
* `data` is null when a list is requested
*/
data?: T;
};
/**
* Access function runs on the server
* and is sent to the client allowing the dashboard to show accessible data and actions.
*
* @see https://payloadcms.com/docs/access-control/overview
*/
export type Access<T = any, U = any> = (
args: AccessArgs<T, U>
) => AccessResult | Promise<AccessResult>;
/** Equivalent to express middleware, but with an enhanced request object */
export interface PayloadHandler {
(req: PayloadRequest, res: Response, next: NextFunction): void;
}
/**
* Access function
* Docs: https://payloadcms.com/docs/rest-api/overview#custom-endpoints
*/
export type Access<T = any, U = any> = (args: AccessArgs<T, U>) => AccessResult | Promise<AccessResult>;
export interface PayloadHandler {
(
req: PayloadRequest,
res: Response,
next: NextFunction,
): void
}
export type Endpoint = {
path: string
method: 'get' | 'head' | 'post' | 'put' | 'patch' | 'delete' | 'connect' | 'options' | string
handler: PayloadHandler | PayloadHandler[]
root?: boolean
}
/**
* Pattern that should match the path of the incoming request
*
* Compatible with the Express router
*/
path: string;
/** HTTP method (or "all") */
method:
| 'get'
| 'head'
| 'post'
| 'put'
| 'patch'
| 'delete'
| 'connect'
| 'options'
| string;
/**
* Middleware that will be called when the path/method matches
*
* Compatible with Express middleware
*/
handler: PayloadHandler | PayloadHandler[];
/**
* Set to `true` to disable the Payload middleware for this endpoint
* @default false
*/
root?: boolean;
};
export type AdminView = React.ComponentType<{ user: User, canAccessAdmin: boolean }>
export type AdminView = React.ComponentType<{
user: User;
canAccessAdmin: boolean;
}>;
export type AdminRoute = {
Component: AdminView
path: string
exact?: boolean
strict?: boolean
sensitive?: boolean
}
Component: AdminView;
path: string;
/** Whether the path should be matched exactly or as a prefix */
exact?: boolean;
strict?: boolean;
sensitive?: boolean;
};
/**
* @see https://payloadcms.com/docs/configuration/localization#localization
*/
export type LocalizationConfig = {
locales: string[]
defaultLocale: string
fallback?: boolean
}
/**
* List of supported locales
* @exanple `["en", "es", "fr", "nl", "de", "jp"]`
*/
locales: string[];
/**
* Locale for users that have not expressed their preference for a specific locale
* @exanple `"en"`
*/
defaultLocale: string;
/** Set to `true` to let missing values in localised fields fall back to the values in `defaultLocale` */
fallback?: boolean;
};
/**
* This is the central configuration
*
* @see https://payloadcms.com/docs/configuration/overview
*/
export type Config = {
/** Configure admin dashboard */
admin?: {
/** The slug of a Collection that you want be used to log in to the Admin dashboard. */
user?: string;
/** Base meta data to use for the Admin panel. Included properties are titleSuffix, ogImage, and favicon. */
meta?: {
/**
* String to append to the <title> of admin pages
* @example `" - My Brand"`
*/
titleSuffix?: string;
/**
* Public path to an image
*
* This image may be displayed as preview when the link is shared on social media
*/
ogImage?: string;
/**
* Public path to an icon
*
* This image may be displayed in the browser next to the title of the page
*/
favicon?: string;
}
};
/** If set to true, the entire Admin panel will be disabled. */
disable?: boolean;
/** Replace the entirety of the index.html file used by the Admin panel. Reference the base index.html file to ensure your replacement has the appropriate HTML elements. */
indexHTML?: string;
css?: string
dateFormat?: string
avatar?: 'default' | 'gravatar' | React.ComponentType<any>,
logoutRoute?: string,
inactivityRoute?: string,
/** Absolute path to a stylesheet that you can use to override / customize the Admin panel styling. */
css?: string;
/** Global date format that will be used for all dates in the Admin panel. Any valid date-fns format pattern can be used. */
dateFormat?: string;
/** Set account profile picture. Options: gravatar, default or a custom React component. */
avatar?: 'default' | 'gravatar' | React.ComponentType<any>;
/** The route for the logout page. */
logoutRoute?: string;
/** The route the user will be redirected to after being inactive for too long. */
inactivityRoute?: string;
/**
* Add extra and/or replace built-in components with custom components
*
* @see https://payloadcms.com/docs/admin/components
*/
components?: {
routes?: AdminRoute[]
providers?: React.ComponentType<{ children: React.ReactNode }>[]
beforeDashboard?: React.ComponentType<any>[]
afterDashboard?: React.ComponentType<any>[]
beforeLogin?: React.ComponentType<any>[]
afterLogin?: React.ComponentType<any>[]
beforeNavLinks?: React.ComponentType<any>[]
afterNavLinks?: React.ComponentType<any>[]
Nav?: React.ComponentType<any>
/**
* Add custom routes in the admin dashboard
*/
routes?: AdminRoute[];
/**
* Wrap the admin dashboard in custom context providers
*/
providers?: React.ComponentType<{ children: React.ReactNode }>[];
/**
* Add custom components before the collection overview
*/
beforeDashboard?: React.ComponentType<any>[];
/**
* Add custom components after the collection overview
*/
afterDashboard?: React.ComponentType<any>[];
/**
* Add custom components before the email/password field
*/
beforeLogin?: React.ComponentType<any>[];
/**
* Add custom components after the email/password field
*/
afterLogin?: React.ComponentType<any>[];
/**
* Add custom components before the navigation links
*/
beforeNavLinks?: React.ComponentType<any>[];
/**
* Add custom components after the navigation links
*/
afterNavLinks?: React.ComponentType<any>[];
/**
* Replace the navigation with a custom component
*/
Nav?: React.ComponentType<any>;
/** Replace logout related components */
logout?: {
Button?: React.ComponentType<any>,
}
/** Replace the logout button */
Button?: React.ComponentType<any>;
};
/** Replace graphical components */
graphics?: {
Icon?: React.ComponentType<any>
Logo?: React.ComponentType<any>
}
/** Replace the icon in the navigation */
Icon?: React.ComponentType<any>;
/** Replace the logo on the login page */
Logo?: React.ComponentType<any>;
v;
};
/* Replace complete pages */
views?: {
Account?: React.ComponentType<any>
Dashboard?: React.ComponentType<any>
}
}
/** Replace the account screen */
Account?: React.ComponentType<any>;
/** Replace the admin homepage */
Dashboard?: React.ComponentType<any>;
v;
};
};
/**
* Control pagination when querying collections.
*
* @see https://payloadcms.com/docs/queries/overview
*/
pagination?: {
/**
* Limit the number of documents that are displayed on 1 page in the list view
*
* @default 10
*/
defaultLimit?: number;
options?: number[];
}
/**
* Suggest alternative options for the limit of documents on the list view
*
* @default [5, 10, 25, 50, 100]
*/
limits?: number[]
};
/** Customize the Webpack config that's used to generate the Admin panel. */
webpack?: (config: Configuration) => Configuration;
};
/**
* Manage the datamodel of your application
*
* @see https://payloadcms.com/docs/configuration/collections#collection-configs
*/
collections?: CollectionConfig[];
/** Custom REST endpoints */
endpoints?: Endpoint[];
/**
* @see https://payloadcms.com/docs/configuration/globals#global-configs
*/
globals?: GlobalConfig[];
/**
* Control the behaviour of the admin internationalisation.
*
* See i18next options.
*
* @default
* {
* fallbackLng: 'en',
* debug: false,
* supportedLngs: Object.keys(translations),
* resources: translations,
* }
*/
i18n?: i18nInitOptions;
/**
* Define the absolute URL of your app including the protocol, for example `https://example.org`.
* No paths allowed, only protocol, domain and (optionally) port.
*
* @see https://payloadcms.com/docs/configuration/overview#options
*/
serverURL?: string;
/**
* Prefix a string to all cookies that Payload sets.
*
* @default "payload"
*/
cookiePrefix?: string;
/** A whitelist array of URLs to allow Payload cookies to be accepted from as a form of CSRF protection. */
csrf?: string[];
/** Either a whitelist array of URLS to allow CORS requests from, or a wildcard string ('*') to accept incoming requests from any domain. */
cors?: string[] | '*';
/** Control the routing structure that Payload binds itself to. */
routes?: {
/** Defaults to /api */
api?: string;
/** Defaults to /admin */
admin?: string;
/** Defaults to /graphql */
graphQL?: string;
/** Defaults to /playground */
graphQLPlayground?: string;
};
/** Control how typescript interfaces are generated from your collections. */
typescript?: {
outputFile?: string
}
debug?: boolean
/** Filename to write the generated types to */
outputFile?: string;
};
/** Enable to expose more detailed error information. */
debug?: boolean;
/**
* Express-specific middleware options such as compression and JSON parsing.
*
* @see https://payloadcms.com/docs/configuration/express
*/
express?: {
/** Control the way JSON is parsed */
json?: {
limit?: number
},
/** Defaults to 2MB */
limit?: number;
};
/** Control the way responses are compressed */
compression?: {
[key: string]: unknown
},
[key: string]: unknown;
};
/**
* @deprecated express.middleware will be removed in a future version. Please migrate to express.postMiddleware.
*/
middleware?: any[]
preMiddleware?: any[]
postMiddleware?: any[]
},
middleware?: any[];
preMiddleware?: any[];
postMiddleware?: any[];
};
/**
* If a user does not specify `depth` while requesting a resource, this depth will be used.
*
* @see https://payloadcms.com/docs/getting-started/concepts#depth
*
* @default 2
*/
defaultDepth?: number;
/**
* The maximum allowed depth to be permitted application-wide. This setting helps prevent against malicious queries.
*
* @see https://payloadcms.com/docs/getting-started/concepts#depth
*
* @default 10
*/
maxDepth?: number;
/**
* The maximum allowed depth to be permitted application-wide. This setting helps prevent against malicious queries.
*
* @default 40000
*/
defaultMaxTextLength?: number;
/** Automatically index all sortable top-level fields in the database to improve sort performance and add database compatibility for Azure Cosmos and similar. */
indexSortableFields?: boolean;
/**
* Limit heavy usage
*
* @default
* {
* window: 15 * 60 * 100, // 1.5 minutes,
* max: 500,
* }
*/
rateLimit?: {
window?: number;
max?: number;
trustProxy?: boolean;
skip?: (req: PayloadRequest) => boolean;
};
/**
* Customize the handling of incoming file uploads for collections that have uploads enabled.
*/
upload?: Options;
/**
* Translate your content to different languages/locales.
*
* @default false // disable localization
*/
localization?: LocalizationConfig | false;
/**
* Manage the GraphQL API
*
* You can add your own GraphQL queries and mutations to Payload, making use of all the types that Payload has defined for you.
*
* @see https://payloadcms.com/docs/access-control/overview
*/
graphQL?: {
mutations?: ((graphQL: typeof GraphQL, payload: Payload) => Record<string, unknown>),
queries?: ((graphQL: typeof GraphQL, payload: Payload) => Record<string, unknown>),
/**
* Function that returns an object containing keys to custom GraphQL mutations
*
* @see https://payloadcms.com/docs/access-control/overview
*/
mutations?: (
graphQL: typeof GraphQL,
payload: Payload
) => Record<string, unknown>;
/**
* Function that returns an object containing keys to custom GraphQL queries
*
* @see https://payloadcms.com/docs/access-control/overview
*/
queries?: (
graphQL: typeof GraphQL,
payload: Payload
) => Record<string, unknown>;
maxComplexity?: number;
disablePlaygroundInProduction?: boolean;
disable?: boolean;
schemaOutputFile?: string;
};
/**
* Replace the built-in components with custom ones
*/
components?: { [key: string]: JSX.Element | (() => JSX.Element) };
/**
* Tap into Payload-wide hooks.
*
* @see https://payloadcms.com/docs/hooks/overview
*/
hooks?: {
afterError?: AfterErrorHook;
};
/**
* An array of Payload plugins.
*
* @see https://payloadcms.com/docs/plugins/overview
*/
plugins?: Plugin[];
/** Send anonymous telemetry data about general usage. */
telemetry?: boolean;
onInit?: (payload: Payload) => Promise<void> | void
/** A function that is called immediately following startup that receives the Payload instance as its only argument. */
onInit?: (payload: Payload) => Promise<void> | void;
};
export type SanitizedConfig = Omit<DeepRequired<Config>, 'collections' | 'globals'> & {
collections: SanitizedCollectionConfig[]
globals: SanitizedGlobalConfig[]
export type SanitizedConfig = Omit<
DeepRequired<Config>,
'collections' | 'globals'
> & {
collections: SanitizedCollectionConfig[];
globals: SanitizedGlobalConfig[];
paths: { [key: string]: string };
}
};
export type EntityDescription = string | Record<string, string> | (() => string) | React.ComponentType<any>
export type EntityDescription =
| string
| (() => string)
| React.ComponentType<any>;

View File

@@ -8,20 +8,44 @@ import { User } from '../auth/types';
import { Document } from '../types';
import { TypeWithID } from '../globals/config/types';
export declare type PayloadRequest<T = any> = Request & {
/** Express request with some Payload related context added */
export declare type PayloadRequest<U = any> = Request & {
/** The global payload object */
payload: Payload;
/** Optimized document loader */
payloadDataLoader: DataLoader<string, TypeWithID>;
/**
* The requested locale if specified
* Only available for localised collections
*/
locale?: string;
/** The locale that should be used for a field when it is not translated to the requested locale */
fallbackLocale?: string;
/** Information about the collection that is being accessed
* - Configuration from payload-config.ts
* - Mongo model for this collection
* - GraphQL type metadata
* */
collection?: Collection;
/** What triggered this request */
payloadAPI: 'REST' | 'local' | 'graphQL';
/** Uploaded files */
files?: {
/**
* This is the file that Payload will use for the file upload, other files are ignored.
*
*/
file: UploadedFile;
};
/** I18next instance */
i18n: Ii18n;
/** Get a translation for the admin screen */
t: TFunction;
user: T & User | null;
/** The signed in user */
user: (U & User) | null;
/** Resized versions of the image that was uploaded during this request */
payloadUploadSizes?: Record<string, Buffer>;
/** Cache of documents related to the current request */
findByID?: {
[slug: string]: (q: unknown) => Document;
};

View File

@@ -16,13 +16,13 @@ describe('sanitizeFields', () => {
sanitizeFields(fields, []);
}).toThrow(MissingFieldType);
});
it("should throw on invalid field name", () => {
it('should throw on invalid field name', () => {
const fields: Field[] = [
{
label: "some.collection",
name: "some.collection",
type: "text",
}
label: 'some.collection',
name: 'some.collection',
type: 'text',
},
];
expect(() => {
sanitizeFields(fields, []);

View File

@@ -16,28 +16,28 @@ export type Operator =
| 'less_than'
| 'less_than_equal'
| 'like'
| 'near'
| 'near';
export type WhereField = {
[key in Operator]?: unknown
}
[key in Operator]?: unknown;
};
export type Where = {
[key: string]: WhereField | Where[]
or?: Where[]
and?: Where[]
}
[key: string]: WhereField | Where[];
or?: Where[];
and?: Where[];
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type Document = any;
export interface PayloadMongooseDocument extends MongooseDocument {
setLocale: (locale: string, fallback: string) => void
filename?: string
sizes?: FileData[]
setLocale: (locale: string, fallback: string) => void;
filename?: string;
sizes?: FileData[];
}
export type Operation = 'create' | 'read' | 'update' | 'delete'
export type Operation = 'create' | 'read' | 'update' | 'delete';
export function docHasTimestamps(doc: any): doc is TypeWithTimestamps {
return doc?.createdAt && doc?.updatedAt;

View File

@@ -115,7 +115,7 @@ export default async function resizeAndSave({
function createImageName(
outputImage: OutputImage,
bufferObject: { data: Buffer; info: sharp.OutputInfo },
extension: string
extension: string,
): string {
return `${outputImage.name}-${bufferObject.info.width}x${bufferObject.info.height}.${extension}`;
}

View File

@@ -3,7 +3,7 @@ export function arrayMove<T>(array: readonly T[], from: number, to: number) {
slicedArray.splice(
to < 0 ? array.length + to : to,
0,
slicedArray.splice(from, 1)[0]
slicedArray.splice(from, 1)[0],
);
return slicedArray;
}