From 31cdc3633dd3249e89fd2d663ef19ee8efbf372d Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Wed, 27 May 2026 19:26:05 -0700 Subject: [PATCH 1/3] Use Array.indexOf instead of Array.includes for legacy support Resolves #7876 --- src/controller/id3-track-controller.ts | 2 +- src/controller/interstitials-controller.ts | 2 +- src/controller/interstitials-schedule.ts | 2 +- src/controller/subtitle-track-controller.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/controller/id3-track-controller.ts b/src/controller/id3-track-controller.ts index f9c6e16cf3f..6a5c6a41a79 100644 --- a/src/controller/id3-track-controller.ts +++ b/src/controller/id3-track-controller.ts @@ -360,7 +360,7 @@ export class ID3TrackController implements ComponentAPI { if (id3Track && removeOldCues) { if (id3Track.track.cues?.length) { const idsToRemove = Object.keys(dateRangeCuesAppended).filter( - (id) => !ids.includes(id), + (id) => ids.indexOf(id) === -1, ); for (let i = idsToRemove.length; i--; ) { const id = idsToRemove[i]; diff --git a/src/controller/interstitials-controller.ts b/src/controller/interstitials-controller.ts index 0cd98c507f3..a11a2d730cf 100644 --- a/src/controller/interstitials-controller.ts +++ b/src/controller/interstitials-controller.ts @@ -1789,7 +1789,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli if ( this.isInterstitial(playingItem) && - removedIds.includes(playingItem.event.identifier) + removedIds.indexOf(playingItem.event.identifier) > -1 ) { this.warn( `Interstitial "${playingItem.event.identifier}" removed while playing`, diff --git a/src/controller/interstitials-schedule.ts b/src/controller/interstitials-schedule.ts index af1d20af70d..3c396238aea 100644 --- a/src/controller/interstitials-schedule.ts +++ b/src/controller/interstitials-schedule.ts @@ -258,7 +258,7 @@ export class InterstitialsSchedule extends Logger { const ids = Object.keys(dateRanges); const removedInterstitials = previousInterstitialEvents ? previousInterstitialEvents.filter( - (event) => !ids.includes(event.identifier), + (event) => ids.indexOf(event.identifier) === -1, ) : []; if (interstitialEvents.length) { diff --git a/src/controller/subtitle-track-controller.ts b/src/controller/subtitle-track-controller.ts index deef87ed33f..91b5e18a46a 100644 --- a/src/controller/subtitle-track-controller.ts +++ b/src/controller/subtitle-track-controller.ts @@ -268,7 +268,7 @@ class SubtitleTrackController extends BasePlaylistController { ? this.hls.config.enableIMSC1 : this.hls.config.enableWebVTT ) { - if (!subtitleGroups || subtitleGroups.includes(track.groupId)) { + if (!subtitleGroups || subtitleGroups.indexOf(track.groupId) > -1) { // track.id should match hls.subtitleTracks index track.id = subtitleTracks.length; subtitleTracks.push(track); From 9e690d0704534b2d4b1548baaddf21ffaeb4d1d3 Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Thu, 28 May 2026 13:10:35 -0700 Subject: [PATCH 2/3] Update the compatibility section of the README along with @babel/preset-env, es-check, and eslint configs --- .escheckrc | 45 ++++++++++++++++++++---- .eslintrc.js | 60 ++++++++++++++++---------------- README.md | 23 ++++++++---- build-config.js | 11 ++++-- src/controller/eme-controller.ts | 1 + 5 files changed, 94 insertions(+), 46 deletions(-) diff --git a/.escheckrc b/.escheckrc index d60c2b4ffed..292697b2673 100644 --- a/.escheckrc +++ b/.escheckrc @@ -1,7 +1,38 @@ -{ - "ecmaVersion": "es5", - "modules": "false", - "files": [ - "./dist/**/*.js" - ] -} +[ + { + "ecmaVersion": "es5", + "module": false, + "files": [ + "./dist/hls.js", + "./dist/hls.min.js", + "./dist/hls.light.js", + "./dist/hls.light.min.js", + "./dist/hls.worker.js" + ] + }, + { + "ecmaVersion": "es2016", + "module": false, + "checkFeatures": true, + "ignore": "globalThis,ErrorCause", + "files": [ + "./dist/hls.light.js", + "./dist/hls.light.min.js", + "./dist/hls.worker.js" + ] + }, + { + "ecmaVersion": "es2017", + "module": false, + "checkFeatures": true, + "ignore": "globalThis,ErrorCause", + "files": ["./dist/hls.js", "./dist/hls.min.js"] + }, + { + "ecmaVersion": "es2017", + "module": true, + "checkFeatures": true, + "ignore": "globalThis,ErrorCause", + "files": ["./dist/hls.mjs", "./dist/hls.light.mjs"] + } +] diff --git a/.eslintrc.js b/.eslintrc.js index ccce4ebb48a..4b084bb03a8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -2,12 +2,23 @@ const asyncKeywordConstraintMsg = 'The async keyword adds a `regenerator` dependency in the hls.js ES5 output not allowed in v1 due to bundle size constraints.'; const selfVsWindowGlobalMsg = 'Use `self` instead of `window` to access the global context everywhere (including workers).'; -const arrayFindCompatibilityMsg = - 'Usage of Array find method is restricted for compatibility.'; -const arrayFindIndexCompatibilityMsg = - 'Usage of Array findIndex method is restricted for compatibility.'; -const arrayEveryCompatibilityMsg = - 'Usage of Array every method is restricted for compatibility. Use a negative Array some check instead.'; +const arrayIncludesCompatibilityMsg = + 'Usage of Array includes method is restricted for compatibility. Use Array indexOf instead. (For String includes, suppress this with an eslint-disable comment.)'; + +const baseRestrictedSyntax = [ + { + selector: 'FunctionDeclaration[async=true]', + message: asyncKeywordConstraintMsg, + }, + { + selector: 'ArrowFunctionExpression[async=true]', + message: asyncKeywordConstraintMsg, + }, + { + selector: 'MethodDefinition[value.async=true]', + message: asyncKeywordConstraintMsg, + }, +]; module.exports = { env: { browser: true, commonjs: true, es6: true }, @@ -31,6 +42,7 @@ module.exports = { // see https://github.com/standard/eslint-config-standard // 'prettier' (https://github.com/prettier/eslint-config-prettier) must be last extends: ['eslint:recommended', 'prettier'], + ignorePatterns: ['dist/'], parser: '@typescript-eslint/parser', parserOptions: { sourceType: 'module', project: './tsconfig.json' }, plugins: ['@typescript-eslint', 'import', 'no-for-of-loops'], @@ -74,32 +86,11 @@ module.exports = { 'no-use-before-define': 'off', 'no-restricted-syntax': [ 'error', - { - selector: 'FunctionDeclaration[async=true]', - message: asyncKeywordConstraintMsg, - }, - { - selector: 'ArrowFunctionExpression[async=true]', - message: asyncKeywordConstraintMsg, - }, - { - selector: 'MethodDefinition[value.async=true]', - message: asyncKeywordConstraintMsg, - }, + ...baseRestrictedSyntax, { selector: - 'MemberExpression[property.name="find"][object.type="Identifier"]', - message: arrayFindCompatibilityMsg, - }, - { - selector: - 'MemberExpression[property.name="findIndex"][object.type="Identifier"]', - message: arrayFindIndexCompatibilityMsg, - }, - { - selector: - 'MemberExpression[property.name="every"][object.type="Identifier"]', - message: arrayEveryCompatibilityMsg, + 'MemberExpression[property.name="includes"][object.type="Identifier"]', + message: arrayIncludesCompatibilityMsg, }, ], 'import/order': [ @@ -137,5 +128,14 @@ module.exports = { '@typescript-eslint/no-misused-promises': 'error', }, }, + { + files: ['tests/**/*.ts'], + rules: { + // Tests run only in modern browsers and aren't bundled, so the + // Array.includes compatibility constraint that applies to src/ doesn't + // apply here. + 'no-restricted-syntax': ['error', ...baseRestrictedSyntax], + }, + }, ], }; diff --git a/README.md b/README.md index cd53893fe37..734000c0255 100644 --- a/README.md +++ b/README.md @@ -296,16 +296,27 @@ HLS.js is only compatible with browsers supporting MediaSource extensions (MSE) HLS.js is supported on: -- Chrome 39+ for Android -- Chrome 39+ for Desktop -- Firefox 41+ for Android -- Firefox 42+ for Desktop +- Chrome 47+ for Desktop +- Firefox 51+ for Desktop - Edge for Windows 10+ -- Safari 9+ for macOS 10.11+ +- Safari 10+ for macOS 10.11+ - Safari for iPadOS 13+ - Safari for iOS 17.1+ since HLS version [1.5.0](https://github.com/video-dev/hls.js/releases/tag/v1.5.0) using Managed Media Source (MMS) [WebKit blog](https://webkit.org/blog/14735/webkit-features-in-safari-17-1/) +- Chrome for Android 5+ +- Firefox for Android 5+ -A [Promise polyfill](https://github.com/taylorhakes/promise-polyfill) is required in browsers missing native promise support. +These versions are the targets passed to [`@babel/preset-env`](https://babeljs.io/docs/babel-preset-env) when building the UMD bundles in `dist/`. They share an **ES2016 runtime baseline**: ES5-style syntax plus native ES2016 globals (`Map`, `Set`, `Promise`, `Array.from`, `Uint8Array.from`, `Array.prototype.includes`, etc.). To keep bundle size small, no `core-js` polyfills are bundled. + +Optional features such as CMCD pull in ES2017 APIs (e.g. `Object.entries`), so the full UMD bundle effectively requires an ES2017-capable runtime. The `light` bundle excludes those features and stays at the ES2016 baseline. + +The `dist/` folder ships two distribution variants: + +- **UMD** (`dist/hls.js`, `dist/hls.min.js`, `dist/hls.light.js`, `dist/hls.light.min.js`) — embeddable directly via a `