Last active
December 5, 2021 23:23
-
-
Save connorjclark/832ee48c82dc36d203e4a7710a470806 to your computer and use it in GitHub Desktop.
web platform latest stable features script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const browserCompat = require('@mdn/browser-compat-data'); | |
const browsersToConsider = [ | |
'chrome', | |
'edge', | |
'firefox', | |
'safari_ios', | |
'safari', | |
]; | |
const releasesCache = {}; | |
// Returns newest first. | |
function sortBrowserReleasesByReleaseDate(browser) { | |
if (releasesCache[browser]) return releasesCache[browser]; | |
const releases = browserCompat.browsers[browser].releases; | |
const asArray = Object.entries(releases).map(entry => ({ version: entry[0], details: entry[1] })); | |
releasesCache[browser] = asArray.sort((a, b) => new Date(b.details.release_date) - new Date(a.details.release_date)); | |
return asArray; | |
} | |
/** | |
* Stable. | |
* @param {Date} month | |
*/ | |
function findBrowsersReleasedInMonth(month) { | |
const result = []; | |
for (const browser of Object.keys(browserCompat.browsers)) { | |
if (!browsersToConsider.includes(browser)) continue; | |
// To simplify, only consider the latest version from each browser. | |
const releaseEntry = sortBrowserReleasesByReleaseDate(browser).find(({ version, details }) => { | |
if (!details.release_date) return false; | |
const releaseDate = new Date(details.release_date); | |
return releaseDate.getYear() === month.getYear() && releaseDate.getMonth() === month.getMonth(); | |
}); | |
if (!releaseEntry) continue; | |
result.push({ | |
browser, | |
version: releaseEntry.version, | |
}); | |
} | |
return result; | |
} | |
/** | |
* @param {Date} month | |
*/ | |
function findStableBrowsersInMonth(month) { | |
const result = []; | |
for (const browser of Object.keys(browserCompat.browsers)) { | |
if (!browsersToConsider.includes(browser)) continue; | |
const releaseEntries = sortBrowserReleasesByReleaseDate(browser).filter(({ details }) => { | |
if (!details.release_date) return false; | |
const releaseDate = new Date(details.release_date); | |
return month.getYear() > releaseDate.getYear() || (month.getYear() === releaseDate.getYear() && month.getMonth() > releaseDate.getMonth()); | |
}); | |
const releaseEntry = releaseEntries[0]; | |
if (!releaseEntry) continue; | |
result.push({ | |
browser, | |
version: releaseEntry.version, | |
}); | |
} | |
return result; | |
} | |
function findBrowserVersionReleaseIndex(browser, version) { | |
const releases = sortBrowserReleasesByReleaseDate(browser); | |
return releases.findIndex(release => release.version === version); | |
} | |
const compatData = getCompatData(); | |
function getCompatData() { | |
let compats = []; | |
function findCompats(obj, path = []) { | |
if (!(typeof obj === 'object' && obj)) return; | |
for (const [key, child] of Object.entries(obj)) { | |
if (child.__compat) { | |
compats.push({ | |
id: [...path, key].join('.'), | |
compat: child.__compat, | |
}); | |
} else { | |
findCompats(child, [...path, key]); | |
} | |
} | |
} | |
const { browser, ...rootCompatObj } = browserCompat; | |
findCompats(rootCompatObj); | |
return compats; | |
} | |
function getFeatureSetForBrowserVersion({ browser, version }) { | |
const features = []; | |
for (const feature of compatData) { | |
if (!feature.compat.support[browser]) continue; | |
const supportStatement = Array.isArray(feature.compat.support[browser]) ? | |
feature.compat.support[browser][0] : | |
feature.compat.support[browser]; | |
if (!supportStatement.version_added) continue; | |
if (supportStatement.version_removed) continue; | |
if (supportStatement.flags) continue; | |
const isSupported = supportStatement.version_added === true || | |
findBrowserVersionReleaseIndex(browser, version) <= findBrowserVersionReleaseIndex(browser, supportStatement.version_added); | |
if (isSupported) { | |
if (supportStatement.flags) console.log(feature.id, supportStatement); | |
features.push(feature.id); | |
} | |
} | |
browserCompat.javascript.operators.optional_chaining.__compat.support | |
return new Set(features); | |
} | |
function getFeatureSetForBrowserVersions(browserVerisons) { | |
return setIntersections(...browserVerisons.map(getFeatureSetForBrowserVersion)); | |
} | |
function setIntersections(...sets) { | |
const result = new Set(); | |
const [firstSet, ...rest] = sets; | |
for (const item of firstSet) { | |
if (rest.every(set => set.has(item))) { | |
result.add(item); | |
} | |
} | |
return result; | |
} | |
function setDifference(a, b) { | |
const result = new Set(); | |
for (const item of a) { | |
if (b.has(item)) continue; | |
result.add(item); | |
} | |
for (const item of b) { | |
if (a.has(item)) continue; | |
result.add(item); | |
} | |
return result; | |
} | |
function yearInReview() { | |
for (let i = 1; i <= 12; i++) { | |
const lastMonth = new Date(i === 1 ? '2020/12/01' : `2021/${i - 1}/01`); | |
const thisMonth = new Date(`2021/${i}/01`); | |
const newReleases = findBrowsersReleasedInMonth(new Date(thisMonth)); | |
const lastMonthStableBrowsers = findStableBrowsersInMonth(lastMonth); | |
const thisMonthStableBrowsers = findStableBrowsersInMonth(thisMonth); | |
const lastMonthFeatureSet = getFeatureSetForBrowserVersions(lastMonthStableBrowsers); | |
const thisMonthFeatureSet = getFeatureSetForBrowserVersions(thisMonthStableBrowsers); | |
const difference = setDifference(lastMonthFeatureSet, thisMonthFeatureSet); | |
console.log(`in ${thisMonth.toLocaleString('en-us', { month: 'short', year: 'numeric' })} these browsers were released:`, newReleases); | |
console.log(`and these features became stable across all major browsers:`, difference); | |
console.log('------------------------'); | |
} | |
} | |
yearInReview(); | |
// console.log(getFeatureSetForBrowserVersion({ browser: 'safari', version: '15.1' }).has('api.AudioWorkletNode')); | |
// console.log(browserCompat.api.AudioWorkletNode.__compat.support); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
output (ran Nov 22 2021): | |
in Jan 2021 these browsers were released: [ | |
{ browser: 'chrome', version: '88' }, | |
{ browser: 'edge', version: '88' }, | |
{ browser: 'firefox', version: '85' } | |
] | |
and these features became stable across all major browsers: Set(0) {} | |
------------------------ | |
in Feb 2021 these browsers were released: [ { browser: 'firefox', version: '86' } ] | |
and these features became stable across all major browsers: Set(2) { 'css.selectors.is', 'css.selectors.where' } | |
------------------------ | |
in Mar 2021 these browsers were released: [ | |
{ browser: 'chrome', version: '89' }, | |
{ browser: 'edge', version: '89' }, | |
{ browser: 'firefox', version: '87' } | |
] | |
and these features became stable across all major browsers: Set(0) {} | |
------------------------ | |
in Apr 2021 these browsers were released: [ | |
{ browser: 'chrome', version: '90' }, | |
{ browser: 'edge', version: '90' }, | |
{ browser: 'firefox', version: '88' }, | |
{ browser: 'safari', version: '14.1' }, | |
{ browser: 'safari_ios', version: '14.5' } | |
] | |
and these features became stable across all major browsers: Set(1) { 'css.properties.text-decoration-thickness' } | |
------------------------ | |
in May 2021 these browsers were released: [ | |
{ browser: 'chrome', version: '91' }, | |
{ browser: 'edge', version: '91' }, | |
{ browser: 'firefox', version: '89' } | |
] | |
and these features became stable across all major browsers: Set(56) { | |
'api.AbstractRange', | |
'api.AudioContext', | |
'api.AudioParamMap', | |
'api.AudioWorklet', | |
'api.AudioWorkletGlobalScope', | |
'api.AudioWorkletNode', | |
'api.AudioWorkletProcessor', | |
'api.BaseAudioContext', | |
'api.ConstantSourceNode', | |
'api.IIRFilterNode', | |
'api.OfflineAudioContext', | |
'api.PannerNode', | |
'api.PerformancePaintTiming', | |
'api.StereoPannerNode', | |
'api.Worklet', | |
'api.WorkletGlobalScope', | |
'css.properties.border-block-color', | |
'css.properties.border-block-style', | |
'css.properties.border-block-width', | |
'css.properties.border-block', | |
'css.properties.border-inline-color', | |
'css.properties.border-inline-style', | |
'css.properties.border-inline-width', | |
'css.properties.border-inline', | |
'css.properties.column-gap.flex_context', | |
'css.properties.gap.flex_context', | |
'css.properties.inset-block-end', | |
'css.properties.inset-block-start', | |
'css.properties.inset-block', | |
'css.properties.inset-inline-end', | |
'css.properties.inset-inline-start', | |
'css.properties.inset-inline', | |
'css.properties.inset', | |
'css.properties.margin-block', | |
'css.properties.margin-inline', | |
'css.properties.padding-block', | |
'css.properties.padding-inline', | |
'css.properties.row-gap.flex_context', | |
'css.properties.scroll-margin-block-end', | |
'css.properties.scroll-margin-block-start', | |
'css.properties.scroll-margin-block', | |
'css.properties.scroll-margin-bottom', | |
'css.properties.scroll-margin-inline-end', | |
'css.properties.scroll-margin-inline-start', | |
'css.properties.scroll-margin-inline', | |
'css.properties.scroll-margin-left', | |
'css.properties.scroll-margin-right', | |
'css.properties.scroll-margin-top', | |
'css.properties.scroll-padding-bottom', | |
'css.properties.scroll-padding-left', | |
'css.properties.scroll-padding-right', | |
'css.properties.scroll-padding-top', | |
'css.properties.scroll-padding', | |
'css.selectors.file-selector-button', | |
'javascript.builtins.FinalizationRegistry', | |
'javascript.builtins.WeakRef' | |
} | |
------------------------ | |
in Jun 2021 these browsers were released: [] | |
and these features became stable across all major browsers: Set(0) {} | |
------------------------ | |
in Jul 2021 these browsers were released: [ | |
{ browser: 'chrome', version: '92' }, | |
{ browser: 'edge', version: '92' }, | |
{ browser: 'firefox', version: '90' } | |
] | |
and these features became stable across all major browsers: Set(0) {} | |
------------------------ | |
in Aug 2021 these browsers were released: [ | |
{ browser: 'chrome', version: '93' }, | |
{ browser: 'firefox', version: '91' } | |
] | |
and these features became stable across all major browsers: Set(1) { 'css.properties.scroll-margin' } | |
------------------------ | |
in Sep 2021 these browsers were released: [ | |
{ browser: 'chrome', version: '94' }, | |
{ browser: 'edge', version: '94' }, | |
{ browser: 'firefox', version: '92' }, | |
{ browser: 'safari', version: '15' }, | |
{ browser: 'safari_ios', version: '15' } | |
] | |
and these features became stable across all major browsers: Set(2) { 'api.VisualViewport', 'css.properties.tab-size' } | |
------------------------ | |
in Oct 2021 these browsers were released: [ | |
{ browser: 'chrome', version: '95' }, | |
{ browser: 'edge', version: '95' }, | |
{ browser: 'firefox', version: '93' }, | |
{ browser: 'safari', version: '15.1' }, | |
{ browser: 'safari_ios', version: '15.1' } | |
] | |
and these features became stable across all major browsers: Set(113) { | |
'api.EXT_float_blend', | |
'api.FormDataEvent', | |
'api.ImageBitmapRenderingContext', | |
'api.MediaMetadata', | |
'api.MediaSession', | |
'api.SubmitEvent', | |
'api.WEBGL_color_buffer_float', | |
'api.WEBGL_draw_buffers', | |
'api.WebGL2RenderingContext', | |
'api.WebGLTransformFeedback', | |
'api.WebGLVertexArrayObject', | |
'api.createImageBitmap', | |
'css.properties.aspect-ratio', | |
'css.properties.border-end-end-radius', | |
'css.properties.border-end-start-radius', | |
'css.properties.border-start-end-radius', | |
'css.properties.border-start-start-radius', | |
'css.properties.scroll-padding-block-end', | |
'css.properties.scroll-padding-block-start', | |
'css.properties.scroll-padding-block', | |
'css.properties.scroll-padding-inline-end', | |
'css.properties.scroll-padding-inline-start', | |
'css.properties.scroll-padding-inline', | |
'javascript.builtins.BigInt64Array', | |
'javascript.builtins.BigUint64Array', | |
'webextensions.api.alarms.Alarm', | |
'webextensions.api.alarms.clear', | |
'webextensions.api.alarms.clearAll', | |
'webextensions.api.alarms.create', | |
'webextensions.api.alarms.get', | |
'webextensions.api.alarms.getAll', | |
'webextensions.api.alarms.onAlarm', | |
'webextensions.api.browserAction.ColorArray', | |
'webextensions.api.browserAction.ImageDataType', | |
'webextensions.api.browserAction.disable', | |
'webextensions.api.browserAction.enable', | |
'webextensions.api.browserAction.getBadgeBackgroundColor', | |
'webextensions.api.browserAction.getBadgeText', | |
'webextensions.api.browserAction.getPopup', | |
'webextensions.api.browserAction.getTitle', | |
'webextensions.api.browserAction.onClicked', | |
'webextensions.api.browserAction.setBadgeBackgroundColor', | |
'webextensions.api.browserAction.setBadgeText', | |
'webextensions.api.browserAction.setIcon', | |
'webextensions.api.browserAction.setPopup', | |
'webextensions.api.browserAction.setTitle', | |
'webextensions.api.commands.Command', | |
'webextensions.api.commands.getAll', | |
'webextensions.api.commands.onCommand', | |
'webextensions.api.cookies.Cookie', | |
'webextensions.api.cookies.CookieStore', | |
'webextensions.api.cookies.get', | |
'webextensions.api.cookies.getAll', | |
'webextensions.api.cookies.getAllCookieStores', | |
'webextensions.api.cookies.remove', | |
'webextensions.api.cookies.sameSiteStatus', | |
'webextensions.api.cookies.set', | |
'webextensions.api.extensionTypes.ImageDetails', | |
'webextensions.api.extensionTypes.ImageFormat', | |
'webextensions.api.extensionTypes.RunAt', | |
'webextensions.api.i18n.getAcceptLanguages', | |
'webextensions.api.i18n.getMessage', | |
'webextensions.api.i18n.getUILanguage', | |
'webextensions.api.pageAction.ImageDataType', | |
'webextensions.api.pageAction.getPopup', | |
'webextensions.api.pageAction.getTitle', | |
'webextensions.api.pageAction.onClicked', | |
'webextensions.api.pageAction.setIcon', | |
'webextensions.api.pageAction.setPopup', | |
'webextensions.api.pageAction.setTitle', | |
'webextensions.api.permissions.contains', | |
'webextensions.api.permissions.getAll', | |
'webextensions.api.permissions.onAdded', | |
'webextensions.api.permissions.onRemoved', | |
'webextensions.api.permissions.Permissions', | |
'webextensions.api.permissions.remove', | |
'webextensions.api.permissions.request', | |
'webextensions.api.runtime.MessageSender', | |
'webextensions.api.runtime.OnInstalledReason', | |
'webextensions.api.runtime.PlatformArch', | |
'webextensions.api.runtime.PlatformInfo', | |
'webextensions.api.runtime.PlatformOs', | |
'webextensions.api.runtime.Port', | |
'webextensions.api.runtime.connect', | |
'webextensions.api.runtime.connectNative', | |
'webextensions.api.runtime.getBackgroundPage', | |
'webextensions.api.runtime.getManifest', | |
'webextensions.api.runtime.getPlatformInfo', | |
'webextensions.api.runtime.getURL', | |
'webextensions.api.runtime.id', | |
'webextensions.api.runtime.lastError', | |
'webextensions.api.runtime.onConnect', | |
'webextensions.api.runtime.onInstalled', | |
'webextensions.api.runtime.onMessage', | |
'webextensions.api.runtime.onMessageExternal', | |
'webextensions.api.runtime.onStartup', | |
'webextensions.api.runtime.openOptionsPage', | |
'webextensions.api.runtime.reload', | |
'webextensions.api.runtime.sendMessage', | |
'webextensions.api.runtime.sendNativeMessage', | |
'webextensions.api.runtime.setUninstallURL', | |
'webextensions.api.storage.StorageArea', | |
'webextensions.api.storage.StorageChange', | |
'webextensions.api.storage.local', | |
'webextensions.api.storage.onChanged', | |
'webextensions.api.storage.sync', | |
'webextensions.api.webNavigation.getAllFrames', | |
'webextensions.api.webNavigation.getFrame', | |
'webextensions.api.webNavigation.onBeforeNavigate', | |
'webextensions.api.webNavigation.onCommitted', | |
'webextensions.api.webNavigation.onCompleted', | |
'webextensions.api.webNavigation.onDOMContentLoaded', | |
'webextensions.api.webNavigation.onErrorOccurred' | |
} | |
------------------------ | |
in Nov 2021 these browsers were released: [ | |
{ browser: 'chrome', version: '96' }, | |
{ browser: 'firefox', version: '94' } | |
] | |
and these features became stable across all major browsers: Set(1) { 'api.PerformanceNavigationTiming' } | |
------------------------ | |
in Dec 2021 these browsers were released: [ { browser: 'firefox', version: '95' } ] | |
and these features became stable across all major browsers: Set(1) { 'html.global_attributes.enterkeyhint' } | |
------------------------ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment