diff options
Diffstat (limited to 'devtools/shared/compatibility/bin/update.js')
-rw-r--r-- | devtools/shared/compatibility/bin/update.js | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/devtools/shared/compatibility/bin/update.js b/devtools/shared/compatibility/bin/update.js new file mode 100644 index 0000000000..4caa1efe01 --- /dev/null +++ b/devtools/shared/compatibility/bin/update.js @@ -0,0 +1,202 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */ + +// The Compatibility panel detects issues by comparing against official MDN compatibility data +// at https://github.com/mdn/browser-compat-data). It uses a local snapshot of the dataset. +// This dataset needs to be manually synchronized periodically + +// The subsets from the dataset required by the Compatibility panel are: +// * css.properties: https://github.com/mdn/browser-compat-data/tree/master/css + +// The MDN compatibility data is available as a node package ("@mdn/browser-compat-data"), +// which is used here to update `../dataset/css-properties.json`. + +/* eslint-disable mozilla/reject-relative-requires */ + +"use strict"; + +const compatData = require("@mdn/browser-compat-data"); +const { properties } = compatData.css; + +const { TARGET_BROWSER_ID } = require("../constants.js"); +const { getCompatTable } = require("../helpers.js"); + +// Flatten all CSS properties aliases here so we don't have to do it at runtime, +// which is costly. +flattenAliases(properties); +parseBrowserVersion(properties); +removeUnusedData(properties); +exportData(properties, "css-properties.json"); + +/** + * Builds a list of aliases between CSS properties, like flex and -webkit-flex, + * and mutates individual entries in the web compat data store for CSS properties to + * add their corresponding aliases. + */ +function flattenAliases(compatNode) { + for (const term in compatNode) { + if (term.startsWith("_")) { + // Ignore exploring if the term is _aliasOf or __compat. + continue; + } + + const compatTable = getCompatTable(compatNode, [term]); + if (compatTable) { + const aliases = findAliasesFrom(compatTable); + + for (const { alternative_name: name, prefix } of aliases) { + const alias = name || prefix + term; + compatNode[alias] = { _aliasOf: term }; + } + + if (aliases.length) { + // Make the term accessible as the alias. + compatNode[term]._aliasOf = term; + } + } + + // Flatten deeper node as well. + flattenAliases(compatNode[term]); + } +} + +function findAliasesFrom(compatTable) { + const aliases = []; + + for (const browser in compatTable.support) { + let supportStates = compatTable.support[browser] || []; + supportStates = Array.isArray(supportStates) + ? supportStates + : [supportStates]; + + for (const { alternative_name: name, prefix } of supportStates) { + if (!prefix && !name) { + continue; + } + + aliases.push({ alternative_name: name, prefix }); + } + } + + return aliases; +} + +function parseBrowserVersion(compatNode) { + for (const term in compatNode) { + if (term.startsWith("_")) { + // Ignore exploring if the term is _aliasOf or __compat. + continue; + } + + const compatTable = getCompatTable(compatNode, [term]); + if (compatTable?.support) { + for (const [browserId, supportItem] of Object.entries( + compatTable.support + )) { + // supportItem is an array when there are info for both prefixed and non-prefixed + // versions. If it's not an array in the original data, transform it into one + // since we'd have to do it in MDNCompatibility at runtime otherwise. + if (!Array.isArray(supportItem)) { + compatTable.support[browserId] = [supportItem]; + } + for (const item of compatTable.support[browserId]) { + replaceVersionsInBrowserSupport(item); + } + } + } + + // Handle deeper node as well. + parseBrowserVersion(compatNode[term]); + } +} + +function replaceVersionsInBrowserSupport(browserSupport) { + browserSupport.added = asFloatVersion(browserSupport.version_added); + browserSupport.removed = asFloatVersion(browserSupport.version_removed); +} + +function asFloatVersion(version) { + // `version` is not always a string (can be null, or a boolean) and in such case, + // we want to keep it that way. + if (typeof version !== "string") { + return version; + } + + if (version.startsWith("\u2264")) { + // MDN compatibility data started to use an expression like "≤66" for version. + // We just ignore the character here. + version = version.substring(1); + } + + return parseFloat(version); +} + +/** + * Remove all unused data from the file so it's smaller and faster to load + */ +function removeUnusedData(compatNode) { + for (const term in compatNode) { + if (term.startsWith("_")) { + // Ignore exploring if the term is _aliasOf or __compat. + continue; + } + + const compatTable = getCompatTable(compatNode, [term]); + + // A term may only have a `_aliasOf` property (e.g. for word-wrap), so we don't have + // compat data in it directly. + if (compatTable) { + // source_file references the name of the file in the MDN compat data repo where the + // property is handled. We don't make use of it so we can remove it. + delete compatTable.source_file; + + // Not used at the moment. Doesn't seem to have much information anyway + delete compatTable.description; + + if (compatTable?.support) { + for (const [browserId, supportItem] of Object.entries( + compatTable.support + )) { + // Remove any browser we won't handle + if (!TARGET_BROWSER_ID.includes(browserId)) { + delete compatTable.support[browserId]; + continue; + } + + // Remove `version_added` and `version_removed`, that are parsed in `replaceVersionsInBrowserSupport` + // and which we don't need anymore. + for (const item of supportItem) { + delete item.version_added; + delete item.version_removed; + // Those might be interesting, but we're not using them at the moment, so let's + // remove them as they can be quite lengthy + delete item.notes; + } + } + } + } + + // Handle deeper node as well. + removeUnusedData(compatNode[term]); + } +} + +function exportData(data, fileName) { + const fs = require("fs"); + const path = require("path"); + + const content = `${JSON.stringify(data)}`; + + fs.writeFile( + path.resolve(__dirname, "../dataset", fileName), + content, + err => { + if (err) { + console.error(err); + } else { + console.log(`${fileName} downloaded`); + } + } + ); +} |