update.js (6410B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */ 4 5 // The Compatibility panel detects issues by comparing against official MDN compatibility data 6 // at https://github.com/mdn/browser-compat-data). It uses a local snapshot of the dataset. 7 // This dataset needs to be manually synchronized periodically 8 9 // The subsets from the dataset required by the Compatibility panel are: 10 // * css.properties: https://github.com/mdn/browser-compat-data/tree/master/css 11 12 // The MDN compatibility data is available as a node package ("@mdn/browser-compat-data"), 13 // which is used here to update `../dataset/css-properties.json`. 14 15 /* eslint-disable mozilla/reject-relative-requires */ 16 17 "use strict"; 18 19 const compatData = require("@mdn/browser-compat-data"); 20 const { properties } = compatData.css; 21 22 const { TARGET_BROWSER_ID } = require("../constants.js"); 23 const { getCompatTable } = require("../helpers.js"); 24 25 // Flatten all CSS properties aliases here so we don't have to do it at runtime, 26 // which is costly. 27 flattenAliases(properties); 28 parseBrowserVersion(properties); 29 removeUnusedData(properties); 30 exportData(properties, "css-properties.json"); 31 32 /** 33 * Builds a list of aliases between CSS properties, like flex and -webkit-flex, 34 * and mutates individual entries in the web compat data store for CSS properties to 35 * add their corresponding aliases. 36 */ 37 function flattenAliases(compatNode) { 38 for (const term in compatNode) { 39 if (term.startsWith("_")) { 40 // Ignore exploring if the term is _aliasOf or __compat. 41 continue; 42 } 43 44 const compatTable = getCompatTable(compatNode, [term]); 45 if (compatTable) { 46 const aliases = findAliasesFrom(compatTable); 47 48 for (const { alternative_name: name, prefix } of aliases) { 49 const alias = name || prefix + term; 50 compatNode[alias] = { _aliasOf: term }; 51 } 52 53 if (aliases.length) { 54 // Make the term accessible as the alias. 55 compatNode[term]._aliasOf = term; 56 } 57 } 58 59 // Flatten deeper node as well. 60 flattenAliases(compatNode[term]); 61 } 62 } 63 64 function findAliasesFrom(compatTable) { 65 const aliases = []; 66 67 for (const browser in compatTable.support) { 68 let supportStates = compatTable.support[browser] || []; 69 supportStates = Array.isArray(supportStates) 70 ? supportStates 71 : [supportStates]; 72 73 for (const { alternative_name: name, prefix } of supportStates) { 74 if (!prefix && !name) { 75 continue; 76 } 77 78 aliases.push({ alternative_name: name, prefix }); 79 } 80 } 81 82 return aliases; 83 } 84 85 function parseBrowserVersion(compatNode) { 86 for (const term in compatNode) { 87 if (term.startsWith("_")) { 88 // Ignore exploring if the term is _aliasOf or __compat. 89 continue; 90 } 91 92 const compatTable = getCompatTable(compatNode, [term]); 93 if (compatTable?.support) { 94 for (const [browserId, supportItem] of Object.entries( 95 compatTable.support 96 )) { 97 // supportItem is an array when there are info for both prefixed and non-prefixed 98 // versions. If it's not an array in the original data, transform it into one 99 // since we'd have to do it in MDNCompatibility at runtime otherwise. 100 if (!Array.isArray(supportItem)) { 101 compatTable.support[browserId] = [supportItem]; 102 } 103 for (const item of compatTable.support[browserId]) { 104 replaceVersionsInBrowserSupport(item); 105 } 106 } 107 } 108 109 // Handle deeper node as well. 110 parseBrowserVersion(compatNode[term]); 111 } 112 } 113 114 function replaceVersionsInBrowserSupport(browserSupport) { 115 browserSupport.added = asFloatVersion(browserSupport.version_added); 116 browserSupport.removed = asFloatVersion(browserSupport.version_removed); 117 } 118 119 function asFloatVersion(version) { 120 // `version` is not always a string (can be null, or a boolean) and in such case, 121 // we want to keep it that way. 122 if (typeof version !== "string") { 123 return version; 124 } 125 126 if (version.startsWith("\u2264")) { 127 // MDN compatibility data started to use an expression like "≤66" for version. 128 // We just ignore the character here. 129 version = version.substring(1); 130 } 131 132 return parseFloat(version); 133 } 134 135 /** 136 * Remove all unused data from the file so it's smaller and faster to load 137 */ 138 function removeUnusedData(compatNode) { 139 for (const term in compatNode) { 140 if (term.startsWith("_")) { 141 // Ignore exploring if the term is _aliasOf or __compat. 142 continue; 143 } 144 145 const compatTable = getCompatTable(compatNode, [term]); 146 147 // A term may only have a `_aliasOf` property (e.g. for word-wrap), so we don't have 148 // compat data in it directly. 149 if (compatTable) { 150 // source_file references the name of the file in the MDN compat data repo where the 151 // property is handled. We don't make use of it so we can remove it. 152 delete compatTable.source_file; 153 154 // Not used at the moment. Doesn't seem to have much information anyway 155 delete compatTable.description; 156 157 if (compatTable?.support) { 158 for (const [browserId, supportItem] of Object.entries( 159 compatTable.support 160 )) { 161 // Remove any browser we won't handle 162 if (!TARGET_BROWSER_ID.includes(browserId)) { 163 delete compatTable.support[browserId]; 164 continue; 165 } 166 167 // Remove `version_added` and `version_removed`, that are parsed in `replaceVersionsInBrowserSupport` 168 // and which we don't need anymore. 169 for (const item of supportItem) { 170 delete item.version_added; 171 delete item.version_removed; 172 // Those might be interesting, but we're not using them at the moment, so let's 173 // remove them as they can be quite lengthy 174 delete item.notes; 175 } 176 } 177 } 178 } 179 180 // Handle deeper node as well. 181 removeUnusedData(compatNode[term]); 182 } 183 } 184 185 function exportData(data, fileName) { 186 const fs = require("fs"); 187 const path = require("path"); 188 189 const content = `${JSON.stringify(data)}`; 190 191 fs.writeFile( 192 path.resolve(__dirname, "../dataset", fileName), 193 content, 194 err => { 195 if (err) { 196 console.error(err); 197 } else { 198 console.log(`${fileName} downloaded`); 199 } 200 } 201 ); 202 }