progress to buildConfig, splitting out type organization

This commit is contained in:
James
2020-11-21 12:22:38 -05:00
parent 894a400b86
commit 16d0289c36
40 changed files with 390 additions and 374 deletions

View File

@@ -1,3 +1,3 @@
const config = require('./src/babel.config'); const config = require('./src/babel.config');
module.exports = config; module.exports = (api) => config(api);

1
config.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
export * from './dist/config';

View File

@@ -1,3 +0,0 @@
const payload = require('./src');
module.exports = payload;

View File

@@ -9,12 +9,4 @@ module.exports = {
'dist', 'dist',
], ],
testTimeout: 15000, testTimeout: 15000,
moduleNameMapper: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/tests/mocks/fileMock.js',
'\\.(css|scss)$': '<rootDir>/tests/mocks/emptyModule.js',
},
transform: {
'\\.(css|less|scss)$': './tests/stub-transformer.js',
'\\.(js|jsx|ts|tsx)$': 'babel-jest',
},
}; };

View File

@@ -1,19 +1,19 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import unsanitizedConfig from 'payload/unsanitizedConfig'; import unsanitizedConfig from 'payload/unsanitizedConfig';
import sanitizeConfig from '../../../../utilities/sanitizeConfig'; import sanitizeConfig from '../../../../config/sanitize';
import Context from './context'; import Context from './context';
const sanitizedConfig = sanitizeConfig(unsanitizedConfig); const sanitizedConfig = sanitizeConfig(unsanitizedConfig);
const ConfigProvider = ({ children }) => ( const ConfigProvider = ({ children }) => (
<Context.Provider value={sanitizedConfig}> <Context.Provider value={sanitizedConfig}>
{children} {children}
</Context.Provider> </Context.Provider>
); );
ConfigProvider.propTypes = { ConfigProvider.propTypes = {
children: PropTypes.node.isRequired, children: PropTypes.node.isRequired,
}; };
export default ConfigProvider; export default ConfigProvider;

View File

@@ -1,5 +1,5 @@
import { MongoClient } from 'mongodb'; import { MongoClient } from 'mongodb';
import getConfig from '../utilities/getConfig'; import getConfig from '../config/load';
import { email, password, mongo } from '../../tests/api/credentials'; import { email, password, mongo } from '../../tests/api/credentials';
require('isomorphic-fetch'); require('isomorphic-fetch');

View File

@@ -1,27 +1,34 @@
module.exports = { module.exports = (api) => {
presets: [ const config = {
'@babel/typescript', presets: [
[ '@babel/typescript',
require.resolve('@babel/preset-env'), [
{ require.resolve('@babel/preset-env'),
targets: [ {
'defaults', targets: [
'not IE 11', 'defaults',
'not IE_Mob 11', 'not IE 11',
], 'not IE_Mob 11',
}, ],
},
],
require.resolve('@babel/preset-react'),
], ],
require.resolve('@babel/preset-react'), plugins: [
], require.resolve('@babel/plugin-transform-runtime'),
plugins: [ require.resolve('@babel/plugin-proposal-class-properties'),
require.resolve('@babel/plugin-transform-runtime'), require.resolve('@babel/plugin-proposal-optional-chaining'),
require.resolve('@babel/plugin-proposal-class-properties'), ],
require.resolve('@babel/plugin-proposal-optional-chaining'), };
[
if (api.env('test')) {
config.plugins.push([
'babel-plugin-ignore-html-and-css-imports', 'babel-plugin-ignore-html-and-css-imports',
{ {
removeExtensions: ['.svg', '.css', '.scss', '.png', '.jpg'], removeExtensions: ['.svg', '.css', '.scss', '.png', '.jpg'],
}, },
], ]);
], }
return config;
}; };

View File

@@ -3,9 +3,9 @@
const webpack = require('webpack'); const webpack = require('webpack');
const getWebpackProdConfig = require('../webpack/getWebpackProdConfig'); const getWebpackProdConfig = require('../webpack/getWebpackProdConfig');
const findConfig = require('../utilities/findConfig'); const findConfig = require('../config/find');
const getConfig = require('../utilities/getConfig'); const getConfig = require('../config/load');
const sanitizeConfig = require('../utilities/sanitizeConfig'); const sanitizeConfig = require('../config/sanitize');
const configPath = findConfig(); const configPath = findConfig();

View File

@@ -0,0 +1,10 @@
import { Collection } from './types';
import sanitize from './sanitize';
const buildCollection = (collection: Collection) => {
const sanitized = sanitize(collection);
return sanitized;
};
module.exports = buildCollection;

View File

@@ -1,13 +1,13 @@
import merge from 'deepmerge'; import merge from 'deepmerge';
import sanitizeFields from '../fields/sanitize'; import sanitizeFields from '../../fields/config/sanitize';
import toKebabCase from '../utilities/toKebabCase'; import toKebabCase from '../../utilities/toKebabCase';
import baseAuthFields from '../fields/baseFields/baseFields'; import baseAuthFields from '../../fields/baseFields/baseFields';
import baseAPIKeyFields from '../fields/baseFields/baseAPIKeyFields'; import baseAPIKeyFields from '../../fields/baseFields/baseAPIKeyFields';
import baseVerificationFields from '../fields/baseFields/baseVerificationFields'; import baseVerificationFields from '../../fields/baseFields/baseVerificationFields';
import baseAccountLockFields from '../fields/baseFields/baseAccountLockFields'; import baseAccountLockFields from '../../fields/baseFields/baseAccountLockFields';
import baseUploadFields from '../fields/baseFields/baseUploadFields'; import baseUploadFields from '../../fields/baseFields/baseUploadFields';
import baseImageUploadFields from '../fields/baseFields/baseImageUploadFields'; import baseImageUploadFields from '../../fields/baseFields/baseImageUploadFields';
import formatLabels from '../utilities/formatLabels'; import formatLabels from '../../utilities/formatLabels';
const mergeBaseFields = (fields, baseFields) => { const mergeBaseFields = (fields, baseFields) => {
const mergedFields = []; const mergedFields = [];

View File

@@ -0,0 +1,47 @@
export type Collection = {
slug: string;
labels?: {
singular: string;
plural: string;
};
admin?: {
useAsTitle?: string;
defaultColumns?: string[];
components?: any;
};
hooks?: {
beforeOperation?: Hook[];
beforeValidate?: Hook[];
beforeChange?: Hook[];
afterChange?: Hook[];
beforeRead?: Hook[];
afterRead?: Hook[];
beforeDelete?: Hook[];
afterDelete?: Hook[];
};
access?: {
create?: Access;
read?: Access;
update?: Access;
delete?: Access;
admin?: Access;
};
auth?: {
tokenExpiration?: number;
verify?:
| boolean
| { generateEmailHTML: string; generateEmailSubject: string };
maxLoginAttempts?: number;
lockTime?: number;
useAPIKey?: boolean;
cookies?:
| {
secure?: boolean;
sameSite?: string;
domain?: string | undefined;
}
| boolean;
};
fields: Field[];
};

View File

@@ -2,7 +2,7 @@
* @jest-environment node * @jest-environment node
*/ */
import { request, GraphQLClient } from 'graphql-request'; import { request, GraphQLClient } from 'graphql-request';
import getConfig from '../../../utilities/getConfig'; import getConfig from '../../../config/load';
import { email, password } from '../../../../tests/api/credentials'; import { email, password } from '../../../../tests/api/credentials';
require('isomorphic-fetch'); require('isomorphic-fetch');

View File

@@ -1,5 +1,5 @@
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import getConfig from '../../utilities/getConfig'; import getConfig from '../../config/load';
import { email, password } from '../../../tests/api/credentials'; import { email, password } from '../../../tests/api/credentials';
require('isomorphic-fetch'); require('isomorphic-fetch');

View File

@@ -1,4 +1,4 @@
import getConfig from '../../utilities/getConfig'; import getConfig from '../../config/load';
import { email, password } from '../../../tests/api/credentials'; import { email, password } from '../../../tests/api/credentials';
require('isomorphic-fetch'); require('isomorphic-fetch');

View File

@@ -1,4 +1,4 @@
import getConfig from '../../utilities/getConfig'; import getConfig from '../../config/load';
import { email, password } from '../../../tests/api/credentials'; import { email, password } from '../../../tests/api/credentials';
require('isomorphic-fetch'); require('isomorphic-fetch');

View File

@@ -1,7 +1,7 @@
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import FormData from 'form-data'; import FormData from 'form-data';
import getConfig from '../../utilities/getConfig'; import getConfig from '../../config/load';
import fileExists from '../../../tests/api/utils/fileExists'; import fileExists from '../../../tests/api/utils/fileExists';
import { email, password } from '../../../tests/api/credentials'; import { email, password } from '../../../tests/api/credentials';

12
src/config/build.ts Normal file
View File

@@ -0,0 +1,12 @@
import { PayloadConfig } from '../types';
import sanitize from './sanitize';
import validate from './validate';
const buildConfig = (config: PayloadConfig) => {
const validated = validate(config);
const sanitized = sanitize(validated);
return sanitized;
};
module.exports = buildConfig;

View File

@@ -1,10 +1,11 @@
/* eslint-disable import/no-dynamic-require */ /* eslint-disable import/no-dynamic-require */
/* eslint-disable global-require */ /* eslint-disable global-require */
import path from 'path'; import path from 'path';
import findConfig from './findConfig'; import { Config } from './types';
import findConfig from './find';
const configPath = findConfig(); const configPath = findConfig();
const getConfig = () => { const getConfig = (): Config => {
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
const publicConfig = require(configPath); const publicConfig = require(configPath);
return { return {

View File

@@ -1,13 +1,12 @@
import { PayloadConfig } from '../types'; import { Config } from './types';
import defaultUser from '../auth/default'; import defaultUser from '../auth/default';
import sanitizeCollection from '../collections/sanitize'; import sanitizeCollection from '../collections/config/sanitize';
import { InvalidConfiguration } from '../errors'; import { InvalidConfiguration } from '../errors';
import sanitizeGlobals from '../globals/sanitize'; import sanitizeGlobals from '../globals/config/sanitize';
import validateSchema from '../schema/validateSchema'; import checkDuplicateCollections from '../utilities/checkDuplicateCollections';
import checkDuplicateCollections from './checkDuplicateCollections';
const sanitizeConfig = (config: PayloadConfig) => { const sanitizeConfig = (config: Config): Config => {
const sanitizedConfig = validateSchema({ ...config }); const sanitizedConfig = { ...config };
// TODO: remove default values from sanitize in favor of assigning in the schema within validateSchema and use https://www.npmjs.com/package/ajv#coercing-data-types where needed // TODO: remove default values from sanitize in favor of assigning in the schema within validateSchema and use https://www.npmjs.com/package/ajv#coercing-data-types where needed
if (sanitizedConfig.publicENV === undefined) sanitizedConfig.publicENV = {}; if (sanitizedConfig.publicENV === undefined) sanitizedConfig.publicENV = {};

107
src/config/types.ts Normal file
View File

@@ -0,0 +1,107 @@
import { Express } from 'express';
import { CSSProperties } from 'react';
import { Transporter } from 'nodemailer';
import SMTPConnection from 'nodemailer/lib/smtp-connection';
import { Collection } from '../collections/config/types';
type MockEmailTransport = {
transport?: 'mock';
fromName?: string;
fromAddress?: string;
};
type ValidEmailTransport = {
transport: Transporter;
transportOptions?: SMTPConnection.Options;
fromName: string;
fromAddress: string;
};
export type EmailOptions = ValidEmailTransport | MockEmailTransport;
export type InitOptions = {
express?: Express;
mongoURL: string;
secret: string;
license?: string;
email?: EmailOptions;
local?: boolean; // I have no idea what this is
onInit?: () => void;
};
export type SendEmailOptions = {
from: string;
to: string;
subject: string;
html: string;
};
export type MockEmailCredentials = {
user: string;
pass: string;
web: string;
};
export type Hook = (...args: any[]) => any | void;
export type Access = (args?: any) => boolean;
export type Config = {
admin?: {
user?: string
meta?: {
titleSuffix?: string
ogImage?: string
favicon?: string
}
disable?: boolean
};
collections?: Collection[];
globals?: Global[];
serverURL?: string;
cookiePrefix?: string;
csrf?: string[];
cors?: string[];
publicENV: { [key: string]: string };
routes?: {
api?: string;
admin?: string;
graphQL?: string;
graphQLPlayground?: string;
};
email?: EmailOptions;
local?: boolean;
defaultDepth?: number;
maxDepth?: number;
rateLimit?: {
window?: number;
max?: number;
trustProxy?: boolean;
skip?: (req: Request) => boolean; // TODO: Type join Request w/ PayloadRequest
};
upload?: {
limits?: {
fileSize?: number;
};
};
localization?: {
locales: string[];
};
defaultLocale?: string;
fallback?: boolean;
graphQL?: {
mutations?: {
[key: string]: unknown
},
queries?: {
[key: string]: unknown
},
maxComplexity?: number;
disablePlaygroundInProduction?: boolean;
};
components: { [key: string]: JSX.Element | (() => JSX.Element) };
paths?: { [key: string]: string };
hooks?: {
afterError?: () => void;
};
webpack?: (config: any) => any;
serverModules?: string[];
};

View File

@@ -1,15 +1,15 @@
import Ajv from 'ajv'; import Ajv from 'ajv';
import * as payloadSchema from './payload.schema.json'; import * as configSchema from './schema.json';
import * as collectionSchema from './collection.schema.json'; import * as collectionSchema from '../collections/config/schema.json';
import InvalidSchema from '../errors/InvalidSchema'; import InvalidSchema from '../errors/InvalidSchema';
import { PayloadConfig } from '../types'; import { PayloadConfig } from './types';
const validateSchema = (config: PayloadConfig) => { const validateSchema = (config: PayloadConfig): PayloadConfig => {
const ajv = new Ajv({ useDefaults: true }); const ajv = new Ajv({ useDefaults: true });
const validate = ajv.addSchema(collectionSchema, '/collection.schema.json') const validate = ajv.addSchema(collectionSchema, '../collections/config/schema.json').compile(configSchema);
.compile(payloadSchema);
const valid = validate(config); const valid = validate(config);
if (!valid) { if (!valid) {
throw new InvalidSchema(`Invalid payload config provided. Found ${validate.errors.length} errors`, validate.errors); throw new InvalidSchema(`Invalid payload config provided. Found ${validate.errors.length} errors`, validate.errors);
} }

View File

@@ -1,12 +1,11 @@
const babelConfig = require('./babel.config'); const babelConfig = require('./babel.config')({
env: () => false,
});
if (process.env.NODE_ENV !== 'test') { if (process.env.NODE_ENV !== 'test') {
// eslint-disable-next-line global-require // eslint-disable-next-line global-require
require('@babel/register')({ require('@babel/register')({
...babelConfig, ...babelConfig,
ignore: [
/node_modules[\\/](?!@payloadcms[\\/]payload[\\/]src[\\/]admin|@payloadcms[\\/]payload[\\/]components|@payloadcms[\\/]payload[\\/]hooks).*/,
],
extensions: ['.js', '.jsx', '.ts', '.tsx'], extensions: ['.js', '.jsx', '.ts', '.tsx'],
plugins: [ plugins: [
[ [

View File

@@ -1,11 +1,13 @@
import nodemailer, { Transporter } from 'nodemailer'; import nodemailer, { Transporter } from 'nodemailer';
import { PayloadEmailOptions } from '../types'; import { EmailOptions } from '../config/types';
import { InvalidConfiguration } from '../errors'; import { InvalidConfiguration } from '../errors';
import mockHandler from './mockHandler'; import mockHandler from './mockHandler';
import Logger from '../utilities/logger'; import Logger from '../utilities/logger';
import { BuildEmailResult } from './types';
const logger = Logger(); const logger = Logger();
export default async function buildEmail(emailConfig: PayloadEmailOptions) { export default async function buildEmail(emailConfig: EmailOptions): BuildEmailResult {
if (!emailConfig.transport || emailConfig.transport === 'mock') { if (!emailConfig.transport || emailConfig.transport === 'mock') {
const mockAccount = await mockHandler(emailConfig); const mockAccount = await mockHandler(emailConfig);
// Only log mock credentials if was explicitly set in config // Only log mock credentials if was explicitly set in config
@@ -38,8 +40,8 @@ export default async function buildEmail(emailConfig: PayloadEmailOptions) {
email.transport = transport; email.transport = transport;
} catch (err) { } catch (err) {
logger.error( logger.error(
"There is an error with the email configuration you have provided.", 'There is an error with the email configuration you have provided.',
err err,
); );
} }

View File

@@ -1,3 +1,12 @@
import { TestAccount, Transporter } from 'nodemailer'; import { TestAccount, Transporter } from 'nodemailer';
import Mail from 'nodemailer/lib/mailer';
import SMTPConnection from 'nodemailer/lib/smtp-connection';
export type BuildEmailResult = Promise<{
transport: Mail,
transportOptions?: SMTPConnection.Options,
fromName: string,
fromAddress: string,
}>
export type MockEmailHandler = { account: TestAccount; transport: Transporter }; export type MockEmailHandler = { account: TestAccount; transport: Transporter };

View File

@@ -1,5 +1,5 @@
import sanitizeFields from './sanitize'; import sanitizeFields from './sanitize';
import { MissingFieldType, InvalidFieldRelationship } from '../errors'; import { MissingFieldType, InvalidFieldRelationship } from '../../errors';
describe('sanitizeFields', () => { describe('sanitizeFields', () => {
it('should throw on missing type field', () => { it('should throw on missing type field', () => {

View File

@@ -1,5 +1,5 @@
import { MissingFieldType, InvalidFieldRelationship } from '../errors'; import { MissingFieldType, InvalidFieldRelationship } from '../../errors';
import validations from './validations'; import validations from '../validations';
const sanitizeFields = (fields, validRelationships) => { const sanitizeFields = (fields, validRelationships) => {
if (!fields) return []; if (!fields) return [];

View File

@@ -0,0 +1,30 @@
import { CSSProperties } from 'react';
export type Field = {
name: string;
label: string;
type:
| 'number'
| 'text'
| 'email'
| 'textarea'
| 'richText'
| 'code'
| 'radio'
| 'checkbox'
| 'date'
| 'upload'
| 'relationship'
| 'row'
| 'array'
| 'group'
| 'select'
| 'blocks';
localized?: boolean;
fields?: Field[];
admin?: {
position?: string;
width?: string;
style?: CSSProperties;
};
};

View File

View File

@@ -1,5 +1,5 @@
import { MissingGlobalLabel } from '../errors'; import { MissingGlobalLabel } from '../../errors';
import sanitizeFields from '../fields/sanitize'; import sanitizeFields from '../../fields/config/sanitize';
const sanitizeGlobals = (collections, globals) => { const sanitizeGlobals = (collections, globals) => {
// ///////////////////////////////// // /////////////////////////////////

View File

View File

@@ -2,7 +2,7 @@
* @jest-environment node * @jest-environment node
*/ */
import getConfig from '../../utilities/getConfig'; import getConfig from '../../config/load';
import { email, password } from '../../../tests/api/credentials'; import { email, password } from '../../../tests/api/credentials';
require('isomorphic-fetch'); require('isomorphic-fetch');

View File

@@ -1,11 +1,15 @@
import express from 'express'; import express, { Express, Router } from 'express';
import crypto from 'crypto'; import crypto from 'crypto';
import { Router } from 'express';
import { TestAccount } from 'nodemailer';
import {
Config,
InitOptions,
} from './config/types';
import {
Collection,
} from './collections/config/types';
import { import {
PayloadConfig,
PayloadCollection,
PayloadInitOptions,
PayloadLogger,
CreateOptions, CreateOptions,
FindOptions, FindOptions,
FindGlobalOptions, FindGlobalOptions,
@@ -18,7 +22,7 @@ import Logger from './utilities/logger';
import bindOperations from './init/bindOperations'; import bindOperations from './init/bindOperations';
import bindRequestHandlers from './init/bindRequestHandlers'; import bindRequestHandlers from './init/bindRequestHandlers';
import bindResolvers from './init/bindResolvers'; import bindResolvers from './init/bindResolvers';
import getConfig from './utilities/getConfig'; import loadConfig from './config/load';
import authenticate from './express/middleware/authenticate'; import authenticate from './express/middleware/authenticate';
import connectMongoose from './mongoose/connect'; import connectMongoose from './mongoose/connect';
import expressMiddleware from './express/middleware'; import expressMiddleware from './express/middleware';
@@ -29,7 +33,6 @@ import initGlobals from './globals/init';
import initGraphQLPlayground from './graphql/initPlayground'; import initGraphQLPlayground from './graphql/initPlayground';
import initStatic from './express/static'; import initStatic from './express/static';
import GraphQL from './graphql'; import GraphQL from './graphql';
import sanitizeConfig from './utilities/sanitizeConfig';
import buildEmail from './email/build'; import buildEmail from './email/build';
import identifyAPI from './express/middleware/identifyAPI'; import identifyAPI from './express/middleware/identifyAPI';
import errorHandler from './express/middleware/errorHandler'; import errorHandler from './express/middleware/errorHandler';
@@ -37,26 +40,58 @@ import performFieldOperations from './fields/performFieldOperations';
import localOperations from './collections/operations/local'; import localOperations from './collections/operations/local';
import localGlobalOperations from './globals/operations/local'; import localGlobalOperations from './globals/operations/local';
import { encrypt, decrypt } from './auth/crypto'; import { encrypt, decrypt } from './auth/crypto';
import { TestAccount } from 'nodemailer'; import { MockEmailHandler, BuildEmailResult } from './email/types';
import { MockEmailHandler } from './email/types';
require('es6-promise').polyfill();
require('isomorphic-fetch'); require('isomorphic-fetch');
class Payload { class Payload {
config: PayloadConfig; config: Config;
collections: PayloadCollection[] = [];
logger: PayloadLogger;
router: Router;
email: any;
init(options: PayloadInitOptions) { collections: Collection[] = [];
logger: typeof Logger;
express: Express
router: Router;
emailOptions: any;
email: BuildEmailResult;
license: string;
secret: string;
mongoURL: string;
local: boolean;
initAuth: typeof initAuth;
encrypt: typeof encrypt;
decrypt: typeof decrypt;
initCollections: typeof initCollections;
initGlobals: typeof initGlobals;
initGraphQLPlayground: typeof initGraphQLPlayground;
initStatic: typeof initStatic;
initAdmin: typeof initAdmin;
performFieldOperations: typeof performFieldOperations;
init(options: InitOptions) {
this.logger = Logger(); this.logger = Logger();
this.logger.info('Starting Payload...'); this.logger.info('Starting Payload...');
if (!options.secret) { if (!options.secret) {
throw new Error( throw new Error(
'Error: missing secret key. A secret key is needed to secure Payload.' 'Error: missing secret key. A secret key is needed to secure Payload.',
); );
} }
@@ -64,21 +99,18 @@ class Payload {
throw new Error('Error: missing MongoDB connection URL.'); throw new Error('Error: missing MongoDB connection URL.');
} }
const config = getConfig(options); this.license = options.license;
const email = { ...(config.email || {}), ...(options.email || {}) }; this.emailOptions = { ...(options.email || {}) };
this.secret = crypto
.createHash('sha256')
.update(options.secret)
.digest('hex')
.slice(0, 32);
this.config = sanitizeConfig({ this.mongoURL = options.mongoURL;
...config, this.local = options.local;
email,
license: options.license, this.config = loadConfig();
secret: crypto
.createHash('sha256')
.update(options.secret)
.digest('hex')
.slice(0, 32),
mongoURL: options.mongoURL,
local: options.local,
});
if (typeof this.config.paths === 'undefined') this.config.paths = {}; if (typeof this.config.paths === 'undefined') this.config.paths = {};
@@ -121,14 +153,14 @@ class Payload {
} }
// Configure email service // Configure email service
this.email = buildEmail(this.config.email); this.email = buildEmail(this.emailOptions);
// Initialize collections & globals // Initialize collections & globals
this.initCollections(); this.initCollections();
this.initGlobals(); this.initGlobals();
// Connect to database // Connect to database
connectMongoose(this.config.mongoURL); connectMongoose(this.mongoURL);
options.express.use((req, res, next) => { options.express.use((req, res, next) => {
req.payload = this; req.payload = this;
@@ -138,8 +170,7 @@ class Payload {
// If not initializing locally, set up HTTP routing // If not initializing locally, set up HTTP routing
if (!this.config.local) { if (!this.config.local) {
this.express = options.express; this.express = options.express;
if (this.config.rateLimit && this.config.rateLimit.trustProxy) if (this.config.rateLimit && this.config.rateLimit.trustProxy) { this.express.set('trust proxy', 1); }
this.express.set('trust proxy', 1);
this.initAdmin(); this.initAdmin();
@@ -150,7 +181,7 @@ class Payload {
this.router.use( this.router.use(
this.config.routes.graphQL, this.config.routes.graphQL,
identifyAPI('GraphQL'), identifyAPI('GraphQL'),
(req, res) => graphQLHandler.init(req, res)(req, res) (req, res) => graphQLHandler.init(req, res)(req, res),
); );
this.initGraphQLPlayground(); this.initGraphQLPlayground();

View File

@@ -1,32 +1,3 @@
// TODO: Split out init options from config types into own file
import { Express, Request } from 'express';
import { Transporter } from 'nodemailer';
import SMTPConnection from 'nodemailer/lib/smtp-connection';
import { Logger } from 'pino';
type MockEmailTransport = {
transport?: 'mock';
fromName?: string;
fromAddress?: string;
};
type ValidEmailTransport = {
transport: Transporter;
transportOptions?: SMTPConnection.Options;
fromName: string;
fromAddress: string;
};
export type PayloadEmailOptions = ValidEmailTransport | MockEmailTransport;
export type PayloadInitOptions = {
express?: Express;
mongoURL: string;
secret: string;
license?: string;
email?: PayloadEmailOptions;
local?: boolean; // I have no idea what this is
onInit?: () => void;
};
export type Document = { export type Document = {
id: string; id: string;
@@ -84,168 +55,3 @@ export type ForgotPasswordOptions = {
expiration: Date; expiration: Date;
data: any; data: any;
}; };
export type SendEmailOptions = {
from: string;
to: string;
subject: string;
html: string;
};
export type MockEmailCredentials = {
user: string;
pass: string;
web: string;
};
export type PayloadField = {
name: string;
label: string;
type:
| 'number'
| 'text'
| 'email'
| 'textarea'
| 'richText'
| 'code'
| 'radio'
| 'checkbox'
| 'date'
| 'upload'
| 'relationship'
| 'row'
| 'array'
| 'group'
| 'select'
| 'blocks';
localized?: boolean;
fields?: PayloadField[];
admin?: {
position?: string;
width?: string;
style?: Object;
};
};
export type PayloadCollectionHook = (...args: any[]) => any | void;
export type PayloadAccess = (args?: any) => boolean;
export type PayloadCollection = {
slug: string;
labels?: {
singular: string;
plural: string;
};
admin?: {
useAsTitle?: string;
defaultColumns?: string[];
components?: any;
};
hooks?: {
beforeOperation?: PayloadCollectionHook[];
beforeValidate?: PayloadCollectionHook[];
beforeChange?: PayloadCollectionHook[];
afterChange?: PayloadCollectionHook[];
beforeRead?: PayloadCollectionHook[];
afterRead?: PayloadCollectionHook[];
beforeDelete?: PayloadCollectionHook[];
afterDelete?: PayloadCollectionHook[];
};
access?: {
create?: PayloadAccess;
read?: PayloadAccess;
update?: PayloadAccess;
delete?: PayloadAccess;
admin?: PayloadAccess;
};
auth?: {
tokenExpiration?: number;
verify?:
| boolean
| { generateEmailHTML: string; generateEmailSubject: string };
maxLoginAttempts?: number;
lockTime?: number;
useAPIKey?: boolean;
cookies?:
| {
secure?: boolean;
sameSite?: string;
domain?: string | undefined;
}
| boolean;
};
fields: PayloadField[];
};
export type PayloadGlobal = {
slug: string;
label: string;
access?: {
create?: PayloadAccess;
read?: PayloadAccess;
update?: PayloadAccess;
delete?: PayloadAccess;
admin?: PayloadAccess;
};
fields: PayloadField[];
};
export type PayloadConfig = {
admin?: {
user?: string;
meta?: {
titleSuffix?: string;
ogImage?: string;
favicon?: string;
};
disable?: boolean;
};
collections?: PayloadCollection[];
globals?: PayloadGlobal[];
serverURL?: string;
cookiePrefix?: string;
csrf?: string[];
cors?: string[];
publicENV: { [key: string]: string };
routes?: {
api?: string;
admin?: string;
graphQL?: string;
graphQLPlayground?: string;
};
email?: PayloadEmailOptions;
local?: boolean;
defaultDepth?: number;
maxDepth?: number;
rateLimit?: {
window?: number;
max?: number;
trustProxy?: boolean;
skip?: (req: Request) => boolean; // TODO: Type join Request w/ PayloadRequest
};
upload?: {
limits?: {
fileSize?: number;
};
};
localization?: {
locales: string[];
};
defaultLocale?: string;
fallback?: boolean;
graphQL?: {
mutations?: Object;
queries?: Object;
maxComplexity?: number;
disablePlaygroundInProduction?: boolean;
};
components: { [key: string]: JSX.Element | (() => JSX.Element) };
paths?: { [key: string]: string };
hooks?: {
afterError?: () => void;
};
webpack?: (config: any) => any;
serverModules?: string[];
};
export type PayloadLogger = Logger;

View File

@@ -2,7 +2,6 @@ import path from 'path';
import MiniCSSExtractPlugin from 'mini-css-extract-plugin'; import MiniCSSExtractPlugin from 'mini-css-extract-plugin';
import TerserJSPlugin from 'terser-webpack-plugin'; import TerserJSPlugin from 'terser-webpack-plugin';
import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin'; import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin';
import babelConfig from '../babel.config';
export default { export default {
entry: { entry: {
@@ -31,7 +30,6 @@ export default {
exclude: /node_modules/, exclude: /node_modules/,
use: { use: {
loader: 'babel-loader', loader: 'babel-loader',
options: babelConfig,
}, },
}, },
{ {

View File

@@ -1,28 +0,0 @@
export default (cssOptions, preProcessor) => {
const loaders = [
'isomorphic-style-loader',
{
loader: require.resolve('css-loader'),
options: cssOptions,
},
{
// Options for PostCSS as we reference these options twice
// Adds vendor prefixing based on your specified browser support in
// package.json
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebook/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env'),
],
},
},
];
if (preProcessor) {
loaders.push(require.resolve(preProcessor));
}
return loaders;
};

View File

@@ -2,7 +2,6 @@ import HtmlWebpackPlugin from 'html-webpack-plugin';
import path from 'path'; import path from 'path';
import webpack from 'webpack'; import webpack from 'webpack';
import MiniCSSExtractPlugin from 'mini-css-extract-plugin'; import MiniCSSExtractPlugin from 'mini-css-extract-plugin';
import babelConfig from '../babel.config';
const mockModulePath = path.resolve(__dirname, '../mocks/emptyModule.js'); const mockModulePath = path.resolve(__dirname, '../mocks/emptyModule.js');
@@ -10,19 +9,19 @@ export default (config) => {
let webpackConfig = { let webpackConfig = {
entry: { entry: {
main: [ main: [
"webpack-hot-middleware/client", 'webpack-hot-middleware/client',
path.resolve(__dirname, "../admin/index.tsx"), path.resolve(__dirname, '../admin/index.tsx'),
], ],
}, },
output: { output: {
path: "/", path: '/',
publicPath: config.routes.admin, publicPath: config.routes.admin,
filename: "[name].js", filename: '[name].js',
}, },
devtool: "inline-cheap-source-map", devtool: 'inline-cheap-source-map',
mode: "development", mode: 'development',
resolveLoader: { resolveLoader: {
modules: ["node_modules", path.join(__dirname, "../../node_modules")], modules: ['node_modules', path.join(__dirname, '../../node_modules')],
}, },
module: { module: {
rules: [ rules: [
@@ -31,8 +30,7 @@ export default (config) => {
exclude: /node_modules[\\/](?!(@payloadcms[\\/]payload)[\\/]).*/, exclude: /node_modules[\\/](?!(@payloadcms[\\/]payload)[\\/]).*/,
use: [ use: [
{ {
loader: "babel-loader", loader: 'babel-loader',
options: babelConfig,
}, },
], ],
}, },
@@ -40,33 +38,33 @@ export default (config) => {
oneOf: [ oneOf: [
{ {
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/], test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve("url-loader"), loader: require.resolve('url-loader'),
options: { options: {
limit: 10000, limit: 10000,
name: "static/media/[name].[hash:8].[ext]", name: 'static/media/[name].[hash:8].[ext]',
}, },
}, },
{ {
test: /\.(sa|sc|c)ss$/, test: /\.(sa|sc|c)ss$/,
use: [ use: [
MiniCSSExtractPlugin.loader, MiniCSSExtractPlugin.loader,
"css-loader", 'css-loader',
{ {
loader: "postcss-loader", loader: 'postcss-loader',
options: { options: {
postcssOptions: { postcssOptions: {
plugins: ["postcss-preset-env"], plugins: ['postcss-preset-env'],
}, },
}, },
}, },
"sass-loader", 'sass-loader',
], ],
}, },
{ {
exclude: [/\.((t|j)s|(t|j)sx|mjs)$/, /\.html$/, /\.json$/], exclude: [/\.((t|j)s|(t|j)sx|mjs)$/, /\.html$/, /\.json$/],
loader: require.resolve("file-loader"), loader: require.resolve('file-loader'),
options: { options: {
name: "static/media/[name].[hash:8].[ext]", name: 'static/media/[name].[hash:8].[ext]',
}, },
}, },
], ],
@@ -74,12 +72,12 @@ export default (config) => {
], ],
}, },
resolve: { resolve: {
modules: ["node_modules", path.resolve(__dirname, "../../node_modules")], modules: ['node_modules', path.resolve(__dirname, '../../node_modules')],
alias: { alias: {
"payload/unsanitizedConfig": config.paths.config, 'payload/unsanitizedConfig': config.paths.config,
"@payloadcms/payload$": mockModulePath, '@payloadcms/payload$': mockModulePath,
}, },
extensions: [".ts", ".tsx", ".js", ".json"], extensions: ['.ts', '.tsx', '.js', '.json'],
}, },
plugins: [ plugins: [
new webpack.DefinePlugin( new webpack.DefinePlugin(
@@ -88,15 +86,15 @@ export default (config) => {
...values, ...values,
[`process.env.${key}`]: `'${val}'`, [`process.env.${key}`]: `'${val}'`,
}), }),
{} {},
) ),
), ),
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
template: template:
config.admin && config.admin.indexHTML config.admin && config.admin.indexHTML
? path.join(config.paths.configDir, config.admin.indexHTML) ? path.join(config.paths.configDir, config.admin.indexHTML)
: path.resolve(__dirname, "../admin/index.html"), : path.resolve(__dirname, '../admin/index.html'),
filename: "./index.html", filename: './index.html',
}), }),
new webpack.HotModuleReplacementPlugin(), new webpack.HotModuleReplacementPlugin(),
new MiniCSSExtractPlugin({ new MiniCSSExtractPlugin({

View File

@@ -5,7 +5,6 @@ import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import path from 'path'; import path from 'path';
import webpack from 'webpack'; import webpack from 'webpack';
import babelConfig from '../babel.config';
const mockModulePath = path.resolve(__dirname, '../mocks/emptyModule.js'); const mockModulePath = path.resolve(__dirname, '../mocks/emptyModule.js');
@@ -44,7 +43,6 @@ export default (config) => {
exclude: /node_modules[\\/](?!(@payloadcms[\\/]payload)[\\/]).*/, exclude: /node_modules[\\/](?!(@payloadcms[\\/]payload)[\\/]).*/,
use: { use: {
loader: 'babel-loader', loader: 'babel-loader',
options: babelConfig,
}, },
}, },
{ {