diff --git a/packages/netlify-cms-core/package.json b/packages/netlify-cms-core/package.json index 088d938e..04f54ece 100644 --- a/packages/netlify-cms-core/package.json +++ b/packages/netlify-cms-core/package.json @@ -26,9 +26,9 @@ "license": "MIT", "dependencies": { "@iarna/toml": "2.2.5", - "ajv": "^7.0.0", - "ajv-errors": "^2.0.0", - "ajv-keywords": "^4.0.0", + "ajv": "^8.0.0", + "ajv-errors": "^3.0.0", + "ajv-keywords": "^5.0.0", "copy-text-to-clipboard": "^3.0.0", "deepmerge": "^4.2.2", "diacritics": "^1.3.0", diff --git a/packages/netlify-cms-core/src/constants/__tests__/configSchema.spec.js b/packages/netlify-cms-core/src/constants/__tests__/configSchema.spec.js index 0077e0f8..6d6a460d 100644 --- a/packages/netlify-cms-core/src/constants/__tests__/configSchema.spec.js +++ b/packages/netlify-cms-core/src/constants/__tests__/configSchema.spec.js @@ -39,25 +39,25 @@ describe('config', () => { it('should throw if backend is not defined in config', () => { expect(() => { validateConfig({ foo: 'bar' }); - }).toThrowError("config should have required property 'backend'"); + }).toThrowError("config must have required property 'backend'"); }); it('should throw if backend name is not defined in config', () => { expect(() => { validateConfig({ foo: 'bar', backend: {} }); - }).toThrowError("'backend' should have required property 'name'"); + }).toThrowError("'backend' must have required property 'name'"); }); it('should throw if backend name is not a string in config', () => { expect(() => { validateConfig({ foo: 'bar', backend: { name: {} } }); - }).toThrowError("'backend.name' should be string"); + }).toThrowError("'backend.name' must be string"); }); it('should throw if backend.open_authoring is not a boolean in config', () => { expect(() => { validateConfig(merge(validConfig, { backend: { open_authoring: 'true' } })); - }).toThrowError("'backend.open_authoring' should be boolean"); + }).toThrowError("'backend.open_authoring' must be boolean"); }); it('should not throw if backend.open_authoring is boolean in config', () => { @@ -69,7 +69,7 @@ describe('config', () => { it('should throw if backend.auth_scope is not "repo" or "public_repo" in config', () => { expect(() => { validateConfig(merge(validConfig, { backend: { auth_scope: 'user' } })); - }).toThrowError("'backend.auth_scope' should be equal to one of the allowed values"); + }).toThrowError("'backend.auth_scope' must be equal to one of the allowed values"); }); it('should not throw if backend.auth_scope is one of "repo" or "public_repo" in config', () => { @@ -84,19 +84,19 @@ describe('config', () => { it('should throw if media_folder is not defined in config', () => { expect(() => { validateConfig({ foo: 'bar', backend: { name: 'bar' } }); - }).toThrowError("config should have required property 'media_folder'"); + }).toThrowError("config must have required property 'media_folder'"); }); it('should throw if media_folder is not a string in config', () => { expect(() => { validateConfig({ foo: 'bar', backend: { name: 'bar' }, media_folder: {} }); - }).toThrowError("'media_folder' should be string"); + }).toThrowError("'media_folder' must be string"); }); it('should throw if collections is not defined in config', () => { expect(() => { validateConfig({ foo: 'bar', backend: { name: 'bar' }, media_folder: 'baz' }); - }).toThrowError("config should have required property 'collections'"); + }).toThrowError("config must have required property 'collections'"); }); it('should throw if collections not an array in config', () => { @@ -107,7 +107,7 @@ describe('config', () => { media_folder: 'baz', collections: {}, }); - }).toThrowError("'collections' should be array"); + }).toThrowError("'collections' must be array"); }); it('should throw if collections is an empty array in config', () => { @@ -118,7 +118,7 @@ describe('config', () => { media_folder: 'baz', collections: [], }); - }).toThrowError("'collections' should NOT have fewer than 1 items"); + }).toThrowError("'collections' must NOT have fewer than 1 items"); }); it('should throw if collections is an array with a single null element in config', () => { @@ -129,25 +129,25 @@ describe('config', () => { media_folder: 'baz', collections: [null], }); - }).toThrowError("'collections[0]' should be object"); + }).toThrowError("'collections[0]' must be object"); }); it('should throw if local_backend is not a boolean or plain object', () => { expect(() => { validateConfig({ ...validConfig, local_backend: [] }); - }).toThrowError("'local_backend' should be boolean"); + }).toThrowError("'local_backend' must be boolean"); }); it('should throw if local_backend url is not a string', () => { expect(() => { validateConfig({ ...validConfig, local_backend: { url: [] } }); - }).toThrowError("'local_backend.url' should be string"); + }).toThrowError("'local_backend.url' must be string"); }); it('should throw if local_backend allowed_hosts is not a string array', () => { expect(() => { validateConfig({ ...validConfig, local_backend: { allowed_hosts: [true] } }); - }).toThrowError("'local_backend.allowed_hosts[0]' should be string"); + }).toThrowError("'local_backend.allowed_hosts[0]' must be string"); }); it('should not throw if local_backend is a boolean', () => { @@ -174,7 +174,7 @@ describe('config', () => { it('should throw if collection publish is not a boolean', () => { expect(() => { validateConfig(merge({}, validConfig, { collections: [{ publish: 'false' }] })); - }).toThrowError("'collections[0].publish' should be boolean"); + }).toThrowError("'collections[0].publish' must be boolean"); }); it('should not throw if collection publish is a boolean', () => { @@ -186,7 +186,7 @@ describe('config', () => { it('should throw if collections sortable_fields is not a boolean or a string array', () => { expect(() => { validateConfig(merge({}, validConfig, { collections: [{ sortable_fields: 'title' }] })); - }).toThrowError("'collections[0].sortable_fields' should be array"); + }).toThrowError("'collections[0].sortable_fields' must be array"); }); it('should allow sortable_fields to be a string array', () => { @@ -212,7 +212,7 @@ describe('config', () => { validateConfig( merge({}, validConfig, { collections: [{ sortable_fields: [], sortableFields: [] }] }), ); - }).toThrowError("'collections[0]' should NOT be valid"); + }).toThrowError("'collections[0]' must NOT be valid"); }); it('should throw if collection names are not unique', () => { @@ -335,7 +335,7 @@ describe('config', () => { }), ); }).toThrowError( - "'collections[0].fields[1].fields[1].search_fields' should be array\n'collections[0].fields[1].fields[1].display_fields' should be array", + "'collections[0].fields[1].fields[1].search_fields' must be array\n'collections[0].fields[1].fields[1].display_fields' must be array", ); }); @@ -374,31 +374,31 @@ describe('config', () => { it('should throw if collection meta is not a plain object', () => { expect(() => { validateConfig(merge({}, validConfig, { collections: [{ meta: [] }] })); - }).toThrowError("'collections[0].meta' should be object"); + }).toThrowError("'collections[0].meta' must be object"); }); it('should throw if collection meta is an empty object', () => { expect(() => { validateConfig(merge({}, validConfig, { collections: [{ meta: {} }] })); - }).toThrowError("'collections[0].meta' should NOT have fewer than 1 items"); + }).toThrowError("'collections[0].meta' must NOT have fewer than 1 items"); }); it('should throw if collection meta is an empty object', () => { expect(() => { validateConfig(merge({}, validConfig, { collections: [{ meta: { path: {} } }] })); - }).toThrowError("'collections[0].meta.path' should have required property 'label'"); + }).toThrowError("'collections[0].meta.path' must have required property 'label'"); expect(() => { validateConfig( merge({}, validConfig, { collections: [{ meta: { path: { label: 'Label' } } }] }), ); - }).toThrowError("'collections[0].meta.path' should have required property 'widget'"); + }).toThrowError("'collections[0].meta.path' must have required property 'widget'"); expect(() => { validateConfig( merge({}, validConfig, { collections: [{ meta: { path: { label: 'Label', widget: 'widget' } } }], }), ); - }).toThrowError("'collections[0].meta.path' should have required property 'index_file'"); + }).toThrowError("'collections[0].meta.path' must have required property 'index_file'"); }); it('should allow collection meta to have a path configuration', () => { @@ -416,7 +416,7 @@ describe('config', () => { it('should throw if collection field pattern is not an array', () => { expect(() => { validateConfig(merge({}, validConfig, { collections: [{ fields: [{ pattern: '' }] }] })); - }).toThrowError("'collections[0].fields[0].pattern' should be array"); + }).toThrowError("'collections[0].fields[0].pattern' must be array"); }); it('should throw if collection field pattern is not an array of [string|regex, string]', () => { @@ -425,14 +425,14 @@ describe('config', () => { merge({}, validConfig, { collections: [{ fields: [{ pattern: [1, ''] }] }] }), ); }).toThrowError( - "'collections[0].fields[0].pattern[0]' should be string\n'collections[0].fields[0].pattern[0]' should be a regular expression", + "'collections[0].fields[0].pattern[0]' must be string\n'collections[0].fields[0].pattern[0]' must be a regular expression", ); expect(() => { validateConfig( merge({}, validConfig, { collections: [{ fields: [{ pattern: ['', 1] }] }] }), ); - }).toThrowError("'collections[0].fields[0].pattern[1]' should be string"); + }).toThrowError("'collections[0].fields[0].pattern[1]' must be string"); }); it('should allow collection field pattern to be an array of [string|regex, string]', () => { @@ -464,7 +464,7 @@ describe('config', () => { }, }), ); - }).toThrowError(`'i18n.locales[1]' should match pattern "^[a-zA-Z-_]+$"`); + }).toThrowError(`'i18n.locales[1]' must match pattern "^[a-zA-Z-_]+$"`); }); it('should throw error when locale is less than 2 characters', () => { @@ -477,7 +477,7 @@ describe('config', () => { }, }), ); - }).toThrowError(`'i18n.locales[1]' should NOT have fewer than 2 characters`); + }).toThrowError(`'i18n.locales[1]' must NOT have fewer than 2 characters`); }); it('should throw error when locale is more than 10 characters', () => { @@ -490,7 +490,7 @@ describe('config', () => { }, }), ); - }).toThrowError(`'i18n.locales[1]' should NOT have more than 10 characters`); + }).toThrowError(`'i18n.locales[1]' must NOT have more than 10 characters`); }); it('should allow valid locales strings', () => { diff --git a/packages/netlify-cms-core/src/constants/configSchema.js b/packages/netlify-cms-core/src/constants/configSchema.js index 945d01ae..ae0b7654 100644 --- a/packages/netlify-cms-core/src/constants/configSchema.js +++ b/packages/netlify-cms-core/src/constants/configSchema.js @@ -327,8 +327,8 @@ function getWidgetSchemas() { class ConfigError extends Error { constructor(errors, ...args) { const message = errors - .map(({ message, dataPath }) => { - const dotPath = dataPath + .map(({ message, instancePath }) => { + const dotPath = instancePath .slice(1) .split('/') .map(seg => (seg.match(/^\d+$/) ? `[${seg}]` : `.${seg}`)) @@ -366,7 +366,7 @@ export function validateConfig(config) { switch (e.keyword) { // TODO: remove after https://github.com/ajv-validator/ajv-keywords/pull/123 is merged case 'uniqueItemProperties': { - const path = e.dataPath || ''; + const path = e.instancePath || ''; let newError = e; if (path.endsWith('/fields')) { newError = { ...e, message: 'fields names must be unique' }; @@ -378,12 +378,12 @@ export function validateConfig(config) { return newError; } case 'instanceof': { - const path = e.dataPath || ''; + const path = e.instancePath || ''; let newError = e; if (/fields\/\d+\/pattern\/\d+/.test(path)) { newError = { ...e, - message: 'should be a regular expression', + message: 'must be a regular expression', }; } return newError; diff --git a/yarn.lock b/yarn.lock index 8161cb79..7acc8199 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4040,20 +4040,22 @@ ajv-errors@^1.0.0: resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== -ajv-errors@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-2.0.1.tgz#af5a199a0de65e6c5be738e108ad480578898a9f" - integrity sha512-hGH2npS6nUdBr61gf3rcG8R04DwwgQsdt6goZGmaZHYRHtLF948Emsdta/bcP9AD6/X1jnHm9lMbgujOQG7W6A== +ajv-errors@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-3.0.0.tgz#e54f299f3a3d30fe144161e5f0d8d51196c527bc" + integrity sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ== ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv-keywords@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-4.0.1.tgz#3fcd1988846b741ec958b2555c96ccecce2b3a59" - integrity sha512-vPmwZT6AL4R7kKrjKOMoKgnompJJS8MUgTB7dPBEknSSv4ahKgu046H+bGOH2eMRbAcz5pYz3fwaSwrCGNvuxA== +ajv-keywords@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.0.0.tgz#d01b3b21715b2f63d02aa511b82fc6eb3b30083c" + integrity sha512-ULd1QMjRoH6JDNUQIfDLrlE+OgZlFaxyYCjzt58uNuUQtKXt8/U+vK/8Ql0gyn/C5mqZzUWtKMqr/4YquvTrWA== + dependencies: + fast-deep-equal "^3.1.3" ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" @@ -4065,7 +4067,7 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^7.0.0, ajv@^7.0.2: +ajv@^7.0.2: version "7.2.4" resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.2.4.tgz#8e239d4d56cf884bccca8cca362f508446dc160f" integrity sha512-nBeQgg/ZZA3u3SYxyaDvpvDtgZ/EZPF547ARgZBrG9Bhu1vKDwAIjtIf+sDtJUKa2zOcEbmRLBRSyMraS/Oy1A== @@ -4075,6 +4077,16 @@ ajv@^7.0.0, ajv@^7.0.2: require-from-string "^2.0.2" uri-js "^4.2.2" +ajv@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.0.1.tgz#dac101898a87f8ebb57fea69617e8096523c628c" + integrity sha512-46ZA4TalFcLLqX1dEU3dhdY38wAtDydJ4e7QQTVekLUTzXkb1LfqU6VOBXC/a9wiv4T094WURqJH6ZitF92Kqw== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + all-contributors-cli@^6.0.0: version "6.20.0" resolved "https://registry.yarnpkg.com/all-contributors-cli/-/all-contributors-cli-6.20.0.tgz#9bc98dda38cb29cfe8afc8a78c004e14af25d2f6" @@ -6600,7 +6612,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" -create-react-class@^15.5.2: +create-react-class@^15.5.1, create-react-class@^15.5.2: version "15.7.0" resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.7.0.tgz#7499d7ca2e69bb51d13faf59bd04f0c65a1d6c1e" integrity sha512-QZv4sFWG9S5RUvkTYWbflxeZX+JG7Cz0Tn33rQBJ+WFQTqTfUTjMjiv9tnfXazjsO5r0KhPs+AqCjyrQX6h2ng== @@ -8357,7 +8369,7 @@ fast-deep-equal@^2.0.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -10125,7 +10137,7 @@ interpret@^2.0.0, interpret@^2.2.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== -invariant@^2.1.0, invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: +invariant@^2.0.0, invariant@^2.1.0, invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -14886,7 +14898,19 @@ react-popper@^1.3.7: typed-styles "^0.0.7" warning "^4.0.2" -react-redux@^4.0.0, react-redux@^7.2.0: +react-redux@^4.0.0: + version "4.4.10" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-4.4.10.tgz#ad57bd1db00c2d0aa7db992b360ce63dd0b80ec5" + integrity sha512-tjL0Bmpkj75Td0k+lXlF8Fc8a9GuXFv/3ahUOCXExWs/jhsKiQeTffdH0j5byejCGCRL4tvGFYlrwBF1X/Aujg== + dependencies: + create-react-class "^15.5.1" + hoist-non-react-statics "^3.3.0" + invariant "^2.0.0" + lodash "^4.17.11" + loose-envify "^1.4.0" + prop-types "^15.7.2" + +react-redux@^7.2.0: version "7.2.3" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.3.tgz#4c084618600bb199012687da9e42123cca3f0be9" integrity sha512-ZhAmQ1lrK+Pyi0ZXNMUZuYxYAZd59wFuVDGUt536kSGdD0ya9Q7BfsE95E3TsFLE3kOSFp5m6G5qbatE+Ic1+w==