From 766c75df357f4436bb3d850669a25ab5a307726d Mon Sep 17 00:00:00 2001 From: James Scott Date: Wed, 23 Jul 2025 21:58:35 +0000 Subject: [PATCH] feat: Add schema validation and data combination script This commit introduces two main features to improve data quality and ease of use: Schema Validation: A schemas.json file is added to define a strict schema for each of the mapping files. A new lint-schemas.js script validates the mapping files against these schemas. It also checks that all feature IDs exist in the web-features package. Combined Data: A new combine-mappings.js script aggregates data from all individual mapping files into a single web-features-mappings.combined.json file. This provides a single, convenient file for data consumers. The package.json has been updated with lint:schemas and combine scripts to run these new tools. The README and .gitignore have also been updated accordingly. --- .gitignore | 4 + README.md | 16 +- schemas.json | 303 ++++++++++++++++++++++++++++++++++++ scripts/.gitignore | 3 - scripts/combine-mappings.js | 30 ++++ scripts/lint-schemas.js | 94 +++++++++++ scripts/package-lock.json | 143 ++++++++++------- scripts/package.json | 6 + 8 files changed, 537 insertions(+), 62 deletions(-) create mode 100644 .gitignore create mode 100644 schemas.json delete mode 100644 scripts/.gitignore create mode 100644 scripts/combine-mappings.js create mode 100644 scripts/lint-schemas.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..87ae2a8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +wpt-repo +surveys +web-features-mappings.combined.json diff --git a/README.md b/README.md index 5f8dd53..8368897 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Examples of data sources which already map to web-features IDs and for which we ## The scripts folder -The `/script/` folder contains the JavaScript files that are responsible for updating the mapping files. +The `/scripts/` folder contains the JavaScript files that are responsible for updating the mapping files. To run these scripts: @@ -63,6 +63,20 @@ The mappings are JSON files that are formatted as follows: } ``` +## Combined data + +The `combine` script generates a `web-features-mappings.combined.json` file in the root of the repository. This file contains all the mapping data from the `mappings` folder, combined into a single file. + +The format of the combined file is as follows: + +```json +{ + "chrome-use-counters": { ... }, + "interop": { ... }, + ... +} +``` + ## TODO * Add mapping to origin trials. diff --git a/schemas.json b/schemas.json new file mode 100644 index 0000000..19889d0 --- /dev/null +++ b/schemas.json @@ -0,0 +1,303 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Web Feature Mappings Data", + "description": "A collection of schemas for each of the web feature mapping files.", + "type": "object", + "properties": { + "chrome-use-counters": { + "$ref": "#/$defs/chromeUseCountersFile" + }, + "interop": { + "$ref": "#/$defs/interopFile" + }, + "mdn-docs": { + "$ref": "#/$defs/mdnDocsFile" + }, + "standards-positions": { + "$ref": "#/$defs/standardsPositionsFile" + }, + "state-of-surveys": { + "$ref": "#/$defs/stateOfSurveysFile" + }, + "wpt": { + "$ref": "#/$defs/wptFile" + } + }, + "$comment": "The properties are the file names of the files in the mapping directory", + "additionalProperties": false, + "$defs": { + "chromeUseCountersFile": { + "description": "Schema for chrome-use-counters.json. An object where keys are feature IDs and values are usage metrics.", + "type": "object", + "patternProperties": { + ".*": { + "$ref": "#/$defs/useCounterValue" + } + }, + "additionalProperties": false + }, + "interopFile": { + "description": "Schema for interop.json. An object where keys are feature IDs and values are an array of interop records.", + "type": "object", + "patternProperties": { + ".*": { + "$ref": "#/$defs/interopHistory" + } + }, + "additionalProperties": false + }, + "mdnDocsFile": { + "description": "Schema for mdn-docs.json. An object where keys are feature IDs and values are an array of MDN documentation links.", + "type": "object", + "patternProperties": { + ".*": { + "$ref": "#/$defs/mdnLinkArray" + } + }, + "additionalProperties": false + }, + "standardsPositionsFile": { + "description": "Schema for standards-positions.json. An object where keys are feature IDs and values are an array of standards position records.", + "type": "object", + "patternProperties": { + ".*": { + "$ref": "#/$defs/standardsPositionArray" + } + }, + "additionalProperties": false + }, + "stateOfSurveysFile": { + "description": "Schema for state-of-surveys.json. An object where keys are feature IDs and values are an array of survey records.", + "type": "object", + "patternProperties": { + ".*": { + "$ref": "#/$defs/surveyRecordArray" + } + }, + "additionalProperties": false + }, + "wptFile": { + "description": "Schema for wpt.json. An object where keys are feature IDs and values are a record of Web Platform Tests.", + "type": "object", + "patternProperties": { + ".*": { + "$ref": "#/$defs/wptRecord" + } + }, + "additionalProperties": false + }, + "useCounterValue": { + "type": "object", + "description": "The usage metrics for a single web feature from chrome-use-counters.json.", + "properties": { + "percentageOfPageLoad": { + "type": "number" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "percentageOfPageLoad", + "url" + ], + "additionalProperties": false + }, + "interopHistory": { + "description": "An array of Interop records from interop.json.", + "type": "array", + "items": { + "$ref": "#/$defs/interopRecord" + }, + "minItems": 1 + }, + "interopRecord": { + "type": "object", + "properties": { + "year": { + "type": "integer" + }, + "label": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "year", + "label", + "url" + ], + "additionalProperties": false + }, + "mdnLinkArray": { + "description": "An array of MDN documentation links from mdn-docs.json.", + "type": "array", + "items": { + "$ref": "#/$defs/mdnLink" + }, + "minItems": 1 + }, + "mdnLink": { + "type": "object", + "properties": { + "slug": { + "type": "string" + }, + "title": { + "type": "string" + }, + "anchor": { + "type": [ + "string", + "null" + ] + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "slug", + "title", + "anchor", + "url" + ], + "additionalProperties": false + }, + "standardsPositionArray": { + "description": "An array of standards position records from standards-positions.json.", + "type": "array", + "items": { + "$ref": "#/$defs/standardsPositionRecord" + }, + "minItems": 1 + }, + "standardsPositionRecord": { + "type": "object", + "properties": { + "vendor": { + "type": "string", + "enum": [ + "mozilla", + "webkit" + ] + }, + "url": { + "type": "string", + "format": "uri" + }, + "position": { + "type": "string", + "enum": [ + "", + "blocked", + "defer", + "negative", + "neutral", + "oppose", + "positive", + "support" + ] + }, + "concerns": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "API design", + "accessibility", + "annoyance", + "compatibility", + "complexity", + "dependencies", + "device independence", + "duplication", + "integration", + "internationalization", + "interoperability", + "maintenance", + "performance", + "portability", + "power", + "privacy", + "security", + "use cases", + "venue" + ] + } + } + }, + "required": [ + "vendor", + "url", + "position", + "concerns" + ], + "additionalProperties": false + }, + "surveyRecordArray": { + "description": "An array of survey records from state-of-surveys.json.", + "type": "array", + "items": { + "$ref": "#/$defs/surveyRecord" + }, + "minItems": 1 + }, + "surveyRecord": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + }, + "question": { + "type": "string" + }, + "subQuestion": { + "type": "string" + }, + "path": { + "type": "string" + } + }, + "required": [ + "name", + "url", + "question", + "subQuestion", + "path" + ], + "additionalProperties": false + }, + "wptRecord": { + "type": "object", + "description": "A record of Web Platform Tests from wpt.json.", + "properties": { + "url": { + "type": "string", + "format": "uri" + }, + "tests": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + } + }, + "required": [ + "url", + "tests" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/scripts/.gitignore b/scripts/.gitignore deleted file mode 100644 index 5e8c304..0000000 --- a/scripts/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules -wpt-repo -surveys diff --git a/scripts/combine-mappings.js b/scripts/combine-mappings.js new file mode 100644 index 0000000..5abcefe --- /dev/null +++ b/scripts/combine-mappings.js @@ -0,0 +1,30 @@ +import fs from "fs/promises"; +import path from "path"; +import { glob } from "glob"; +import { fileURLToPath } from "url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +async function main() { + console.log("Combining mapping files..."); + + const mappingFiles = await glob(path.join(__dirname, "../mappings/*.json")); + + const combinedData = {}; + + for (const file of mappingFiles) { + const fileKey = path.basename(file, ".json"); + const data = JSON.parse(await fs.readFile(file, "utf-8")); + combinedData[fileKey] = data; + } + + const outputFile = path.join(__dirname, "../web-features-mappings.combined.json"); + await fs.writeFile(outputFile, JSON.stringify(combinedData, null, 2)); + + console.log(`Combined data written to ${outputFile}`); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); \ No newline at end of file diff --git a/scripts/lint-schemas.js b/scripts/lint-schemas.js new file mode 100644 index 0000000..5022e0b --- /dev/null +++ b/scripts/lint-schemas.js @@ -0,0 +1,94 @@ +import fs from "fs/promises"; +import path from "path"; +import Ajv from "ajv/dist/2020.js"; +import addFormats from "ajv-formats"; +import { glob } from "glob"; +import { fileURLToPath } from "url"; +import { features } from "web-features"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +async function main() { + console.log("Linting mapping files against schemas..."); + + const ajv = new Ajv({ allErrors: true }); + addFormats(ajv); + + const schemaPath = path.join(__dirname, "../schemas.json"); + const mainSchema = JSON.parse(await fs.readFile(schemaPath, "utf-8")); + ajv.addSchema(mainSchema, "schemas.json"); + + const mappingFiles = await glob(path.join(__dirname, "../mappings/*.json")); + + const schemaKeys = new Set(Object.keys(mainSchema.properties)); + const mappingFileKeys = new Set( + mappingFiles.map((file) => path.basename(file, ".json")) + ); + for (const schemaKey of schemaKeys) { + if (!mappingFileKeys.has(schemaKey)) { + console.warn( + ` +Warning: Schema definition exists for "${schemaKey}" but no corresponding file was found in mappings/.` + ); + } + } + + let hasErrors = false; + + for (const file of mappingFiles) { + const fileKey = path.basename(file, ".json"); + console.log(` +Linting ${file}...`); + + const schemaRef = mainSchema.properties[fileKey]?.$ref; + if (!schemaRef) { + console.warn( + ` No schema definition found for ${fileKey} in schemas.json, skipping.` + ); + continue; + } + + const validator = ajv.getSchema(`schemas.json${schemaRef}`); + if (!validator) { + console.error( + ` Could not find or compile schema for ${fileKey} with ref ${schemaRef}` + ); + hasErrors = true; + continue; + } + + const data = JSON.parse(await fs.readFile(file, "utf-8")); + + if (!validator(data)) { + hasErrors = true; + console.error(` Validation errors in ${file}:`); + for (const error of validator.errors) { + console.error(` - ${error.instancePath || "root"}: ${error.message}`); + } + } else { + console.log(` ${file} is valid.`); + } + + const featureIds = Object.keys(data); + for (const featureId of featureIds) { + if (!features[featureId]) { + hasErrors = true; + console.error( + ` Error: Feature ID "${featureId}" does not exist in web-features.` + ); + } + } + } + + if (hasErrors) { + console.error("\n\nLinting failed with errors."); + process.exit(1); + } + + console.log("\n\nAll mapping files are valid."); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/scripts/package-lock.json b/scripts/package-lock.json index a502f62..d225f73 100644 --- a/scripts/package-lock.json +++ b/scripts/package-lock.json @@ -8,6 +8,8 @@ "devDependencies": { "@ddbeck/mdn-content-inventory": "^0.2.20250828", "@mdn/browser-compat-data": "^7.1.0", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", "glob": "^11.0.3", "js-yaml": "^4.1.0", "web-features": "^2.45.0" @@ -17,15 +19,13 @@ "version": "0.2.20250828", "resolved": "https://registry.npmjs.org/@ddbeck/mdn-content-inventory/-/mdn-content-inventory-0.2.20250828.tgz", "integrity": "sha512-g0rFDiq9d8SQEgzo4VDF8xliI3hz+A9qenAFbS5Byq4olUL1xdrJPGnHfTxFCh04HPKAfUoNuJqpslIVNbEvNw==", - "dev": true, - "license": "CC-BY-SA-2.5" + "dev": true }, "node_modules/@isaacs/balanced-match": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", "dev": true, - "license": "MIT", "engines": { "node": "20 || >=22" } @@ -35,7 +35,6 @@ "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", "dev": true, - "license": "MIT", "dependencies": { "@isaacs/balanced-match": "^4.0.1" }, @@ -48,7 +47,6 @@ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, - "license": "ISC", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -65,15 +63,46 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-7.1.0.tgz", "integrity": "sha512-xEgsAGfEDa95aXtafVFQZ+KlP+MaUWwwN2thfOUfoMwM9m9P8DFmcMuJg6h2QwTg/Qpy31GjPIGbBfEilxqjOQ==", + "dev": true + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "dev": true, - "license": "CC0-1.0" + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } }, "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -86,7 +115,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -98,15 +126,13 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" + "dev": true }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -118,15 +144,13 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, - "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -140,22 +164,41 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" + "dev": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, - "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" @@ -172,7 +215,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", "dev": true, - "license": "ISC", "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", @@ -196,7 +238,6 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -205,15 +246,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/jackspeak": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", "dev": true, - "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -229,7 +268,6 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -237,12 +275,17 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, "node_modules/lru-cache": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", "dev": true, - "license": "ISC", "engines": { "node": "20 || >=22" } @@ -252,7 +295,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", "dev": true, - "license": "ISC", "dependencies": { "@isaacs/brace-expansion": "^5.0.0" }, @@ -268,7 +310,6 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, - "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } @@ -277,15 +318,13 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" + "dev": true }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -295,7 +334,6 @@ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", "dev": true, - "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" @@ -307,12 +345,20 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -325,7 +371,6 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -335,7 +380,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "license": "ISC", "engines": { "node": ">=14" }, @@ -348,7 +392,6 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, - "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -367,7 +410,6 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -382,7 +424,6 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -391,15 +432,13 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/string-width-cjs/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -412,7 +451,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -429,7 +467,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -442,7 +479,6 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -451,15 +487,13 @@ "version": "2.45.0", "resolved": "https://registry.npmjs.org/web-features/-/web-features-2.45.0.tgz", "integrity": "sha512-0MkI7qOcK7Tr4uY4HX4+pRl+qU1L0prAvgfHKOATejb6ADuHP1ZUhkt63INlMRt/X/Hwzz4x7IO3ssTu5OTzeQ==", - "dev": true, - "license": "Apache-2.0" + "dev": true }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -475,7 +509,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -494,7 +527,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -512,7 +544,6 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -522,7 +553,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -537,15 +567,13 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -560,7 +588,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, diff --git a/scripts/package.json b/scripts/package.json index f16c159..2234aaf 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -5,8 +5,14 @@ "devDependencies": { "@ddbeck/mdn-content-inventory": "^0.2.20250828", "@mdn/browser-compat-data": "^7.1.0", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", "glob": "^11.0.3", "js-yaml": "^4.1.0", "web-features": "^2.45.0" + }, + "scripts": { + "lint:schemas": "node lint-schemas.js", + "combine": "node combine-mappings.js" } }