From c58b71dd5a869b4b6f0af5f0ff8079e8f757577f Mon Sep 17 00:00:00 2001 From: James Date: Tue, 9 Jun 2020 17:13:39 -0400 Subject: [PATCH 01/14] adds PoC for how to detect autofill events in Chrome --- src/client/components/forms/Form/index.js | 2 ++ .../forms/field-types/Email/index.js | 32 +++++++++++++++++-- .../components/forms/field-types/shared.scss | 29 +++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/client/components/forms/Form/index.js b/src/client/components/forms/Form/index.js index 464a4dacda..f6acfff278 100644 --- a/src/client/components/forms/Form/index.js +++ b/src/client/components/forms/Form/index.js @@ -243,6 +243,8 @@ const Form = (props) => { baseClass, ].filter(Boolean).join(' '); + console.log(fields); + return (
{ const path = pathFromProps || name; + const ref = useRef(null); + const [autofilled, setAutofilled] = useState(null); + const { value, showError, - processing, setValue, errorMessage, } = useFieldType({ @@ -47,6 +51,29 @@ const Email = (props) => { readOnly && 'read-only', ].filter(Boolean).join(' '); + const autofillListener = useCallback((e) => { + switch (e?.animationName) { + case 'onAutoFillStart': + return setAutofilled(true); + + default: + return setAutofilled(false); + } + }, []); + + useEffect(() => { + const input = ref.current; + input.addEventListener('animationstart', autofillListener); + + return () => input.removeEventListener('animationstart', autofillListener); + }, [autofillListener]); + + useEffect(() => { + if (autofilled) { + setValue(value); + } + }, [autofilled, value, setValue]); + return (
{ id={path} name={path} autoComplete={autoComplete} + ref={ref} />
); diff --git a/src/client/components/forms/field-types/shared.scss b/src/client/components/forms/field-types/shared.scss index c861754e91..693267c1bf 100644 --- a/src/client/components/forms/field-types/shared.scss +++ b/src/client/components/forms/field-types/shared.scss @@ -36,6 +36,35 @@ color: $color-gray; } + // Autofill event polyfill + // Thanks, Klarna: + // https://stackoverflow.com/questions/11708092/detecting-browser-autofill/11710295#11710295 + + @keyframes onAutoFillStart { + from {/**/} + to {/**/} + } + + @keyframes onAutoFillCancel { + from {/**/} + to {/**/} + } + + &:-internal-autofill-selected { + // Expose a hook for JavaScript when auto fill is shown. + // JavaScript can capture 'animationstart' events + animation-name: onAutoFillStart; + + // Make the backgound color become yellow _really slowly_ + transition: background-color 50000s ease-in-out 0s; + } + + &:not(:-internal-autofill-selected) { + // Expose a hook for JS onAutoFillCancel + // JavaScript can capture 'animationstart' events + animation-name: onAutoFillCancel; + } + @include small-break { margin-bottom: base(.5); } From c2d0548be176f1ea21741ee4290207336622f487 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 9 Jun 2020 17:14:10 -0400 Subject: [PATCH 02/14] fixes jims negligence --- src/client/components/forms/Form/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/client/components/forms/Form/index.js b/src/client/components/forms/Form/index.js index f6acfff278..464a4dacda 100644 --- a/src/client/components/forms/Form/index.js +++ b/src/client/components/forms/Form/index.js @@ -243,8 +243,6 @@ const Form = (props) => { baseClass, ].filter(Boolean).join(' '); - console.log(fields); - return ( Date: Wed, 10 Jun 2020 09:23:12 -0400 Subject: [PATCH 03/14] fixes bug with registration policies --- src/auth/operations/register.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/operations/register.js b/src/auth/operations/register.js index 844c311873..174ebf61c2 100644 --- a/src/auth/operations/register.js +++ b/src/auth/operations/register.js @@ -10,7 +10,7 @@ const register = async (args) => { // ///////////////////////////////////// if (!args.overridePolicy) { - await executePolicy(args, args.collection.config.policies.register); + await executePolicy(args, args.collection.config.policies.create); } let options = { ...args }; From 81c7bde0ed3951fa87170d3c237ee5faa917e21d Mon Sep 17 00:00:00 2001 From: Jarrod Flesch Date: Thu, 11 Jun 2020 09:28:32 -0400 Subject: [PATCH 04/14] adds radio group field type - #224 --- demo/collections/AllFields.js | 17 +++ .../RadioGroup/RadioInput/index.js | 48 ++++++++ .../RadioGroup/RadioInput/index.scss | 45 +++++++ .../forms/field-types/RadioGroup/index.js | 115 ++++++++++++++++++ .../forms/field-types/RadioGroup/index.scss | 0 .../components/forms/field-types/index.js | 1 + src/fields/validations.js | 4 + src/graphql/schema/buildMutationInputType.js | 1 + src/graphql/schema/buildObjectType.js | 1 + src/graphql/schema/buildWhereInputType.js | 11 ++ src/mongoose/buildSchema.js | 6 + 11 files changed, 249 insertions(+) create mode 100644 src/client/components/forms/field-types/RadioGroup/RadioInput/index.js create mode 100644 src/client/components/forms/field-types/RadioGroup/RadioInput/index.scss create mode 100644 src/client/components/forms/field-types/RadioGroup/index.js create mode 100644 src/client/components/forms/field-types/RadioGroup/index.scss diff --git a/demo/collections/AllFields.js b/demo/collections/AllFields.js index 93d025d9e7..c16bad413e 100644 --- a/demo/collections/AllFields.js +++ b/demo/collections/AllFields.js @@ -65,6 +65,23 @@ const AllFields = { required: true, hasMany: true, }, + { + name: 'radioGroupExample', + label: 'Radio Group Example', + type: 'radioGroup', + options: [{ + value: 'option-1', + label: 'Options 1 Value', + }, { + value: 'option-2', + label: 'Option 2 Label', + }, { + value: 'option-3', + label: 'Option 3 Label', + }], + defaultValue: 'option-2', + required: true, + }, { name: 'checkbox', type: 'checkbox', diff --git a/src/client/components/forms/field-types/RadioGroup/RadioInput/index.js b/src/client/components/forms/field-types/RadioGroup/RadioInput/index.js new file mode 100644 index 0000000000..86e0d50866 --- /dev/null +++ b/src/client/components/forms/field-types/RadioGroup/RadioInput/index.js @@ -0,0 +1,48 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import './index.scss'; + +const baseClass = 'radio-input'; + +const RadioInput = (props) => { + const { isSelected, option, onChange } = props; + + const classes = [ + baseClass, + isSelected && `${baseClass}--is-selected`, + ].filter(Boolean).join(' '); + + return ( + + ); +}; + +RadioInput.defaultProps = { + isSelected: false, +}; + +RadioInput.propTypes = { + isSelected: PropTypes.bool, + option: PropTypes.shape({ + label: PropTypes.string, + value: PropTypes.string, + }).isRequired, + onChange: PropTypes.func.isRequired, +}; + +export default RadioInput; diff --git a/src/client/components/forms/field-types/RadioGroup/RadioInput/index.scss b/src/client/components/forms/field-types/RadioGroup/RadioInput/index.scss new file mode 100644 index 0000000000..d55899f5e5 --- /dev/null +++ b/src/client/components/forms/field-types/RadioGroup/RadioInput/index.scss @@ -0,0 +1,45 @@ +@import '../../../../../scss/styles'; + +.radio-input { + display: flex; + align-items: center; + cursor: pointer; + margin: base(.10) 0; + + input[type=radio] { + display: none; + } + + &__styled-radio { + display: inline-block; + width: 18px; + height: 18px; + border-radius: 50%; + background-color: white; + box-shadow: + inset 0px 0px 0px 1px $color-dark-gray, + inset 0px 0px 0px 4px white; + } + + &__label { + margin-left: base(.5); + } + + &--is-selected { + .radio-input { + &__styled-radio { + background-color: $color-dark-gray; + } + } + } + + &:not(&--is-selected) { + &:hover { + .radio-input { + &__styled-radio { + background-color: rgba($color-dark-gray, .4); + } + } + } + } +} diff --git a/src/client/components/forms/field-types/RadioGroup/index.js b/src/client/components/forms/field-types/RadioGroup/index.js new file mode 100644 index 0000000000..e226de410f --- /dev/null +++ b/src/client/components/forms/field-types/RadioGroup/index.js @@ -0,0 +1,115 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import useFieldType from '../../useFieldType'; +import withCondition from '../../withCondition'; +import Error from '../../Error'; +import Label from '../../Label'; +import RadioInput from './RadioInput'; +import { radioGroup } from '../../../../../fields/validations'; + +import './index.scss'; + +const RadioGroup = (props) => { + const { + name, + path: pathFromProps, + required, + defaultValue, + initialData, + validate, + style, + width, + label, + readOnly, + options, + } = props; + + const path = pathFromProps || name; + + const { + value, + showError, + errorMessage, + setValue, + formProcessing, + } = useFieldType({ + path, + required, + initialData, + defaultValue, + validate, + }); + + const classes = [ + 'field-type', + 'radio-group', + showError && 'error', + readOnly && 'read-only', + ].filter(Boolean).join(' '); + + return ( +
+ +
+ ); +}; + +RadioGroup.defaultProps = { + label: null, + required: false, + readOnly: false, + defaultValue: null, + initialData: false, + validate: radioGroup, + width: undefined, + style: {}, + path: '', +}; + +RadioGroup.propTypes = { + path: PropTypes.string, + name: PropTypes.string.isRequired, + readOnly: PropTypes.bool, + required: PropTypes.bool, + defaultValue: PropTypes.string, + initialData: PropTypes.bool, + validate: PropTypes.func, + width: PropTypes.string, + style: PropTypes.shape({}), + label: PropTypes.string, + options: PropTypes.arrayOf( + PropTypes.shape({ + label: PropTypes.string, + value: PropTypes.string, + }), + ).isRequired, +}; + +export default withCondition(RadioGroup); diff --git a/src/client/components/forms/field-types/RadioGroup/index.scss b/src/client/components/forms/field-types/RadioGroup/index.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/client/components/forms/field-types/index.js b/src/client/components/forms/field-types/index.js index 22cc4e48f3..d9ae0bbe16 100644 --- a/src/client/components/forms/field-types/index.js +++ b/src/client/components/forms/field-types/index.js @@ -9,6 +9,7 @@ export { default as textarea } from './Textarea'; export { default as select } from './Select'; export { default as number } from './Number'; export { default as checkbox } from './Checkbox'; +export { default as radioGroup } from './RadioGroup'; export { default as flexible } from './Flexible'; export { default as group } from './Group'; diff --git a/src/fields/validations.js b/src/fields/validations.js index eb7fa934f0..8d111a310f 100644 --- a/src/fields/validations.js +++ b/src/fields/validations.js @@ -136,6 +136,10 @@ const optionsToValidatorMap = { if (value && value.length > 0) return true; return 'This field is required.'; }, + radioGroup: (value) => { + if (value) return true; + return 'This field is required.'; + }, flexible: async (value, options) => { if (value.length === 0) { return `This field requires at least one ${options.singularLabel}.`; diff --git a/src/graphql/schema/buildMutationInputType.js b/src/graphql/schema/buildMutationInputType.js index cb4fc1e708..bfb408d828 100644 --- a/src/graphql/schema/buildMutationInputType.js +++ b/src/graphql/schema/buildMutationInputType.js @@ -26,6 +26,7 @@ function buildMutationInputType(name, fields, parentName) { upload: field => ({ type: withNullableType(field, GraphQLString) }), 'rich-text': field => ({ type: withNullableType(field, GraphQLString) }), html: field => ({ type: withNullableType(field, GraphQLString) }), + radioGroup: field => ({ type: withNullableType(field, GraphQLString) }), checkbox: () => ({ type: GraphQLBoolean }), select: (field) => { const formattedName = `${combineParentName(parentName, field.name)}_MutationInput`; diff --git a/src/graphql/schema/buildObjectType.js b/src/graphql/schema/buildObjectType.js index 233e99df96..b56d572ffa 100644 --- a/src/graphql/schema/buildObjectType.js +++ b/src/graphql/schema/buildObjectType.js @@ -29,6 +29,7 @@ function buildObjectType(name, fields, parentName, baseFields = {}) { code: field => ({ type: withNullableType(field, GraphQLString) }), date: field => ({ type: withNullableType(field, GraphQLString) }), upload: field => ({ type: withNullableType(field, GraphQLString) }), + radioGroup: field => ({ type: withNullableType(field, GraphQLString) }), checkbox: field => ({ type: withNullableType(field, GraphQLBoolean) }), select: (field) => { const fullName = combineParentName(parentName, field.name); diff --git a/src/graphql/schema/buildWhereInputType.js b/src/graphql/schema/buildWhereInputType.js index 21e32452f5..c51e7df758 100644 --- a/src/graphql/schema/buildWhereInputType.js +++ b/src/graphql/schema/buildWhereInputType.js @@ -123,6 +123,17 @@ const buildWhereInputType = (name, fields, parentName) => { ), }; }, + radioGroup: (field) => { + const type = GraphQLString; + return { + type: withOperators( + field.name, + type, + parentName, + ['equals', 'like', 'not_equals'], + ), + }; + }, date: (field) => { const type = GraphQLString; return { diff --git a/src/mongoose/buildSchema.js b/src/mongoose/buildSchema.js index 4c4d76708f..b37749fd82 100644 --- a/src/mongoose/buildSchema.js +++ b/src/mongoose/buildSchema.js @@ -82,6 +82,12 @@ const fieldToSchemaMap = { [field.name]: { ...formatBaseSchema(field), type: String }, }; }, + radioGroup: (field, fields) => { + return { + ...fields, + [field.name]: { ...formatBaseSchema(field), type: String }, + }; + }, checkbox: (field, fields) => { return { ...fields, From ca76b02473a03f6f9d4aa1913258c77f3ac948a6 Mon Sep 17 00:00:00 2001 From: Jarrod Flesch Date: Thu, 11 Jun 2020 09:57:09 -0400 Subject: [PATCH 05/14] standardizes the example labels --- demo/collections/AllFields.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/collections/AllFields.js b/demo/collections/AllFields.js index c16bad413e..adb5c119f5 100644 --- a/demo/collections/AllFields.js +++ b/demo/collections/AllFields.js @@ -71,7 +71,7 @@ const AllFields = { type: 'radioGroup', options: [{ value: 'option-1', - label: 'Options 1 Value', + label: 'Options 1 Label', }, { value: 'option-2', label: 'Option 2 Label', From 578f61ebb55cdfa08055cea3944c3acbb6045504 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 12 Jun 2020 09:32:58 -0400 Subject: [PATCH 06/14] renames radioGroup to radio --- demo/collections/AllFields.js | 2 +- demo/collections/{Customers.js => PublicUsers.js} | 6 +++--- demo/payload.public.config.js | 4 ++-- src/client/components/forms/field-types/RadioGroup/index.js | 4 ++-- src/client/components/forms/field-types/index.js | 2 +- src/fields/validations.js | 2 +- src/graphql/schema/buildMutationInputType.js | 2 +- src/graphql/schema/buildObjectType.js | 2 +- src/graphql/schema/buildWhereInputType.js | 2 +- src/mongoose/buildSchema.js | 2 +- 10 files changed, 14 insertions(+), 14 deletions(-) rename demo/collections/{Customers.js => PublicUsers.js} (90%) diff --git a/demo/collections/AllFields.js b/demo/collections/AllFields.js index adb5c119f5..722d73db9e 100644 --- a/demo/collections/AllFields.js +++ b/demo/collections/AllFields.js @@ -68,7 +68,7 @@ const AllFields = { { name: 'radioGroupExample', label: 'Radio Group Example', - type: 'radioGroup', + type: 'radio', options: [{ value: 'option-1', label: 'Options 1 Label', diff --git a/demo/collections/Customers.js b/demo/collections/PublicUsers.js similarity index 90% rename from demo/collections/Customers.js rename to demo/collections/PublicUsers.js index 8e2a47f427..c539d2f897 100644 --- a/demo/collections/Customers.js +++ b/demo/collections/PublicUsers.js @@ -1,10 +1,10 @@ const checkRole = require('../policies/checkRole'); module.exports = { - slug: 'customers', + slug: 'public-users', labels: { - singular: 'Customer', - plural: 'Customers', + singular: 'Public User', + plural: 'Public Users', }, useAsTitle: 'email', policies: { diff --git a/demo/payload.public.config.js b/demo/payload.public.config.js index c551824e74..cf483bb6a0 100644 --- a/demo/payload.public.config.js +++ b/demo/payload.public.config.js @@ -4,7 +4,6 @@ const AllFields = require('./collections/AllFields'); const Code = require('./collections/Code'); const Conditions = require('./collections/Conditions'); const CustomComponents = require('./collections/CustomComponents'); -const Customers = require('./collections/Customers'); const File = require('./collections/File'); const FlexibleContent = require('./collections/FlexibleContent'); const HiddenFields = require('./collections/HiddenFields'); @@ -12,6 +11,7 @@ const Hooks = require('./collections/Hooks'); const Localized = require('./collections/Localized'); const Media = require('./collections/Media'); const Preview = require('./collections/Preview'); +const PublicUsers = require('./collections/PublicUsers'); const RelationshipA = require('./collections/RelationshipA'); const RelationshipB = require('./collections/RelationshipB'); const RichText = require('./collections/RichText'); @@ -33,7 +33,6 @@ module.exports = { Code, Conditions, CustomComponents, - Customers, File, FlexibleContent, HiddenFields, @@ -41,6 +40,7 @@ module.exports = { Localized, Media, Preview, + PublicUsers, RelationshipA, RelationshipB, RichText, diff --git a/src/client/components/forms/field-types/RadioGroup/index.js b/src/client/components/forms/field-types/RadioGroup/index.js index e226de410f..3df0d3fc32 100644 --- a/src/client/components/forms/field-types/RadioGroup/index.js +++ b/src/client/components/forms/field-types/RadioGroup/index.js @@ -6,7 +6,7 @@ import withCondition from '../../withCondition'; import Error from '../../Error'; import Label from '../../Label'; import RadioInput from './RadioInput'; -import { radioGroup } from '../../../../../fields/validations'; +import { radio } from '../../../../../fields/validations'; import './index.scss'; @@ -87,7 +87,7 @@ RadioGroup.defaultProps = { readOnly: false, defaultValue: null, initialData: false, - validate: radioGroup, + validate: radio, width: undefined, style: {}, path: '', diff --git a/src/client/components/forms/field-types/index.js b/src/client/components/forms/field-types/index.js index d9ae0bbe16..be542bbd94 100644 --- a/src/client/components/forms/field-types/index.js +++ b/src/client/components/forms/field-types/index.js @@ -9,7 +9,7 @@ export { default as textarea } from './Textarea'; export { default as select } from './Select'; export { default as number } from './Number'; export { default as checkbox } from './Checkbox'; -export { default as radioGroup } from './RadioGroup'; +export { default as radio } from './RadioGroup'; export { default as flexible } from './Flexible'; export { default as group } from './Group'; diff --git a/src/fields/validations.js b/src/fields/validations.js index 8d111a310f..5aeea60eb2 100644 --- a/src/fields/validations.js +++ b/src/fields/validations.js @@ -136,7 +136,7 @@ const optionsToValidatorMap = { if (value && value.length > 0) return true; return 'This field is required.'; }, - radioGroup: (value) => { + radio: (value) => { if (value) return true; return 'This field is required.'; }, diff --git a/src/graphql/schema/buildMutationInputType.js b/src/graphql/schema/buildMutationInputType.js index bfb408d828..c4f3a93e91 100644 --- a/src/graphql/schema/buildMutationInputType.js +++ b/src/graphql/schema/buildMutationInputType.js @@ -26,7 +26,7 @@ function buildMutationInputType(name, fields, parentName) { upload: field => ({ type: withNullableType(field, GraphQLString) }), 'rich-text': field => ({ type: withNullableType(field, GraphQLString) }), html: field => ({ type: withNullableType(field, GraphQLString) }), - radioGroup: field => ({ type: withNullableType(field, GraphQLString) }), + radio: field => ({ type: withNullableType(field, GraphQLString) }), checkbox: () => ({ type: GraphQLBoolean }), select: (field) => { const formattedName = `${combineParentName(parentName, field.name)}_MutationInput`; diff --git a/src/graphql/schema/buildObjectType.js b/src/graphql/schema/buildObjectType.js index b56d572ffa..874cf1f14c 100644 --- a/src/graphql/schema/buildObjectType.js +++ b/src/graphql/schema/buildObjectType.js @@ -29,7 +29,7 @@ function buildObjectType(name, fields, parentName, baseFields = {}) { code: field => ({ type: withNullableType(field, GraphQLString) }), date: field => ({ type: withNullableType(field, GraphQLString) }), upload: field => ({ type: withNullableType(field, GraphQLString) }), - radioGroup: field => ({ type: withNullableType(field, GraphQLString) }), + radio: field => ({ type: withNullableType(field, GraphQLString) }), checkbox: field => ({ type: withNullableType(field, GraphQLBoolean) }), select: (field) => { const fullName = combineParentName(parentName, field.name); diff --git a/src/graphql/schema/buildWhereInputType.js b/src/graphql/schema/buildWhereInputType.js index c51e7df758..7f961676db 100644 --- a/src/graphql/schema/buildWhereInputType.js +++ b/src/graphql/schema/buildWhereInputType.js @@ -123,7 +123,7 @@ const buildWhereInputType = (name, fields, parentName) => { ), }; }, - radioGroup: (field) => { + radio: (field) => { const type = GraphQLString; return { type: withOperators( diff --git a/src/mongoose/buildSchema.js b/src/mongoose/buildSchema.js index b37749fd82..a25e94689c 100644 --- a/src/mongoose/buildSchema.js +++ b/src/mongoose/buildSchema.js @@ -82,7 +82,7 @@ const fieldToSchemaMap = { [field.name]: { ...formatBaseSchema(field), type: String }, }; }, - radioGroup: (field, fields) => { + radio: (field, fields) => { return { ...fields, [field.name]: { ...formatBaseSchema(field), type: String }, From aa13bd9084618bf637269ab7ba3fdf05722f916e Mon Sep 17 00:00:00 2001 From: James Date: Fri, 12 Jun 2020 14:30:15 -0400 Subject: [PATCH 07/14] uses config path approach similar to NextJS --- ...oad.public.config.js => payload.config.js} | 0 demo/payload.private.config.js | 7 ---- demo/server.js | 19 +++++----- src/auth/auth.spec.js | 2 +- .../requestHandlers/collections.spec.js | 2 +- src/tests/globalSetup.js | 2 +- src/utilities/getConfig.js | 36 +++++++++++++++---- src/webpack/getWebpackDevConfig.js | 2 +- 8 files changed, 43 insertions(+), 27 deletions(-) rename demo/{payload.public.config.js => payload.config.js} (100%) delete mode 100644 demo/payload.private.config.js diff --git a/demo/payload.public.config.js b/demo/payload.config.js similarity index 100% rename from demo/payload.public.config.js rename to demo/payload.config.js diff --git a/demo/payload.private.config.js b/demo/payload.private.config.js deleted file mode 100644 index 76dc985664..0000000000 --- a/demo/payload.private.config.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - email: { - provider: 'mock', - }, - secret: 'SECRET_KEY', - mongoURL: 'mongodb://localhost/payload', -}; diff --git a/demo/server.js b/demo/server.js index 2aa8ffa2f5..3c2a325310 100644 --- a/demo/server.js +++ b/demo/server.js @@ -1,16 +1,17 @@ const path = require('path'); const express = require('express'); const Payload = require('../src'); -const privateConfig = require('./payload.private.config'); const expressApp = express(); const payload = new Payload({ - config: { - public: path.resolve(__dirname, 'payload.public.config.js'), - private: path.resolve(__dirname, 'payload.private.config.js'), + email: { + provider: 'mock', }, + secret: 'SECRET_KEY', + mongoURL: 'mongodb://localhost/payload', express: expressApp, + config: path.resolve(__dirname, 'payload.config.js'), }); exports.payload = payload; @@ -20,12 +21,10 @@ exports.start = (cb) => { console.log(`listening on ${3000}...`); if (cb) cb(); - if (privateConfig.email.provider === 'mock') { - const creds = await payload.getMockEmailCredentials(); - console.log(`Mock email account username: ${creds.user}`); - console.log(`Mock email account password: ${creds.pass}`); - console.log(`Log in to mock email provider at ${creds.web}`); - } + const creds = await payload.getMockEmailCredentials(); + console.log(`Mock email account username: ${creds.user}`); + console.log(`Mock email account password: ${creds.pass}`); + console.log(`Log in to mock email provider at ${creds.web}`); }); return server; diff --git a/src/auth/auth.spec.js b/src/auth/auth.spec.js index 3fbfe1684c..fad327a5ed 100644 --- a/src/auth/auth.spec.js +++ b/src/auth/auth.spec.js @@ -6,7 +6,7 @@ const { email, password } = require('../tests/credentials'); * @jest-environment node */ -const config = require('../../demo/payload.public.config'); +const config = require('../../demo/payload.config'); const url = config.serverURL; diff --git a/src/collections/requestHandlers/collections.spec.js b/src/collections/requestHandlers/collections.spec.js index 135adddcf5..a15b0e9a43 100644 --- a/src/collections/requestHandlers/collections.spec.js +++ b/src/collections/requestHandlers/collections.spec.js @@ -4,7 +4,7 @@ require('isomorphic-fetch'); const faker = require('faker'); -const config = require('../../../demo/payload.public.config'); +const config = require('../../../demo/payload.config'); const { email, password } = require('../../tests/credentials'); const url = config.serverURL; diff --git a/src/tests/globalSetup.js b/src/tests/globalSetup.js index 547ef14438..40b7639274 100644 --- a/src/tests/globalSetup.js +++ b/src/tests/globalSetup.js @@ -1,6 +1,6 @@ require('isomorphic-fetch'); const server = require('../../demo/server'); -const config = require('../../demo/payload.public.config'); +const config = require('../../demo/payload.config'); const { email, password } = require('./credentials'); const url = config.serverURL; diff --git a/src/utilities/getConfig.js b/src/utilities/getConfig.js index f12bf297af..34f1ff9363 100644 --- a/src/utilities/getConfig.js +++ b/src/utilities/getConfig.js @@ -1,17 +1,41 @@ +const path = require('path'); +const fs = require('fs'); + /* eslint-disable import/no-dynamic-require */ /* eslint-disable global-require */ -const getConfig = (options) => { - if (!options.config || !options.config.public || !options.config.private) { - throw new Error('Error: missing config paths. Please specify where to find your configuration files while initializing Payload.'); +const getConfig = (options = {}) => { + if (!options.secret) { + throw new Error('Error: missing secret key. A secret key is needed to secure Payload.'); } - const publicConfig = require(options.config.public); - const privateConfig = require(options.config.private); + if (!options.mongoURL) { + throw new Error('Error: missing MongoDB connection URL.'); + } + + let configPath = path.resolve(__dirname, '../../../payload.config.js'); + + if (!fs.existsSync(configPath)) { + if (typeof options.config !== 'string') { + throw new Error('Error: cannot find Payload config. Please create a configuration file located at the root of your project called "payload.config.js".'); + } + + if (fs.existsSync(options.config)) { + configPath = options.config; + } + } + + const publicConfig = require(configPath); return { ...publicConfig, - ...privateConfig, + secret: options.secret, + mongoURL: options.mongoURL, + email: options.email, + paths: { + ...publicConfig.paths, + config: configPath, + }, }; }; diff --git a/src/webpack/getWebpackDevConfig.js b/src/webpack/getWebpackDevConfig.js index 93173ce3d1..8c83949e25 100644 --- a/src/webpack/getWebpackDevConfig.js +++ b/src/webpack/getWebpackDevConfig.js @@ -118,7 +118,7 @@ module.exports = (config) => { resolve: { modules: ['node_modules', path.resolve(__dirname, '../../node_modules')], alias: { - 'payload/unsanitizedConfig': config.paths.publicConfig, + 'payload/unsanitizedConfig': config.paths.config, 'payload/config': path.resolve(__dirname, '../client/config.js'), }, }, From 4e63863338a2adaa1b0679ef55a62d4e5163bd40 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 12 Jun 2020 15:57:29 -0400 Subject: [PATCH 08/14] replaces moment with date-fns --- package.json | 5 +- src/bin/build.js | 17 ++- .../views/collections/Edit/Default.js | 6 +- .../components/views/collections/List/Cell.js | 4 +- src/utilities/findConfig.js | 20 ++++ src/utilities/getConfig.js | 16 +-- src/webpack/getWebpackProdConfig.js | 37 +++++-- yarn.lock | 103 ++++++++++++++++-- 8 files changed, 170 insertions(+), 38 deletions(-) mode change 100644 => 100755 src/bin/build.js create mode 100644 src/utilities/findConfig.js diff --git a/package.json b/package.json index 7c12bec8ca..3efcbb8422 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "test:unit": "cross-env NODE_ENV=test jest", "test:int": "cross-env NODE_ENV=test jest --forceExit --runInBand", "cov": "npm run core:build && node ./node_modules/jest/bin/jest.js src/tests --coverage", - "dev": "nodemon demo/server.js", + "dev": "PAYLOAD_CONFIG_PATH=/Users/jmikrut/www/payload/demo/payload.config.js nodemon demo/server.js", "lint": "eslint **/*.js", "debug": "nodemon --inspect demo/server.js", "debug:test:int": "node --inspect-brk node_modules/.bin/jest --runInBand" @@ -34,6 +34,7 @@ "body-parser": "^1.19.0", "compression": "^1.7.4", "cookie-parser": "^1.4.5", + "date-fns": "^2.14.0", "dotenv": "^6.0.0", "dotenv-webpack": "^1.8.0", "es6-promise": "^4.2.8", @@ -55,7 +56,6 @@ "method-override": "^3.0.0", "minimist": "^1.2.0", "mkdirp": "^0.5.1", - "moment": "^2.26.0", "mongodb-memory-server": "^6.5.2", "mongoose": "^5.8.9", "mongoose-autopopulate": "^0.11.0", @@ -90,6 +90,7 @@ "url-loader": "^1.0.1", "val-loader": "^2.1.0", "webpack": "^4.41.5", + "webpack-bundle-analyzer": "^3.8.0", "webpack-dev-middleware": "^3.7.2", "webpack-hot-middleware": "^2.25.0" }, diff --git a/src/bin/build.js b/src/bin/build.js old mode 100644 new mode 100755 index 46fe3fb80b..2f2f66a460 --- a/src/bin/build.js +++ b/src/bin/build.js @@ -1,16 +1,24 @@ /* eslint-disable global-require */ /* eslint-disable import/no-dynamic-require */ -const path = require('path'); const webpack = require('webpack'); const getWebpackProdConfig = require('../webpack/getWebpackProdConfig'); +const findConfig = require('../utilities/findConfig'); -module.exports = (args) => { - const configPath = path.resolve(process.cwd(), (args.config || './payload.config.js')); +module.exports = () => { + const configPath = findConfig(); try { const config = require(configPath); - const webpackProdConfig = getWebpackProdConfig(config); + + const webpackProdConfig = getWebpackProdConfig({ + ...config, + paths: { + ...(config.paths || {}), + config: configPath, + }, + }); + webpack(webpackProdConfig, (err, stats) => { // Stats Object if (err || stats.hasErrors()) { // Handle errors here @@ -24,6 +32,7 @@ module.exports = (args) => { // Done processing }); } catch (err) { + console.log(err); console.error(`Error: can't find the configuration file located at ${configPath}.`); } diff --git a/src/client/components/views/collections/Edit/Default.js b/src/client/components/views/collections/Edit/Default.js index 0ed9c180b7..1f9eae5ec5 100644 --- a/src/client/components/views/collections/Edit/Default.js +++ b/src/client/components/views/collections/Edit/Default.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Link, useRouteMatch, useLocation } from 'react-router-dom'; -import moment from 'moment'; +import { format } from 'date-fns'; import config from 'payload/config'; import Eyebrow from '../../../elements/Eyebrow'; import Form from '../../../forms/Form'; @@ -127,11 +127,11 @@ const DefaultEditView = (props) => { <>
  • Last Modified
    -
    {moment(data.updatedAt).format('MMMM Do YYYY, h:mma')}
    +
    {format(data.updatedAt, 'MMMM do yyyy, h:mma')}
  • Created
    -
    {moment(data.createdAt).format('MMMM Do YYYY, h:mma')}
    +
    {format(data.createdAt, 'MMMM do yyyy, h:mma')}
  • )} diff --git a/src/client/components/views/collections/List/Cell.js b/src/client/components/views/collections/List/Cell.js index 5fcc390b64..62b35a8ae4 100644 --- a/src/client/components/views/collections/List/Cell.js +++ b/src/client/components/views/collections/List/Cell.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Link } from 'react-router-dom'; -import moment from 'moment'; +import { format } from 'date-fns'; import config from 'payload/config'; import RenderCustomComponent from '../../../utilities/RenderCustomComponent'; @@ -34,7 +34,7 @@ const DefaultCell = (props) => { {(field.type === 'date' && cellData) && ( - {moment(cellData).format('MMMM Do YYYY, h:mma')} + {format(new Date(cellData), 'MMMM do yyyy, h:mma')} )} {field.type !== 'date' && ( diff --git a/src/utilities/findConfig.js b/src/utilities/findConfig.js new file mode 100644 index 0000000000..3a1a42d2c7 --- /dev/null +++ b/src/utilities/findConfig.js @@ -0,0 +1,20 @@ +const path = require('path'); +const fs = require('fs'); + +const findConfig = () => { + let configPath = path.resolve(__dirname, '../../../payload.config.js'); + + if (!fs.existsSync(configPath)) { + if (typeof process.env.PAYLOAD_CONFIG_PATH !== 'string') { + throw new Error('Error: cannot find Payload config. Please create a configuration file located at the root of your project called "payload.config.js".'); + } + + if (fs.existsSync(process.env.PAYLOAD_CONFIG_PATH)) { + configPath = process.env.PAYLOAD_CONFIG_PATH; + } + } + + return configPath; +}; + +module.exports = findConfig; diff --git a/src/utilities/getConfig.js b/src/utilities/getConfig.js index 34f1ff9363..b3c4c7027a 100644 --- a/src/utilities/getConfig.js +++ b/src/utilities/getConfig.js @@ -1,5 +1,6 @@ const path = require('path'); const fs = require('fs'); +const findConfig = require('./findConfig'); /* eslint-disable import/no-dynamic-require */ /* eslint-disable global-require */ @@ -13,18 +14,7 @@ const getConfig = (options = {}) => { throw new Error('Error: missing MongoDB connection URL.'); } - let configPath = path.resolve(__dirname, '../../../payload.config.js'); - - if (!fs.existsSync(configPath)) { - if (typeof options.config !== 'string') { - throw new Error('Error: cannot find Payload config. Please create a configuration file located at the root of your project called "payload.config.js".'); - } - - if (fs.existsSync(options.config)) { - configPath = options.config; - } - } - + const configPath = findConfig(); const publicConfig = require(configPath); return { @@ -33,7 +23,7 @@ const getConfig = (options = {}) => { mongoURL: options.mongoURL, email: options.email, paths: { - ...publicConfig.paths, + ...(publicConfig.paths || {}), config: configPath, }, }; diff --git a/src/webpack/getWebpackProdConfig.js b/src/webpack/getWebpackProdConfig.js index 2944c04fdf..0d438c0f5b 100644 --- a/src/webpack/getWebpackProdConfig.js +++ b/src/webpack/getWebpackProdConfig.js @@ -1,20 +1,30 @@ const HtmlWebpackPlugin = require('html-webpack-plugin'); +const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); const path = require('path'); const getStyleLoaders = require('./getStyleLoaders'); module.exports = (config) => { - return { + const webpackConfig = { entry: { - main: [path.resolve(__dirname, '../components/index.js')], + main: [path.resolve(__dirname, '../client/components/index.js')], }, output: { path: path.resolve(process.cwd(), 'build'), filename: '[name].[chunkhash].js', }, mode: 'production', - resolveLoader: { modules: [path.join(__dirname, '../../../node_modules')] }, + resolveLoader: { modules: [path.join(__dirname, '../../node_modules')] }, module: { rules: [ + { + test: require.resolve('../client/components/customComponents'), + use: [ + { + loader: 'val-loader', + options: config, + }, + ], + }, { test: /\.js$/, exclude: /node_modules/, @@ -26,13 +36,14 @@ module.exports = (config) => { require.resolve('@babel/preset-env'), { modules: 'commonjs', + targets: '> 1%, not dead', + }, ], require.resolve('@babel/preset-react'), ], plugins: [ require.resolve('@babel/plugin-proposal-class-properties'), - require.resolve('babel-plugin-add-module-exports'), [ require.resolve('@babel/plugin-transform-runtime'), { @@ -87,17 +98,29 @@ module.exports = (config) => { ], }, plugins: [ + new BundleAnalyzerPlugin({ + generateStatsFile: true, + }), new HtmlWebpackPlugin({ - template: path.resolve(__dirname, '../index.html'), + template: path.resolve(__dirname, '../client/index.html'), filename: './index.html', minify: true, }), ], resolve: { - modules: ['node_modules', path.resolve(__dirname, '../../../node_modules')], + modules: ['node_modules', path.resolve(__dirname, '../../node_modules')], alias: { - 'payload-scss-overrides': config.paths.scss, + 'payload/unsanitizedConfig': config.paths.config, + 'payload/config': path.resolve(__dirname, '../client/config.js'), }, }, }; + + if (config.paths.scss) { + webpackConfig.resolve.alias['payload-scss-overrides'] = config.paths.scss; + } else { + webpackConfig.resolve.alias['payload-scss-overrides'] = path.resolve(__dirname, '../client/scss/overrides.scss'); + } + + return webpackConfig; }; diff --git a/yarn.lock b/yarn.lock index 8ae3b790c0..6c5d2f1799 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1571,6 +1571,11 @@ acorn-walk@^6.0.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== +acorn-walk@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e" + integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== + acorn@^6.0.1, acorn@^6.2.1: version "6.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" @@ -1837,6 +1842,11 @@ async-foreach@^0.1.3: resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" integrity sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI= +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + async-some@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/async-some/-/async-some-1.0.2.tgz#4d8a81620d5958791b5b98f802d3207776e95509" @@ -2048,6 +2058,16 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +bfj@^6.1.1: + version "6.1.2" + resolved "https://registry.yarnpkg.com/bfj/-/bfj-6.1.2.tgz#325c861a822bcb358a41c78a33b8e6e2086dde7f" + integrity sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw== + dependencies: + bluebird "^3.5.5" + check-types "^8.0.3" + hoopy "^0.1.4" + tryer "^1.0.1" + big.js@^3.1.3: version "3.2.0" resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" @@ -2516,6 +2536,11 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +check-types@^8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552" + integrity sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ== + chokidar@^2.0.4, chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" @@ -2725,7 +2750,7 @@ commander@2.17.x: resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== -commander@^2.11.0, commander@^2.20.0: +commander@^2.11.0, commander@^2.18.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -3280,6 +3305,11 @@ date-fns@^2.0.1: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.12.0.tgz#01754c8a2f3368fc1119cf4625c3dad8c1845ee6" integrity sha512-qJgn99xxKnFgB1qL4jpxU7Q2t0LOn1p8KMIveef3UZD7kqjT3tpFNNdXJelEHhE+rUgffriXriw/sOSU+cS1Hw== +date-fns@^2.14.0: + version "2.14.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.14.0.tgz#359a87a265bb34ef2e38f93ecf63ac453f9bc7ba" + integrity sha512-1zD+68jhFgDIM0rF05rcwYO8cExdNqxjq4xP1QKM60Q45mnO6zaMWB4tOzrIr4M4GSLntsKeE4c9Bdl2jhL/yw== + debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -3591,6 +3621,11 @@ duplexer3@^0.1.4: resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= +duplexer@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" + integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= + duplexify@^3.4.2, duplexify@^3.6.0: version "3.7.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" @@ -3621,6 +3656,11 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= +ejs@^2.6.1: + version "2.7.4" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" + integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== + electron-to-chromium@^1.3.413: version "1.3.413" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.413.tgz#9c457a4165c7b42e59d66dff841063eb9bfe5614" @@ -4101,7 +4141,7 @@ express-graphql@^0.9.0: http-errors "^1.7.3" raw-body "^2.4.1" -express@^4.17.1: +express@^4.16.3, express@^4.17.1: version "4.17.1" resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== @@ -4271,6 +4311,11 @@ file-uri-to-path@1.0.0: resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== +filesize@^3.6.1: + version "3.6.1" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" + integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg== + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -4760,6 +4805,14 @@ gud@^1.0.0: resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0" integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw== +gzip-size@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" + integrity sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA== + dependencies: + duplexer "^0.1.1" + pify "^4.0.1" + har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -4899,6 +4952,11 @@ homedir-polyfill@^1.0.1: dependencies: parse-passwd "^1.0.0" +hoopy@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d" + integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ== + hosted-git-info@^2.1.4: version "2.8.8" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" @@ -6812,11 +6870,6 @@ mkdirp@^1.0.3: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -moment@^2.26.0: - version "2.26.0" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.26.0.tgz#5e1f82c6bafca6e83e808b30c8705eed0dcbd39a" - integrity sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw== - mongodb-memory-server-core@6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/mongodb-memory-server-core/-/mongodb-memory-server-core-6.5.2.tgz#21c96b6e3104b999905ecb232f83d2432467a0f5" @@ -7406,6 +7459,11 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" +opener@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed" + integrity sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA== + optimize-css-assets-webpack-plugin@^5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz#e2f1d4d94ad8c0af8967ebd7cf138dcb1ef14572" @@ -10514,6 +10572,11 @@ truncate-utf8-bytes@^1.0.0: dependencies: utf8-byte-length "^1.0.1" +tryer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" + integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== + tslib@^1.9.0: version "1.11.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" @@ -10964,6 +11027,25 @@ webidl-conversions@^4.0.2: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== +webpack-bundle-analyzer@^3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.8.0.tgz#ce6b3f908daf069fd1f7266f692cbb3bded9ba16" + integrity sha512-PODQhAYVEourCcOuU+NiYI7WdR8QyELZGgPvB1y2tjbUpbmcQOt5Q7jEK+ttd5se0KSBKD9SXHCEozS++Wllmw== + dependencies: + acorn "^7.1.1" + acorn-walk "^7.1.1" + bfj "^6.1.1" + chalk "^2.4.1" + commander "^2.18.0" + ejs "^2.6.1" + express "^4.16.3" + filesize "^3.6.1" + gzip-size "^5.0.0" + lodash "^4.17.15" + mkdirp "^0.5.1" + opener "^1.5.1" + ws "^6.0.0" + webpack-cli@^3.3.11: version "3.3.11" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.11.tgz#3bf21889bf597b5d82c38f215135a411edfdc631" @@ -11185,6 +11267,13 @@ write@1.0.3: dependencies: mkdirp "^0.5.1" +ws@^6.0.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" + integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== + dependencies: + async-limiter "~1.0.0" + ws@^7.0.0: version "7.2.3" resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.3.tgz#a5411e1fb04d5ed0efee76d26d5c46d830c39b46" From 15628ebe88835f63fa45d863fcdda0b3b540b6f1 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 12 Jun 2020 16:21:30 -0400 Subject: [PATCH 09/14] enables production admin static routes --- package.json | 8 +++++++- .../components/views/collections/Edit/Default.js | 6 +++--- src/client/components/views/collections/List/Cell.js | 2 +- src/express/static.js | 2 +- src/index.js | 10 ++++++++-- src/webpack/getWebpackProdConfig.js | 8 ++------ src/webpack/init.js | 6 +++--- 7 files changed, 25 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 3efcbb8422..6777b0f367 100644 --- a/package.json +++ b/package.json @@ -119,5 +119,11 @@ "sass-loader": "7.1.0", "style-loader": "^0.21.0", "webpack-cli": "^3.3.11" - } + }, + "browserslist": [ + "defaults", + "not IE 11", + "not IE_Mob 11", + "maintained node versions" + ] } diff --git a/src/client/components/views/collections/Edit/Default.js b/src/client/components/views/collections/Edit/Default.js index 1f9eae5ec5..6966798842 100644 --- a/src/client/components/views/collections/Edit/Default.js +++ b/src/client/components/views/collections/Edit/Default.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Link, useRouteMatch, useLocation } from 'react-router-dom'; -import { format } from 'date-fns'; +import format from 'date-fns/format'; import config from 'payload/config'; import Eyebrow from '../../../elements/Eyebrow'; import Form from '../../../forms/Form'; @@ -127,11 +127,11 @@ const DefaultEditView = (props) => { <>
  • Last Modified
    -
    {format(data.updatedAt, 'MMMM do yyyy, h:mma')}
    +
    {format(new Date(data.updatedAt), 'MMMM do yyyy, h:mma')}
  • Created
    -
    {format(data.createdAt, 'MMMM do yyyy, h:mma')}
    +
    {format(new Date(data.createdAt), 'MMMM do yyyy, h:mma')}
  • )} diff --git a/src/client/components/views/collections/List/Cell.js b/src/client/components/views/collections/List/Cell.js index 62b35a8ae4..c7bf9419e3 100644 --- a/src/client/components/views/collections/List/Cell.js +++ b/src/client/components/views/collections/List/Cell.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Link } from 'react-router-dom'; -import { format } from 'date-fns'; +import format from 'date-fns/format'; import config from 'payload/config'; import RenderCustomComponent from '../../../utilities/RenderCustomComponent'; diff --git a/src/express/static.js b/src/express/static.js index 2e63aeb7f2..9f6925ea9c 100644 --- a/src/express/static.js +++ b/src/express/static.js @@ -3,7 +3,7 @@ const express = require('express'); function initStatic() { this.config.collections.forEach((collection) => { if (collection.upload) { - this.express.use(collection.upload.staticURL, express.static(collection.upload.staticDir)); + this.express.use(`${collection.upload.staticURL}*`, express.static(collection.upload.staticDir)); } }); } diff --git a/src/index.js b/src/index.js index efcfb823ec..f0208ae99b 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,7 @@ require('es6-promise').polyfill(); require('isomorphic-fetch'); +const path = require('path'); const express = require('express'); const graphQLPlayground = require('graphql-playground-middleware-express').default; const getConfig = require('./utilities/getConfig'); @@ -39,6 +40,7 @@ class Payload { this.sendEmail = this.sendEmail.bind(this); this.getMockEmailCredentials = this.getMockEmailCredentials.bind(this); this.initStatic = initStatic.bind(this); + this.initWebpack = initWebpack.bind(this); // Configure email service this.email = this.buildEmail(); @@ -53,9 +55,13 @@ class Payload { // Register globals this.initGlobals(); - // Enable client + // Initialize Admin panel if (!this.config.admin.disable && process.env.NODE_ENV !== 'test') { - this.express.use(initWebpack(this.config)); + if (process.env.NODE_ENV === 'production') { + this.express.use(this.config.routes.admin, express.static(path.resolve(process.cwd(), 'build'))); + } else { + this.express.use(initWebpack()); + } } // Init policies route diff --git a/src/webpack/getWebpackProdConfig.js b/src/webpack/getWebpackProdConfig.js index 0d438c0f5b..6d7a948dfe 100644 --- a/src/webpack/getWebpackProdConfig.js +++ b/src/webpack/getWebpackProdConfig.js @@ -1,5 +1,5 @@ const HtmlWebpackPlugin = require('html-webpack-plugin'); -const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); +// const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); const path = require('path'); const getStyleLoaders = require('./getStyleLoaders'); @@ -36,8 +36,6 @@ module.exports = (config) => { require.resolve('@babel/preset-env'), { modules: 'commonjs', - targets: '> 1%, not dead', - }, ], require.resolve('@babel/preset-react'), @@ -98,9 +96,7 @@ module.exports = (config) => { ], }, plugins: [ - new BundleAnalyzerPlugin({ - generateStatsFile: true, - }), + // new BundleAnalyzerPlugin(), new HtmlWebpackPlugin({ template: path.resolve(__dirname, '../client/index.html'), filename: './index.html', diff --git a/src/webpack/init.js b/src/webpack/init.js index eed3cc7e6e..ae09a758fd 100644 --- a/src/webpack/init.js +++ b/src/webpack/init.js @@ -6,8 +6,8 @@ const getWebpackDevConfig = require('./getWebpackDevConfig'); const router = express.Router(); -const initWebpack = (config) => { - const webpackDevConfig = getWebpackDevConfig(config); +const initWebpack = () => { + const webpackDevConfig = getWebpackDevConfig(this.config); const compiler = webpack(webpackDevConfig); router.use(webpackDevMiddleware(compiler, { @@ -16,7 +16,7 @@ const initWebpack = (config) => { router.use(webpackHotMiddleware(compiler)); - router.get(`${config.routes.admin}*`, (req, res, next) => { + router.get(`${this.config.routes.admin}*`, (req, res, next) => { compiler.outputFileSystem.readFile('/index.html', (err, result) => { if (err) { return next(err); From 93164e4ea04eda26413346dff2fb37bc0e9e7b0e Mon Sep 17 00:00:00 2001 From: James Date: Fri, 12 Jun 2020 16:26:34 -0400 Subject: [PATCH 10/14] enables config paths to use cwd to be located --- package.json | 6 +++--- src/utilities/findConfig.js | 6 ++++-- src/utilities/getConfig.js | 2 -- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 6777b0f367..097f94abee 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,10 @@ "description": "", "main": "index.js", "scripts": { - "test:unit": "cross-env NODE_ENV=test jest", - "test:int": "cross-env NODE_ENV=test jest --forceExit --runInBand", + "test:unit": "cross-env PAYLOAD_CONFIG_PATH=demo/payload.config.js NODE_ENV=test jest", + "test:int": "cross-env PAYLOAD_CONFIG_PATH=demo/payload.config.js NODE_ENV=test jest --forceExit --runInBand", "cov": "npm run core:build && node ./node_modules/jest/bin/jest.js src/tests --coverage", - "dev": "PAYLOAD_CONFIG_PATH=/Users/jmikrut/www/payload/demo/payload.config.js nodemon demo/server.js", + "dev": "PAYLOAD_CONFIG_PATH=demo/payload.config.js nodemon demo/server.js", "lint": "eslint **/*.js", "debug": "nodemon --inspect demo/server.js", "debug:test:int": "node --inspect-brk node_modules/.bin/jest --runInBand" diff --git a/src/utilities/findConfig.js b/src/utilities/findConfig.js index 3a1a42d2c7..ca0755220f 100644 --- a/src/utilities/findConfig.js +++ b/src/utilities/findConfig.js @@ -9,8 +9,10 @@ const findConfig = () => { throw new Error('Error: cannot find Payload config. Please create a configuration file located at the root of your project called "payload.config.js".'); } - if (fs.existsSync(process.env.PAYLOAD_CONFIG_PATH)) { - configPath = process.env.PAYLOAD_CONFIG_PATH; + const pathFromENV = path.resolve(process.cwd(), process.env.PAYLOAD_CONFIG_PATH); + + if (fs.existsSync(pathFromENV)) { + configPath = pathFromENV; } } diff --git a/src/utilities/getConfig.js b/src/utilities/getConfig.js index b3c4c7027a..a10aecf0be 100644 --- a/src/utilities/getConfig.js +++ b/src/utilities/getConfig.js @@ -1,5 +1,3 @@ -const path = require('path'); -const fs = require('fs'); const findConfig = require('./findConfig'); /* eslint-disable import/no-dynamic-require */ From 17744098a6634a866de1331fdb434b0d252a550f Mon Sep 17 00:00:00 2001 From: James Date: Fri, 12 Jun 2020 16:39:23 -0400 Subject: [PATCH 11/14] enables a more robust way to search for the config path --- src/index.js | 2 +- src/utilities/findConfig.js | 33 ++++++++++++++++++++++----------- src/webpack/init.js | 4 ++-- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/index.js b/src/index.js index f0208ae99b..e1328cb7ef 100644 --- a/src/index.js +++ b/src/index.js @@ -60,7 +60,7 @@ class Payload { if (process.env.NODE_ENV === 'production') { this.express.use(this.config.routes.admin, express.static(path.resolve(process.cwd(), 'build'))); } else { - this.express.use(initWebpack()); + this.express.use(this.initWebpack()); } } diff --git a/src/utilities/findConfig.js b/src/utilities/findConfig.js index ca0755220f..486694b4b1 100644 --- a/src/utilities/findConfig.js +++ b/src/utilities/findConfig.js @@ -2,21 +2,32 @@ const path = require('path'); const fs = require('fs'); const findConfig = () => { - let configPath = path.resolve(__dirname, '../../../payload.config.js'); - - if (!fs.existsSync(configPath)) { - if (typeof process.env.PAYLOAD_CONFIG_PATH !== 'string') { - throw new Error('Error: cannot find Payload config. Please create a configuration file located at the root of your project called "payload.config.js".'); + // If the developer has specified a config path, + // format it if relative and use it directly if absolute + if (process.env.PAYLOAD_CONFIG_PATH) { + if (path.isAbsolute(process.env.PAYLOAD_CONFIG_PATH)) { + return process.env.PAYLOAD_CONFIG_PATH; } - const pathFromENV = path.resolve(process.cwd(), process.env.PAYLOAD_CONFIG_PATH); - - if (fs.existsSync(pathFromENV)) { - configPath = pathFromENV; - } + return path.resolve(process.cwd(), process.env.PAYLOAD_CONFIG_PATH); } - return configPath; + // By default, Payload is installed as a node_module. + // Traverse up three levels and check for config + const defaultPath = path.resolve(__dirname, '../../../payload.config.js'); + + if (fs.existsSync(defaultPath)) { + return defaultPath; + } + + // Check for config in current working directory + const cwdPath = path.resolve(process.cwd(), 'payload.config.js'); + + if (fs.existsSync(cwdPath)) { + return cwdPath; + } + + throw new Error('Error: cannot find Payload config. Please create a configuration file located at the root of your current working directory called "payload.config.js".'); }; module.exports = findConfig; diff --git a/src/webpack/init.js b/src/webpack/init.js index ae09a758fd..359136b166 100644 --- a/src/webpack/init.js +++ b/src/webpack/init.js @@ -6,7 +6,7 @@ const getWebpackDevConfig = require('./getWebpackDevConfig'); const router = express.Router(); -const initWebpack = () => { +function initWebpack() { const webpackDevConfig = getWebpackDevConfig(this.config); const compiler = webpack(webpackDevConfig); @@ -26,6 +26,6 @@ const initWebpack = () => { }); return router; -}; +} module.exports = initWebpack; From c10c91dd3782349f913516c9aeb24c8ac48ec247 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 12 Jun 2020 16:41:57 -0400 Subject: [PATCH 12/14] removes unused config prop in payload instantiation --- demo/server.js | 1 - src/index.js | 2 -- 2 files changed, 3 deletions(-) diff --git a/demo/server.js b/demo/server.js index 3c2a325310..49faeb6b70 100644 --- a/demo/server.js +++ b/demo/server.js @@ -11,7 +11,6 @@ const payload = new Payload({ secret: 'SECRET_KEY', mongoURL: 'mongodb://localhost/payload', express: expressApp, - config: path.resolve(__dirname, 'payload.config.js'), }); exports.payload = payload; diff --git a/src/index.js b/src/index.js index e1328cb7ef..8683460450 100644 --- a/src/index.js +++ b/src/index.js @@ -28,8 +28,6 @@ class Payload { if (typeof this.config.paths === 'undefined') this.config.paths = {}; - this.config.paths.publicConfig = options.config.public; - this.express = options.express; this.router = express.Router(); this.collections = {}; From 4bdfe4d587d0968f37cc5c1772cab2b7f296817f Mon Sep 17 00:00:00 2001 From: James Date: Fri, 12 Jun 2020 16:47:21 -0400 Subject: [PATCH 13/14] implements prod webpack config override --- src/webpack/getWebpackProdConfig.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/webpack/getWebpackProdConfig.js b/src/webpack/getWebpackProdConfig.js index 6d7a948dfe..d09d715072 100644 --- a/src/webpack/getWebpackProdConfig.js +++ b/src/webpack/getWebpackProdConfig.js @@ -4,7 +4,7 @@ const path = require('path'); const getStyleLoaders = require('./getStyleLoaders'); module.exports = (config) => { - const webpackConfig = { + let webpackConfig = { entry: { main: [path.resolve(__dirname, '../client/components/index.js')], }, @@ -118,5 +118,9 @@ module.exports = (config) => { webpackConfig.resolve.alias['payload-scss-overrides'] = path.resolve(__dirname, '../client/scss/overrides.scss'); } + if (config.webpack && typeof config.webpack === 'function') { + webpackConfig = config.webpack(webpackConfig); + } + return webpackConfig; }; From 80164364332e3b54e91953471b1d32e2b506f94f Mon Sep 17 00:00:00 2001 From: James Date: Fri, 12 Jun 2020 17:43:56 -0400 Subject: [PATCH 14/14] adds env to debug scripts --- .vscode/launch.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 582d8ec156..f664872aa2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -13,6 +13,9 @@ "${workspaceRoot}/node_modules/.bin/jest", "--runInBand" ], + "env": { + "PAYLOAD_CONFIG_PATH": "demo/payload.config.js" + }, "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "port": 9229 @@ -21,8 +24,13 @@ "type": "node", "request": "launch", "name": "Launch Program", + "env": { + "PAYLOAD_CONFIG_PATH": "demo/payload.config.js" + }, "program": "${workspaceFolder}/demo/server.js", - "skipFiles": ["/**"] + "skipFiles": [ + "/**" + ] }, { "type": "chrome",