diff --git a/scripts/migrations/008-remove-redundant-flags.js b/scripts/migrations/008-remove-redundant-flags.js new file mode 100644 index 00000000000000..1259de14b3c1fe --- /dev/null +++ b/scripts/migrations/008-remove-redundant-flags.js @@ -0,0 +1,171 @@ +#!/usr/bin/env node +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +'use strict'; +const fs = require('fs'); +const path = require('path'); +const { platform } = require('os'); + +const compareVersions = require('compare-versions'); + +const bcd = require('../..'); + +/** Determines if the OS is Windows */ +const IS_WINDOWS = platform() === 'win32'; + +let twoYearsAgo = new Date(Date.now()); +twoYearsAgo.setFullYear(twoYearsAgo.getFullYear() - 2); + +const getEarliestVersion = (...args) => { + const versions = args + .filter(version => typeof version === 'string') + .map(version => version.replace('≤', '')); + + let earliestVersion = versions[0]; + + for (const version of versions) { + if (compareVersions.compare(earliestVersion, version, '>')) + earliestVersion = version; + } + + return earliestVersion; +}; + +const removeRedundantFlags = (key, value) => { + if (key === '__compat') { + for (const [browser, originalSupportData] of Object.entries( + value.support, + )) { + if (originalSupportData !== undefined) { + const supportData = Array.isArray(originalSupportData) + ? originalSupportData + : [originalSupportData]; + const result = []; + + const simpleStatement = supportData.find(statement => { + const ignoreKeys = new Set([ + 'version_removed', + 'notes', + 'partial_implementation', + ]); + const keys = Object.keys(statement).filter( + key => !ignoreKeys.has(key), + ); + return keys.length === 1; + }); + + for (let i = 0; i < supportData.length; i++) { + let addData = true; + + if (supportData[i].flags) { + const versionToCheck = getEarliestVersion( + supportData[i].version_removed || + (simpleStatement && supportData[i].version_added), + simpleStatement && simpleStatement.version_added, + ); + + if (typeof versionToCheck === 'string') { + const releaseDate = new Date( + bcd.browsers[browser].releases[ + versionToCheck.replace('≤', '') + ].release_date, + ); + + if (releaseDate <= twoYearsAgo) { + addData = false; + } + } + } + + if (addData) result.push(supportData[i]); + } + + if (result.length == 0) { + if (!simpleStatement) { + value.support[browser] = { version_added: false }; + } else { + continue; + } + } else if (result.length == 1) { + value.support[browser] = result[0]; + } else { + value.support[browser] = result; + } + } + } + } + return value; +}; + +/** + * @param {Promise} filename + */ +const fixRedundantFlags = filename => { + const actual = fs.readFileSync(filename, 'utf-8').trim(); + const expected = JSON.stringify( + JSON.parse(actual, removeRedundantFlags), + null, + 2, + ); + + if (IS_WINDOWS) { + // prevent false positives from git.core.autocrlf on Windows + actual = actual.replace(/\r/g, ''); + expected = expected.replace(/\r/g, ''); + } + + if (actual !== expected) { + fs.writeFileSync(filename, expected + '\n', 'utf-8'); + } +}; + +if (require.main === module) { + /** + * @param {string[]} files + */ + function load(...files) { + for (let file of files) { + if (file.indexOf(__dirname) !== 0) { + file = path.resolve(__dirname, '..', '..', file); + } + + if (!fs.existsSync(file)) { + continue; // Ignore non-existent files + } + + if (fs.statSync(file).isFile()) { + if (path.extname(file) === '.json') { + fixRedundantFlags(file); + } + + continue; + } + + const subFiles = fs.readdirSync(file).map(subfile => { + return path.join(file, subfile); + }); + + load(...subFiles); + } + } + + if (process.argv[2]) { + load(process.argv[2]); + } else { + load( + 'api', + 'css', + 'html', + 'http', + 'svg', + 'javascript', + 'mathml', + 'test', + 'webdriver', + 'webextensions', + ); + } +} + +module.exports = { removeRedundantFlags, fixRedundantFlags }; diff --git a/scripts/migrations/008-remove-redundant-flags.test.js b/scripts/migrations/008-remove-redundant-flags.test.js new file mode 100644 index 00000000000000..4c9267e42e53a1 --- /dev/null +++ b/scripts/migrations/008-remove-redundant-flags.test.js @@ -0,0 +1,344 @@ +#!/usr/bin/env node +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +'use strict'; +const fs = require('fs'); +const path = require('path'); +const chalk = require('chalk'); +const { platform } = require('os'); + +const { removeRedundantFlags } = require('./008-remove-redundant-flags.js'); + +const tests = [ + { + input: { + test1: { + __compat: { + support: { + chrome: [ + { + version_added: '70', + }, + { + version_added: '21', + version_removed: '65', + flags: [ + { + type: 'preference', + name: '#service-worker-payment-apps', + value_to_set: 'Enabled', + }, + ], + }, + ], + }, + status: { + experimental: true, + standard_track: false, + deprecated: false, + }, + }, + }, + }, + output: { + test1: { + __compat: { + support: { + chrome: { + version_added: '70', + }, + }, + status: { + experimental: true, + standard_track: false, + deprecated: false, + }, + }, + }, + }, + }, + { + input: { + test2: { + __compat: { + support: { + chrome: [ + { + version_added: '62', + }, + { + version_added: '21', + version_removed: '80', + flags: [ + { + type: 'preference', + name: '#service-worker-payment-apps', + value_to_set: 'Enabled', + }, + ], + }, + ], + }, + status: { + experimental: true, + standard_track: false, + deprecated: false, + }, + }, + }, + }, + output: { + test2: { + __compat: { + support: { + chrome: { + version_added: '62', + }, + }, + status: { + experimental: true, + standard_track: false, + deprecated: false, + }, + }, + }, + }, + }, + { + input: { + test3: { + __compat: { + support: { + chrome: [ + { + version_added: '62', + }, + { + version_added: '21', + flags: [ + { + type: 'preference', + name: '#service-worker-payment-apps', + value_to_set: 'Enabled', + }, + ], + }, + ], + }, + status: { + experimental: true, + standard_track: false, + deprecated: false, + }, + }, + }, + }, + output: { + test3: { + __compat: { + support: { + chrome: { + version_added: '62', + }, + }, + status: { + experimental: true, + standard_track: false, + deprecated: false, + }, + }, + }, + }, + }, + { + input: { + test4: { + __compat: { + support: { + chrome: [ + { + version_added: '42', + flags: [ + { + type: 'preference', + name: '#enable-experimental-web-features', + value_to_set: 'Enabled', + }, + ], + }, + { + version_added: '21', + version_removed: '45', + flags: [ + { + type: 'preference', + name: '#service-worker-payment-apps', + value_to_set: 'Enabled', + }, + ], + }, + ], + }, + status: { + experimental: true, + standard_track: false, + deprecated: false, + }, + }, + }, + }, + output: { + test4: { + __compat: { + support: { + chrome: { + version_added: '42', + flags: [ + { + type: 'preference', + name: '#enable-experimental-web-features', + value_to_set: 'Enabled', + }, + ], + }, + }, + status: { + experimental: true, + standard_track: false, + deprecated: false, + }, + }, + }, + }, + }, + { + input: { + test5: { + __compat: { + support: { + chrome: { + version_added: '42', + version_removed: '43', + flags: [ + { + type: 'preference', + name: '#enable-experimental-web-features', + value_to_set: 'Enabled', + }, + ], + }, + }, + status: { + experimental: true, + standard_track: false, + deprecated: false, + }, + }, + }, + }, + output: { + test5: { + __compat: { + support: { + chrome: { + version_added: false, + }, + }, + status: { + experimental: true, + standard_track: false, + deprecated: false, + }, + }, + }, + }, + }, + { + input: { + test6: { + __compat: { + support: { + chrome: [ + { + version_added: '80', + }, + { + version_added: '21', + version_removed: '80', + flags: [ + { + type: 'preference', + name: '#service-worker-payment-apps', + value_to_set: 'Enabled', + }, + ], + }, + ], + }, + status: { + experimental: true, + standard_track: false, + deprecated: false, + }, + }, + }, + }, + output: { + test6: { + __compat: { + support: { + chrome: [ + { + version_added: '80', + }, + { + version_added: '21', + version_removed: '80', + flags: [ + { + type: 'preference', + name: '#service-worker-payment-apps', + value_to_set: 'Enabled', + }, + ], + }, + ], + }, + status: { + experimental: true, + standard_track: false, + deprecated: false, + }, + }, + }, + }, + }, +]; + +const testFixRedundantFlags = (logger = console) => { + let hasErrors = false; + for (let i = 0; i < tests.length; i++) { + let expected = JSON.stringify(tests[i]['output'], null, 2); + let output = JSON.stringify( + JSON.parse(JSON.stringify(tests[i]['input']), removeRedundantFlags), + null, + 2, + ); + + if (output !== expected) { + logger.error(chalk`{red Redundant flags aren't removed properly!} + {yellow Actual: {bold ${output}}} + {green Expected: {bold ${expected}}}`); + hasErrors = true; + } + } + + return hasErrors; +}; + +if (require.main === module) { + testFixRedundantFlags(); +} + +module.exports = testFixRedundantFlags; diff --git a/test/test-migrations.js b/test/test-migrations.js index 086a308a4f8011..16304752d49838 100644 --- a/test/test-migrations.js +++ b/test/test-migrations.js @@ -8,6 +8,7 @@ const path = require('path'); const chalk = require('chalk'); const m002 = require('../scripts/migrations/002-remove-webview-flags.test.js'); +const m008 = require('../scripts/migrations/008-remove-redundant-flags.test.js'); /** * @returns {boolean} If the migrations aren't functioning properly @@ -23,6 +24,7 @@ const testMigrations = () => { }; m002(logger); + m008(logger); if (errors.length) { console.error(