summaryrefslogtreecommitdiffstats
path: root/devtools/shared/compatibility/bin/update.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/shared/compatibility/bin/update.js')
-rw-r--r--devtools/shared/compatibility/bin/update.js202
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`);
+ }
+ }
+ );
+}