diff --git a/demo/server.js b/demo/server.js index 6c98a740b9..d229ff6447 100644 --- a/demo/server.js +++ b/demo/server.js @@ -33,7 +33,7 @@ expressApp.use('/external-route', externalRouter); exports.start = (cb) => { const server = expressApp.listen(3000, async () => { - logger.info(`listening on ${3000}...`); + payload.logger.info(`listening on ${3000}...`); if (cb) cb(); }); diff --git a/logger.js b/logger.js deleted file mode 100644 index a377aaef07..0000000000 --- a/logger.js +++ /dev/null @@ -1,3 +0,0 @@ -const logger = require('./dist/utilities/logger'); - -module.exports = logger; diff --git a/package.json b/package.json index 59fb906400..1356cc7b74 100644 --- a/package.json +++ b/package.json @@ -197,6 +197,7 @@ "@types/webpack-hot-middleware": "^2.25.3", "@typescript-eslint/eslint-plugin": "^4.8.1", "babel-eslint": "^10.0.1", + "babel-plugin-ignore-html-and-css-imports": "^0.1.0", "cross-env": "^7.0.2", "eslint": "^6.8.0", "eslint-plugin-import": "^2.20.0", diff --git a/src/auth/operations/login.ts b/src/auth/operations/login.ts index b899dbc96c..49444f47ac 100644 --- a/src/auth/operations/login.ts +++ b/src/auth/operations/login.ts @@ -1,8 +1,8 @@ -const jwt = require('jsonwebtoken'); -const { AuthenticationError, LockedAuth } = require('../../errors'); -const getCookieExpiration = require('../../utilities/getCookieExpiration'); -const isLocked = require('../isLocked'); -const removeInternalFields = require('../../utilities/removeInternalFields'); +import jwt from 'jsonwebtoken'; +import { AuthenticationError, LockedAuth } from '../../errors'; +import getCookieExpiration from '../../utilities/getCookieExpiration'; +import isLocked from '../isLocked'; +import removeInternalFields from '../../utilities/removeInternalFields'; async function login(incomingArgs) { const { config, operations } = this; diff --git a/src/dev.js b/src/dev.js index c7c2f831ce..11a911df88 100644 --- a/src/dev.js +++ b/src/dev.js @@ -6,6 +6,15 @@ require('@babel/register')({ /node_modules[\\/](?!@payloadcms[\\/]payload[\\/]src[\\/]admin|@payloadcms[\\/]payload[\\/]components|@payloadcms[\\/]payload[\\/]hooks).*/, ], extensions: ['.js', '.jsx', '.ts', '.tsx'], + plugins: [ + [ + 'babel-plugin-ignore-html-and-css-imports', + { + removeExtensions: ['.svg', '.css', '.scss', '.png', '.jpg'], + }, + ], + ...babelConfig.plugins, + ], }); const payload = require('./index.ts'); diff --git a/src/email/build.ts b/src/email/build.ts index 1ede392473..34dc12c14e 100644 --- a/src/email/build.ts +++ b/src/email/build.ts @@ -1,18 +1,15 @@ import nodemailer from 'nodemailer'; -import logger from '../utilities/logger'; import mockHandler from './mockHandler'; -logger(); - async function buildEmail() { if (!this.config.email.transport || this.config.email.transport === 'mock') { - logger.info('E-mail configured with mock configuration'); + this.logger.info('E-mail configured with mock configuration'); const mockAccount = await mockHandler(this.config.email); if (this.config.email.transport === 'mock') { const { account: { web, user, pass } } = mockAccount; - logger.info(`Log into mock email provider at ${web}`); - logger.info(`Mock email account username: ${user}`); - logger.info(`Mock email account password: ${pass}`); + this.logger.info(`Log into mock email provider at ${web}`); + this.logger.info(`Mock email account username: ${user}`); + this.logger.info(`Mock email account password: ${pass}`); } return mockAccount; } @@ -30,7 +27,7 @@ async function buildEmail() { try { await email.transport.verify(); } catch (err) { - logger.error('There is an error with the email configuration you have provided.', err); + this.logger.error('There is an error with the email configuration you have provided.', err); } return email; diff --git a/src/errors/LockedAuth.ts b/src/errors/LockedAuth.ts index 27bc1d2da9..d0ab8e8551 100644 --- a/src/errors/LockedAuth.ts +++ b/src/errors/LockedAuth.ts @@ -7,4 +7,4 @@ class LockedAuth extends APIError { } } -module.exports = LockedAuth; +export default LockedAuth; diff --git a/src/errors/MissingCollectionLabel.ts b/src/errors/MissingCollectionLabel.ts index 115b4571cc..3d742a3064 100644 --- a/src/errors/MissingCollectionLabel.ts +++ b/src/errors/MissingCollectionLabel.ts @@ -6,4 +6,4 @@ class MissingCollectionLabel extends APIError { } } -module.exports = MissingCollectionLabel; +export default MissingCollectionLabel; diff --git a/src/errors/MissingFieldInputOptions.ts b/src/errors/MissingFieldInputOptions.ts index f761cc2daf..ec22f55a0a 100644 --- a/src/errors/MissingFieldInputOptions.ts +++ b/src/errors/MissingFieldInputOptions.ts @@ -6,4 +6,4 @@ class MissingFieldInputOptions extends APIError { } } -module.exports = MissingFieldInputOptions; +export default MissingFieldInputOptions; diff --git a/src/errors/MissingFieldType.ts b/src/errors/MissingFieldType.ts index 8f52ce532d..5593faae6c 100644 --- a/src/errors/MissingFieldType.ts +++ b/src/errors/MissingFieldType.ts @@ -6,4 +6,4 @@ class MissingFieldType extends APIError { } } -module.exports = MissingFieldType; +export default MissingFieldType; diff --git a/src/errors/MissingFile.ts b/src/errors/MissingFile.ts index fffbfd5230..d9694bbca8 100644 --- a/src/errors/MissingFile.ts +++ b/src/errors/MissingFile.ts @@ -7,4 +7,4 @@ class MissingFile extends APIError { } } -module.exports = MissingFile; +export default MissingFile; diff --git a/src/errors/MissingGlobalLabel.ts b/src/errors/MissingGlobalLabel.ts index 25ffcd39d2..12836cbdf4 100644 --- a/src/errors/MissingGlobalLabel.ts +++ b/src/errors/MissingGlobalLabel.ts @@ -6,4 +6,4 @@ class MissingGlobalLabel extends APIError { } } -module.exports = MissingGlobalLabel; +export default MissingGlobalLabel; diff --git a/src/errors/NotFound.ts b/src/errors/NotFound.ts index 9da977cf19..8dfb78cfb9 100644 --- a/src/errors/NotFound.ts +++ b/src/errors/NotFound.ts @@ -7,4 +7,4 @@ class NotFound extends APIError { } } -module.exports = NotFound; +export default NotFound; diff --git a/src/errors/ValidationError.ts b/src/errors/ValidationError.ts index 316aca3782..3c16173c05 100644 --- a/src/errors/ValidationError.ts +++ b/src/errors/ValidationError.ts @@ -7,4 +7,4 @@ class ValidationError extends APIError { } } -module.exports = ValidationError; +export default ValidationError; diff --git a/src/express/middleware/errorHandler.ts b/src/express/middleware/errorHandler.ts index f74d28d04b..24ad3c9a3c 100644 --- a/src/express/middleware/errorHandler.ts +++ b/src/express/middleware/errorHandler.ts @@ -1,15 +1,13 @@ import httpStatus from 'http-status'; import formatErrorResponse from '../responses/formatError'; -import logger from '../../utilities/logger'; -logger(); - -const errorHandler = (config) => async (err, req, res, next) => { +const errorHandler = (payload) => async (err, req, res, next) => { + const { config } = payload; const data = formatErrorResponse(err); let response; let status = err.status || httpStatus.INTERNAL_SERVER_ERROR; - logger.error(err.stack); + payload.logger.error(err.stack); if (config.debug && config.debug === true) { data.stack = err.stack; @@ -26,8 +24,7 @@ const errorHandler = (config) => async (err, req, res, next) => { ({ response, status } = await config.hooks.afterError(err, response) || { response, status }); } - res.status(status) - .send(response); + res.status(status).send(response); }; export default errorHandler; diff --git a/src/fields/baseFields/baseFields.ts b/src/fields/baseFields/baseFields.ts index 1c69072a21..ad6033ba2a 100644 --- a/src/fields/baseFields/baseFields.ts +++ b/src/fields/baseFields/baseFields.ts @@ -1,11 +1,11 @@ -import validations from '../validations'; +import { email } from '../validations'; export default [ { name: 'email', label: 'Email', type: 'email', - validate: validations.email, + validate: email, admin: { disabled: true, }, diff --git a/src/fields/validations.ts b/src/fields/validations.ts index 0524e85008..06b871c3fc 100644 --- a/src/fields/validations.ts +++ b/src/fields/validations.ts @@ -1,188 +1,218 @@ -const defaultRichTextValue = require('./richText/defaultValue'); +import defaultRichTextValue from './richText/defaultValue'; const defaultMessage = 'This field is required.'; -const optionsToValidatorMap = { - number: (value, options = {}) => { - const parsedValue = parseInt(value, 10); +export const number = (value, options = {}) => { + const parsedValue = parseInt(value, 10); - if ((value && typeof parsedValue !== 'number') || (options.required && Number.isNaN(parsedValue))) { - return 'Please enter a valid number.'; - } + if ((value && typeof parsedValue !== 'number') || (options.required && Number.isNaN(parsedValue))) { + return 'Please enter a valid number.'; + } - if (options.max && parsedValue > options.max) { - return `"${value}" is greater than the max allowed value of ${options.max}.`; - } + if (options.max && parsedValue > options.max) { + return `"${value}" is greater than the max allowed value of ${options.max}.`; + } - if (options.min && parsedValue < options.min) { - return `"${value}" is less than the min allowed value of ${options.min}.`; - } + if (options.min && parsedValue < options.min) { + return `"${value}" is less than the min allowed value of ${options.min}.`; + } - if (options.required && typeof parsedValue !== 'number') { - return defaultMessage; - } - - return true; - }, - text: (value, options = {}) => { - if (options.maxLength && (value && value.length > options.maxLength)) { - return `This value must be shorter than the max length of ${options.max} characters.`; - } - - if (options.minLength && (value && value.length < options.minLength)) { - return `This value must be longer than the minimum length of ${options.max} characters.`; - } - - if (options.required) { - if (typeof value !== 'string' || (typeof value === 'string' && value.length === 0)) { - return defaultMessage; - } - } - - return true; - }, - password: (value, options = {}) => { - if (options.maxLength && value.length > options.maxLength) { - return `This value must be shorter than the max length of ${options.max} characters.`; - } - - if (options.minLength && value.length < options.minLength) { - return `This value must be longer than the minimum length of ${options.max} characters.`; - } - - if (options.required && !value) { - return defaultMessage; - } - - return true; - }, - email: (value, options = {}) => { - if ((value && !/\S+@\S+\.\S+/.test(value)) - || (!value && options.required)) { - return 'Please enter a valid email address.'; - } - - return true; - }, - textarea: (value, options = {}) => { - if (options.maxLength && value.length > options.maxLength) { - return `This value must be shorter than the max length of ${options.max} characters.`; - } - - if (options.minLength && value.length < options.minLength) { - return `This value must be longer than the minimum length of ${options.max} characters.`; - } - - if (options.required && !value) { - return defaultMessage; - } - - return true; - }, - wysiwyg: (value, options = {}) => { - if (options.required && !value) { - return defaultMessage; - } - - return true; - }, - code: (value, options = {}) => { - if (options.required && value === undefined) { - return defaultMessage; - } - - return true; - }, - richText: (value, options) => { - if (options.required) { - const stringifiedDefaultValue = JSON.stringify(defaultRichTextValue); - if (value && JSON.stringify(value) !== stringifiedDefaultValue) return true; - return 'This field is required.'; - } - - return true; - }, - checkbox: (value, options = {}) => { - if ((value && typeof value !== 'boolean') - || (options.required && typeof value !== 'boolean')) { - return 'This field can only be equal to true or false.'; - } - - return true; - }, - date: (value, options = {}) => { - if (value && !isNaN(Date.parse(value.toString()))) { /* eslint-disable-line */ - return true; - } - - if (value) { - return `"${value}" is not a valid date.`; - } - - if (options.required) { - return defaultMessage; - } - - return true; - }, - upload: (value, options = {}) => { - if (value || !options.required) return true; + if (options.required && typeof parsedValue !== 'number') { return defaultMessage; - }, - relationship: (value, options = {}) => { - if (value || !options.required) return true; - return defaultMessage; - }, - array: (value, options = {}) => { - if (options.minRows && value < options.minRows) { - return `This field requires at least ${options.minRows} row(s).`; - } + } - if (options.maxRows && value > options.maxRows) { - return `This field requires no more than ${options.maxRows} row(s).`; - } - - if (!value && options.required) { - return 'This field requires at least one row.'; - } - - return true; - }, - select: (value, options = {}) => { - if (Array.isArray(value) && value.find((input) => !options.options.find((option) => (option === input || option.value === input)))) { - return 'This field has an invalid selection'; - } - - if (typeof value === 'string' && !options.options.find((option) => (option === value || option.value === value))) { - return 'This field has an invalid selection'; - } - - if (options.required && !value) { - return defaultMessage; - } - - return true; - }, - radio: (value, options = {}) => { - const stringValue = String(value); - if ((typeof value !== 'undefined' || !options.required) && (options.options.find((option) => String(option.value) === stringValue))) return true; - return defaultMessage; - }, - blocks: (value, options) => { - if (options.minRows && value < options.minRows) { - return `This field requires at least ${options.minRows} row(s).`; - } - - if (options.maxRows && value > options.maxRows) { - return `This field requires no more than ${options.maxRows} row(s).`; - } - - if (!value && options.required) { - return 'This field requires at least one row.'; - } - - return true; - }, + return true; }; -export default optionsToValidatorMap; +export const text = (value, options = {}) => { + if (options.maxLength && (value && value.length > options.maxLength)) { + return `This value must be shorter than the max length of ${options.max} characters.`; + } + + if (options.minLength && (value && value.length < options.minLength)) { + return `This value must be longer than the minimum length of ${options.max} characters.`; + } + + if (options.required) { + if (typeof value !== 'string' || (typeof value === 'string' && value.length === 0)) { + return defaultMessage; + } + } + + return true; +}; + +export const password = (value, options = {}) => { + if (options.maxLength && value.length > options.maxLength) { + return `This value must be shorter than the max length of ${options.max} characters.`; + } + + if (options.minLength && value.length < options.minLength) { + return `This value must be longer than the minimum length of ${options.max} characters.`; + } + + if (options.required && !value) { + return defaultMessage; + } + + return true; +}; + +export const email = (value, options = {}) => { + if ((value && !/\S+@\S+\.\S+/.test(value)) + || (!value && options.required)) { + return 'Please enter a valid email address.'; + } + + return true; +}; + +export const textarea = (value, options = {}) => { + if (options.maxLength && value.length > options.maxLength) { + return `This value must be shorter than the max length of ${options.max} characters.`; + } + + if (options.minLength && value.length < options.minLength) { + return `This value must be longer than the minimum length of ${options.max} characters.`; + } + + if (options.required && !value) { + return defaultMessage; + } + + return true; +}; + +export const wysiwyg = (value, options = {}) => { + if (options.required && !value) { + return defaultMessage; + } + + return true; +}; + +export const code = (value, options = {}) => { + if (options.required && value === undefined) { + return defaultMessage; + } + + return true; +}; + +export const richText = (value, options) => { + if (options.required) { + const stringifiedDefaultValue = JSON.stringify(defaultRichTextValue); + if (value && JSON.stringify(value) !== stringifiedDefaultValue) return true; + return 'This field is required.'; + } + + return true; +}; + +export const checkbox = (value, options = {}) => { + if ((value && typeof value !== 'boolean') + || (options.required && typeof value !== 'boolean')) { + return 'This field can only be equal to true or false.'; + } + + return true; +}; + +export const date = (value, options = {}) => { + if (value && !isNaN(Date.parse(value.toString()))) { /* eslint-disable-line */ + return true; + } + + if (value) { + return `"${value}" is not a valid date.`; + } + + if (options.required) { + return defaultMessage; + } + + return true; +}; + +export const upload = (value, options = {}) => { + if (value || !options.required) return true; + return defaultMessage; +}; + +export const relationship = (value, options = {}) => { + if (value || !options.required) return true; + return defaultMessage; +}; + +export const array = (value, options = {}) => { + if (options.minRows && value < options.minRows) { + return `This field requires at least ${options.minRows} row(s).`; + } + + if (options.maxRows && value > options.maxRows) { + return `This field requires no more than ${options.maxRows} row(s).`; + } + + if (!value && options.required) { + return 'This field requires at least one row.'; + } + + return true; +}; + +export const select = (value, options = {}) => { + if (Array.isArray(value) && value.find((input) => !options.options.find((option) => (option === input || option.value === input)))) { + return 'This field has an invalid selection'; + } + + if (typeof value === 'string' && !options.options.find((option) => (option === value || option.value === value))) { + return 'This field has an invalid selection'; + } + + if (options.required && !value) { + return defaultMessage; + } + + return true; +}; + +export const radio = (value, options = {}) => { + const stringValue = String(value); + if ((typeof value !== 'undefined' || !options.required) && (options.options.find((option) => String(option.value) === stringValue))) return true; + return defaultMessage; +}; + +export const blocks = (value, options) => { + if (options.minRows && value < options.minRows) { + return `This field requires at least ${options.minRows} row(s).`; + } + + if (options.maxRows && value > options.maxRows) { + return `This field requires no more than ${options.maxRows} row(s).`; + } + + if (!value && options.required) { + return 'This field requires at least one row.'; + } + + return true; +}; + +export default { + number, + text, + password, + email, + textarea, + code, + wysiwyg, + richText, + checkbox, + date, + upload, + relationship, + array, + select, + radio, + blocks, +}; diff --git a/src/graphql/index.ts b/src/graphql/index.ts index dc70d7868c..6411a15997 100644 --- a/src/graphql/index.ts +++ b/src/graphql/index.ts @@ -1,9 +1,6 @@ import GraphQL, { GraphQLObjectType, GraphQLSchema } from 'graphql'; -import queryComplexity, { - simpleEstimator, - fieldExtensionsEstimator, -} from 'graphql-query-complexity'; import graphQLHTTP from 'express-graphql'; +import queryComplexity, { simpleEstimator, fieldExtensionsEstimator } from 'graphql-query-complexity'; import buildObjectType from './schema/buildObjectType'; import buildMutationInputType from './schema/buildMutationInputType'; import errorHandler from './errorHandler'; diff --git a/src/index.ts b/src/index.ts index 51c0c8bad3..971ffd3036 100644 --- a/src/index.ts +++ b/src/index.ts @@ -136,7 +136,7 @@ class Payload { // Enable static routes for all collections permitting upload this.initStatic(); - this.errorHandler = errorHandler(this.config); + this.errorHandler = errorHandler(this); this.router.use(this.errorHandler); this.authenticate = authenticate(this.config); diff --git a/src/utilities/getConfig.ts b/src/utilities/getConfig.ts index 04350d26b3..5cbbe7c46c 100644 --- a/src/utilities/getConfig.ts +++ b/src/utilities/getConfig.ts @@ -3,7 +3,6 @@ import path from 'path'; import findConfig from './findConfig'; - const configPath = findConfig(); const getConfig = () => { // eslint-disable-next-line @typescript-eslint/no-var-requires diff --git a/tsconfig.json b/tsconfig.json index f52a245a9c..01c6283596 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,7 @@ "jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ "declaration": true, /* Generates corresponding '.d.ts' file. */ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - "sourceMap": true, /* Generates corresponding '.map' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ "outDir": "./dist", /* Redirect output structure to the directory. */ "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ diff --git a/yarn.lock b/yarn.lock index b05d8335a7..7e189abfd7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3048,6 +3048,11 @@ babel-plugin-emotion@^10.0.27: find-root "^1.1.0" source-map "^0.5.7" +babel-plugin-ignore-html-and-css-imports@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/babel-plugin-ignore-html-and-css-imports/-/babel-plugin-ignore-html-and-css-imports-0.1.0.tgz#a164a607c0424e6f7c2639f79e96782d6b3170a7" + integrity sha512-1ayvecgwu98HyLVNe8iAR+cE7B+MBwEsNEJOJoARxzxtq6p0qCBULAQtuDS5/WH+bmxkjRg+uE3w+QSgkElKCw== + babel-plugin-istanbul@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765"