From 150296950d4023aa722635fbca7786f8cab4f5d9 Mon Sep 17 00:00:00 2001 From: Alex Lende Date: Tue, 23 Jul 2024 03:16:32 -0500 Subject: [PATCH] Update JSON Schemas to Draft 7 (#63583) * Run ajv-cli migrate on JSON schemas * Migrate additionalProperties to propertyNames where reuse is needed * Update tests to use Ajv Draft 7 * Remove ajv-draft-04 * Fix test string * Add tests for all propertyNames changes Unlinked contributors: DivyanshVinayak23. Co-authored-by: ajlende Co-authored-by: scruffian Co-authored-by: t-hamano --- package-lock.json | 21 -- package.json | 1 - schemas/json/block.json | 2 +- schemas/json/font-collection.json | 2 +- schemas/json/theme.json | 293 ++++++++++-------- schemas/json/wp-env.json | 48 +-- test/integration/blocks-schema.test.js | 4 +- .../fixtures/schemas/settings.json | 7 + .../schemas/settingsPropertiesComplete.json | 11 + test/integration/fixtures/schemas/styles.json | 7 + ...ylesElementsPropertiesComplete_button.json | 11 + ...stylesElementsPropertiesComplete_link.json | 11 + .../stylesPropertiesAndElementsComplete.json | 11 + .../schemas/stylesPropertiesComplete.json | 13 + ...tylesVariationBlockPropertiesComplete.json | 15 + .../schemas/stylesVariationProperties.json | 11 + .../stylesVariationPropertiesComplete.json | 15 + test/integration/theme-schema.test.js | 17 +- 18 files changed, 317 insertions(+), 183 deletions(-) create mode 100644 test/integration/fixtures/schemas/settings.json create mode 100644 test/integration/fixtures/schemas/settingsPropertiesComplete.json create mode 100644 test/integration/fixtures/schemas/styles.json create mode 100644 test/integration/fixtures/schemas/stylesElementsPropertiesComplete_button.json create mode 100644 test/integration/fixtures/schemas/stylesElementsPropertiesComplete_link.json create mode 100644 test/integration/fixtures/schemas/stylesPropertiesAndElementsComplete.json create mode 100644 test/integration/fixtures/schemas/stylesPropertiesComplete.json create mode 100644 test/integration/fixtures/schemas/stylesVariationBlockPropertiesComplete.json create mode 100644 test/integration/fixtures/schemas/stylesVariationProperties.json create mode 100644 test/integration/fixtures/schemas/stylesVariationPropertiesComplete.json diff --git a/package-lock.json b/package-lock.json index 2789eccbaafaa..0318a97778e11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -160,7 +160,6 @@ "@wordpress/scripts": "file:packages/scripts", "@wordpress/stylelint-config": "file:packages/stylelint-config", "ajv": "8.7.1", - "ajv-draft-04": "1.0.0", "appium": "2.0.0", "babel-jest": "29.7.0", "babel-loader": "9.1.3", @@ -17543,20 +17542,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ajv-draft-04": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", - "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", - "dev": true, - "peerDependencies": { - "ajv": "^8.5.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, "node_modules/ajv-errors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", @@ -69390,12 +69375,6 @@ } } }, - "ajv-draft-04": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", - "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", - "dev": true - }, "ajv-errors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", diff --git a/package.json b/package.json index fb6e473b0f6dc..89d576e745aae 100644 --- a/package.json +++ b/package.json @@ -172,7 +172,6 @@ "@wordpress/scripts": "file:packages/scripts", "@wordpress/stylelint-config": "file:packages/stylelint-config", "ajv": "8.7.1", - "ajv-draft-04": "1.0.0", "appium": "2.0.0", "babel-jest": "29.7.0", "babel-loader": "9.1.3", diff --git a/schemas/json/block.json b/schemas/json/block.json index 6a27447f7b496..f142ddd0b490b 100644 --- a/schemas/json/block.json +++ b/schemas/json/block.json @@ -1,6 +1,6 @@ { "title": "JSON schema for WordPress blocks", - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "definitions": { "//": { "reference": "https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/", diff --git a/schemas/json/font-collection.json b/schemas/json/font-collection.json index e52455b220cdb..97b0cdf8edae4 100644 --- a/schemas/json/font-collection.json +++ b/schemas/json/font-collection.json @@ -1,6 +1,6 @@ { "title": "JSON schema for WordPress Font Collections", - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "definitions": { "fontFace": { diff --git a/schemas/json/theme.json b/schemas/json/theme.json index e04c21cf4bd58..1bcfd1335c64c 100644 --- a/schemas/json/theme.json +++ b/schemas/json/theme.json @@ -1,6 +1,6 @@ { "title": "JSON schema for WordPress block theme global settings and styles", - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "definitions": { "//": { "explainer": "https://developer.wordpress.org/themes/advanced-topics/theme-json/", @@ -469,8 +469,7 @@ "increment": { "description": "The amount to increment each step by.", "type": "number", - "exclusiveMinimum": true, - "minimum": 0, + "exclusiveMinimum": 0, "default": 1.5 }, "steps": { @@ -483,8 +482,7 @@ "mediumStep": { "description": "The value to medium setting in the scale.", "type": "number", - "exclusiveMinimum": true, - "minimum": 0, + "exclusiveMinimum": 0, "default": 1.5 }, "unit": { @@ -813,6 +811,22 @@ { "$ref": "#/definitions/settingsPropertiesCustom" } ] }, + "settingsPropertyNames": { + "enum": [ + "appearanceTools", + "background", + "border", + "color", + "dimensions", + "layout", + "lightbox", + "position", + "shadow", + "spacing", + "typography", + "custom" + ] + }, "settingsPropertiesComplete": { "type": "object", "allOf": [ @@ -820,21 +834,9 @@ "$ref": "#/definitions/settingsProperties" }, { - "properties": { - "appearanceTools": {}, - "background": {}, - "border": {}, - "color": {}, - "dimensions": {}, - "layout": {}, - "lightbox": {}, - "position": {}, - "shadow": {}, - "spacing": {}, - "typography": {}, - "custom": {} - }, - "additionalProperties": false + "propertyNames": { + "$ref": "#/definitions/settingsPropertyNames" + } } ] }, @@ -1881,6 +1883,20 @@ } } }, + "stylesPropertyNames": { + "enum": [ + "background", + "border", + "color", + "dimensions", + "spacing", + "typography", + "filter", + "shadow", + "outline", + "css" + ] + }, "stylesPropertiesComplete": { "type": "object", "allOf": [ @@ -1888,19 +1904,9 @@ "$ref": "#/definitions/stylesProperties" }, { - "properties": { - "background": {}, - "border": {}, - "color": {}, - "dimensions": {}, - "spacing": {}, - "typography": {}, - "filter": {}, - "shadow": {}, - "outline": {}, - "css": {} - }, - "additionalProperties": false + "propertyNames": { + "$ref": "#/definitions/stylesPropertyNames" + } } ] }, @@ -1916,16 +1922,6 @@ }, { "properties": { - "border": {}, - "background": {}, - "color": {}, - "dimensions": {}, - "filter": {}, - "shadow": {}, - "outline": {}, - "spacing": {}, - "typography": {}, - "css": {}, ":hover": { "$ref": "#/definitions/stylesPropertiesComplete" }, @@ -1944,8 +1940,26 @@ ":any-link": { "$ref": "#/definitions/stylesPropertiesComplete" } - }, - "additionalProperties": false + } + }, + { + "propertyNames": { + "anyOf": [ + { + "$ref": "#/definitions/stylesPropertyNames" + }, + { + "enum": [ + ":hover", + ":focus", + ":active", + ":visited", + ":link", + ":any-link" + ] + } + ] + } } ] }, @@ -1957,16 +1971,6 @@ }, { "properties": { - "background": {}, - "border": {}, - "color": {}, - "dimensions": {}, - "spacing": {}, - "typography": {}, - "filter": {}, - "shadow": {}, - "outline": {}, - "css": {}, ":hover": { "$ref": "#/definitions/stylesPropertiesComplete" }, @@ -1985,8 +1989,26 @@ ":any-link": { "$ref": "#/definitions/stylesPropertiesComplete" } - }, - "additionalProperties": false + } + }, + { + "propertyNames": { + "anyOf": [ + { + "$ref": "#/definitions/stylesPropertyNames" + }, + { + "enum": [ + ":hover", + ":focus", + ":active", + ":visited", + ":link", + ":any-link" + ] + } + ] + } } ] }, @@ -2334,24 +2356,25 @@ }, { "properties": { - "background": {}, - "border": {}, - "color": {}, - "dimensions": {}, - "spacing": {}, - "typography": {}, - "filter": {}, - "shadow": {}, - "outline": {}, - "css": {}, "elements": { "$ref": "#/definitions/stylesElementsPropertiesComplete" }, "variations": { "$ref": "#/definitions/stylesVariationsPropertiesComplete" } - }, - "additionalProperties": false + } + }, + { + "propertyNames": { + "anyOf": [ + { + "$ref": "#/definitions/stylesPropertyNames" + }, + { + "enum": [ "elements", "variations" ] + } + ] + } } ] }, @@ -2371,24 +2394,25 @@ }, { "properties": { - "background": {}, - "border": {}, - "color": {}, - "dimensions": {}, - "spacing": {}, - "typography": {}, - "filter": {}, - "shadow": {}, - "outline": {}, - "css": {}, "elements": { "$ref": "#/definitions/stylesElementsPropertiesComplete" }, "blocks": { "$ref": "#/definitions/stylesVariationBlocksPropertiesComplete" } - }, - "additionalProperties": false + } + }, + { + "propertyNames": { + "anyOf": [ + { + "$ref": "#/definitions/stylesPropertyNames" + }, + { + "enum": [ "elements", "blocks" ] + } + ] + } } ] }, @@ -2408,24 +2432,25 @@ }, { "properties": { - "background": {}, - "border": {}, - "color": {}, - "dimensions": {}, - "spacing": {}, - "typography": {}, - "filter": {}, - "shadow": {}, - "outline": {}, - "css": {}, "elements": { "$ref": "#/definitions/stylesElementsPropertiesComplete" }, "blocks": { "$ref": "#/definitions/stylesVariationBlocksPropertiesComplete" } - }, - "additionalProperties": false + } + }, + { + "propertyNames": { + "anyOf": [ + { + "$ref": "#/definitions/stylesPropertyNames" + }, + { + "enum": [ "elements", "blocks" ] + } + ] + } } ] }, @@ -2742,21 +2767,22 @@ }, { "properties": { - "background": {}, - "border": {}, - "color": {}, - "dimensions": {}, - "spacing": {}, - "typography": {}, - "filter": {}, - "shadow": {}, - "outline": {}, - "css": {}, "elements": { "$ref": "#/definitions/stylesElementsPropertiesComplete" } - }, - "additionalProperties": false + } + }, + { + "propertyNames": { + "anyOf": [ + { + "$ref": "#/definitions/stylesPropertyNames" + }, + { + "enum": [ "elements" ] + } + ] + } } ] } @@ -2770,7 +2796,7 @@ "version": { "description": "Version of theme.json to use.", "type": "integer", - "enum": [ 3 ] + "const": 3 }, "title": { "type": "string", @@ -2800,26 +2826,28 @@ }, { "properties": { - "appearanceTools": {}, "useRootPaddingAwareAlignments": { "$ref": "#/definitions/settingsPropertiesUseRootPaddingAwareAlignments/properties/useRootPaddingAwareAlignments" }, - "background": {}, - "color": {}, - "layout": {}, - "lightbox": {}, - "spacing": {}, - "typography": {}, - "border": {}, - "shadow": {}, - "position": {}, - "dimensions": {}, - "custom": {}, "blocks": { "$ref": "#/definitions/settingsBlocksPropertiesComplete" } - }, - "additionalProperties": false + } + }, + { + "propertyNames": { + "anyOf": [ + { + "$ref": "#/definitions/settingsPropertyNames" + }, + { + "enum": [ + "useRootPaddingAwareAlignments", + "blocks" + ] + } + ] + } } ] }, @@ -2832,16 +2860,6 @@ }, { "properties": { - "background": {}, - "border": {}, - "color": {}, - "dimensions": {}, - "spacing": {}, - "typography": {}, - "filter": {}, - "shadow": {}, - "outline": {}, - "css": {}, "elements": { "$ref": "#/definitions/stylesElementsPropertiesComplete" }, @@ -2851,8 +2869,19 @@ "variations": { "$ref": "#/definitions/stylesVariationsProperties" } - }, - "additionalProperties": false + } + }, + { + "propertyNames": { + "anyOf": [ + { + "$ref": "#/definitions/stylesPropertyNames" + }, + { + "enum": [ "elements", "blocks", "variations" ] + } + ] + } } ] }, diff --git a/schemas/json/wp-env.json b/schemas/json/wp-env.json index b082c861e4b67..689eb5bc58e22 100644 --- a/schemas/json/wp-env.json +++ b/schemas/json/wp-env.json @@ -1,6 +1,6 @@ { "title": "JSON schema for WordPress wp-env configuration files", - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "definitions": { "//": { "reference": "https://developer.wordpress.org/block-editor/reference-guides/packages/packages-env/" @@ -63,6 +63,17 @@ "type": "object" } } + }, + "wpEnvPropertyNames": { + "enum": [ + "core", + "phpVersion", + "plugins", + "themes", + "port", + "config", + "mappings" + ] } }, "type": "object", @@ -85,16 +96,9 @@ "allOf": [ { "$ref": "#/definitions/wpEnvProperties" }, { - "properties": { - "core": {}, - "phpVersion": {}, - "plugins": {}, - "themes": {}, - "port": {}, - "config": {}, - "mappings": {} - }, - "additionalProperties": false + "propertyNames": { + "$ref": "#/definitions/wpEnvPropertyNames" + } } ] } @@ -103,18 +107,16 @@ } }, { - "properties": { - "$schema": {}, - "core": {}, - "phpVersion": {}, - "plugins": {}, - "themes": {}, - "port": {}, - "config": {}, - "mappings": {}, - "env": {} - }, - "additionalProperties": false + "propertyNames": { + "anyOf": [ + { + "$ref": "#/definitions/wpEnvPropertyNames" + }, + { + "enum": [ "$schema", "env" ] + } + ] + } } ] } diff --git a/test/integration/blocks-schema.test.js b/test/integration/blocks-schema.test.js index c9456efddc1ad..37137ddd09d21 100644 --- a/test/integration/blocks-schema.test.js +++ b/test/integration/blocks-schema.test.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import Ajv from 'ajv-draft-04'; +import Ajv from 'ajv'; import glob from 'fast-glob'; /** @@ -16,7 +16,7 @@ describe( 'block.json schema', () => { ); const ajv = new Ajv(); - test( 'strictly adheres to the draft-04 meta schema', () => { + test( 'strictly adheres to the draft-07 meta schema', () => { // Use ajv.compile instead of ajv.validateSchema to validate the schema // because validateSchema only checks syntax, whereas, compile checks // if the schema is semantically correct with strict mode. diff --git a/test/integration/fixtures/schemas/settings.json b/test/integration/fixtures/schemas/settings.json new file mode 100644 index 0000000000000..0ed0548aa7b0a --- /dev/null +++ b/test/integration/fixtures/schemas/settings.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../../schemas/json/theme.json", + "version": 3, + "settings": { + "invalidAdditionalProperty": true + } +} diff --git a/test/integration/fixtures/schemas/settingsPropertiesComplete.json b/test/integration/fixtures/schemas/settingsPropertiesComplete.json new file mode 100644 index 0000000000000..e70019cc26697 --- /dev/null +++ b/test/integration/fixtures/schemas/settingsPropertiesComplete.json @@ -0,0 +1,11 @@ +{ + "$schema": "../../../../schemas/json/theme.json", + "version": 3, + "settings": { + "blocks": { + "core/button": { + "invalidAdditionalProperty": true + } + } + } +} diff --git a/test/integration/fixtures/schemas/styles.json b/test/integration/fixtures/schemas/styles.json new file mode 100644 index 0000000000000..50635d3de7e92 --- /dev/null +++ b/test/integration/fixtures/schemas/styles.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../../schemas/json/theme.json", + "version": 3, + "styles": { + "invalidAdditionalProperty": true + } +} diff --git a/test/integration/fixtures/schemas/stylesElementsPropertiesComplete_button.json b/test/integration/fixtures/schemas/stylesElementsPropertiesComplete_button.json new file mode 100644 index 0000000000000..8c596653abcb0 --- /dev/null +++ b/test/integration/fixtures/schemas/stylesElementsPropertiesComplete_button.json @@ -0,0 +1,11 @@ +{ + "$schema": "../../../../schemas/json/theme.json", + "version": 3, + "styles": { + "elements": { + "button": { + "invalidAdditionalProperty": true + } + } + } +} diff --git a/test/integration/fixtures/schemas/stylesElementsPropertiesComplete_link.json b/test/integration/fixtures/schemas/stylesElementsPropertiesComplete_link.json new file mode 100644 index 0000000000000..b8bbd11910d50 --- /dev/null +++ b/test/integration/fixtures/schemas/stylesElementsPropertiesComplete_link.json @@ -0,0 +1,11 @@ +{ + "$schema": "../../../../schemas/json/theme.json", + "version": 3, + "styles": { + "elements": { + "link": { + "invalidAdditionalProperty": true + } + } + } +} diff --git a/test/integration/fixtures/schemas/stylesPropertiesAndElementsComplete.json b/test/integration/fixtures/schemas/stylesPropertiesAndElementsComplete.json new file mode 100644 index 0000000000000..cf9e2c5689534 --- /dev/null +++ b/test/integration/fixtures/schemas/stylesPropertiesAndElementsComplete.json @@ -0,0 +1,11 @@ +{ + "$schema": "../../../../schemas/json/theme.json", + "version": 3, + "styles": { + "blocks": { + "core/button": { + "invalidAdditionalProperty": true + } + } + } +} diff --git a/test/integration/fixtures/schemas/stylesPropertiesComplete.json b/test/integration/fixtures/schemas/stylesPropertiesComplete.json new file mode 100644 index 0000000000000..0727fb2abdbe1 --- /dev/null +++ b/test/integration/fixtures/schemas/stylesPropertiesComplete.json @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schemas/json/theme.json", + "version": 3, + "styles": { + "elements": { + "button": { + ":hover": { + "invalidAdditionalProperty": true + } + } + } + } +} diff --git a/test/integration/fixtures/schemas/stylesVariationBlockPropertiesComplete.json b/test/integration/fixtures/schemas/stylesVariationBlockPropertiesComplete.json new file mode 100644 index 0000000000000..42b3e4b2174f5 --- /dev/null +++ b/test/integration/fixtures/schemas/stylesVariationBlockPropertiesComplete.json @@ -0,0 +1,15 @@ +{ + "$schema": "../../../../schemas/json/theme.json", + "version": 3, + "styles": { + "variations": { + "custom-variation": { + "blocks": { + "core/button": { + "invalidAdditionalProperty": true + } + } + } + } + } +} diff --git a/test/integration/fixtures/schemas/stylesVariationProperties.json b/test/integration/fixtures/schemas/stylesVariationProperties.json new file mode 100644 index 0000000000000..a8a28550dbeee --- /dev/null +++ b/test/integration/fixtures/schemas/stylesVariationProperties.json @@ -0,0 +1,11 @@ +{ + "$schema": "../../../../schemas/json/theme.json", + "version": 3, + "styles": { + "variations": { + "custom-variation": { + "invalidAdditionalProperty": true + } + } + } +} diff --git a/test/integration/fixtures/schemas/stylesVariationPropertiesComplete.json b/test/integration/fixtures/schemas/stylesVariationPropertiesComplete.json new file mode 100644 index 0000000000000..4fc448a22a016 --- /dev/null +++ b/test/integration/fixtures/schemas/stylesVariationPropertiesComplete.json @@ -0,0 +1,15 @@ +{ + "$schema": "../../../../schemas/json/theme.json", + "version": 3, + "styles": { + "blocks": { + "core/button": { + "variations": { + "custom-variation": { + "invalidAdditionalProperty": true + } + } + } + } + } +} diff --git a/test/integration/theme-schema.test.js b/test/integration/theme-schema.test.js index df6ee29fbec1e..01c95ddaefadc 100644 --- a/test/integration/theme-schema.test.js +++ b/test/integration/theme-schema.test.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import Ajv from 'ajv-draft-04'; +import Ajv from 'ajv'; import glob from 'fast-glob'; /** @@ -14,13 +14,17 @@ describe( 'theme.json schema', () => { [ 'packages/*/src/**/theme.json', '{lib,phpunit,test}/**/theme.json' ], { onlyFiles: true } ); + const invalidFiles = glob.sync( + [ 'test/integration/fixtures/schemas/*.json' ], + { onlyFiles: true } + ); const ajv = new Ajv( { // Used for matching unknown blocks without repeating core blocks names // with patternProperties in settings.blocks and settings.styles allowMatchingProperties: true, } ); - it( 'strictly adheres to the draft-04 meta schema', () => { + it( 'strictly adheres to the draft-07 meta schema', () => { // Use ajv.compile instead of ajv.validateSchema to validate the schema // because validateSchema only checks syntax, whereas, compile checks // if the schema is semantically correct with strict mode. @@ -45,4 +49,13 @@ describe( 'theme.json schema', () => { expect( result ).toBe( true ); } ); + + test.each( invalidFiles )( 'invalidates schema for `%s`', ( filepath ) => { + // We want to validate the theme.json file using the local schema. + const { $schema, ...metadata } = require( filepath ); + + const result = ajv.validate( themeSchema, metadata ); + + expect( result ).toBe( false ); + } ); } );