Skip to content

Commit 178333c

Browse files
authored
Add ESLint syntax rules to restrict async syntax, no-floating-promises, and no-misused-promises (#7480)
ESlint error messages explain reasoning
1 parent 82e4695 commit 178333c

File tree

10 files changed

+192
-119
lines changed

10 files changed

+192
-119
lines changed

.eslintrc.js

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
const asyncKeywordConstraintMsg =
2+
'The async keyword adds a `regenerator` dependency in the hls.js ES5 output not allowed in v1 due to bundle size constraints.';
3+
const selfVsWindowGlobalMsg =
4+
'Use `self` instead of `window` to access the global context everywhere (including workers).';
5+
const arrayFindCompatibilityMsg =
6+
'Usage of Array find methods is restricted for compatibility.';
7+
const arrayFindIndexCompatibilityMsg =
8+
'Usage of Array findIndex methods is restricted for compatibility.';
9+
110
module.exports = {
211
env: { browser: true, commonjs: true, es6: true },
312
globals: {
@@ -27,20 +36,13 @@ module.exports = {
2736
2,
2837
{
2938
name: 'window',
30-
message:
31-
'Use `self` instead of `window` to access the global context everywhere (including workers).',
39+
message: selfVsWindowGlobalMsg,
3240
},
3341
{ name: 'SourceBuffer', message: 'Use `self.SourceBuffer`' },
3442
{ name: 'setTimeout', message: 'Use `self.setTimeout`' },
3543
{ name: 'setInterval', message: 'Use `self.setInterval`' },
3644
],
3745

38-
'no-restricted-properties': [
39-
2,
40-
{ property: 'findIndex' }, // Intended to block usage of Array.prototype.findIndex
41-
{ property: 'find' }, // Intended to block usage of Array.prototype.find
42-
],
43-
4446
'import/first': 1,
4547
'no-var': 1,
4648
'no-empty': 1,
@@ -67,6 +69,31 @@ module.exports = {
6769
'no-unused-vars': 0,
6870
'no-undef': 0,
6971
'no-use-before-define': 'off',
72+
'no-restricted-syntax': [
73+
'error',
74+
{
75+
selector: 'FunctionDeclaration[async=true]',
76+
message: asyncKeywordConstraintMsg,
77+
},
78+
{
79+
selector: 'ArrowFunctionExpression[async=true]',
80+
message: asyncKeywordConstraintMsg,
81+
},
82+
{
83+
selector: 'MethodDefinition[value.async=true]',
84+
message: asyncKeywordConstraintMsg,
85+
},
86+
{
87+
selector:
88+
'MemberExpression[property.name="find"][object.type="Identifier"]',
89+
message: arrayFindCompatibilityMsg,
90+
},
91+
{
92+
selector:
93+
'MemberExpression[property.name="findIndex"][object.type="Identifier"]',
94+
message: arrayFindIndexCompatibilityMsg,
95+
},
96+
],
7097
'import/order': [
7198
'warn',
7299
{
@@ -98,6 +125,8 @@ module.exports = {
98125
'@typescript-eslint/consistent-type-imports': 'error',
99126
'@typescript-eslint/no-import-type-side-effects': 'error',
100127
'@typescript-eslint/no-restricted-imports': 'error',
128+
'@typescript-eslint/no-floating-promises': 'error',
129+
'@typescript-eslint/no-misused-promises': 'error',
101130
},
102131
},
103132
],

src/controller/abr-controller.ts

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -845,43 +845,49 @@ class AbrController extends Logger implements AbrComponentAPI {
845845
mediaCapabilities,
846846
this.supportedCache,
847847
);
848-
levelInfo.supportedPromise.then((decodingInfo) => {
849-
if (!this.hls) {
850-
return;
851-
}
852-
levelInfo.supportedResult = decodingInfo;
853-
const levels = this.hls.levels;
854-
const index = levels.indexOf(levelInfo);
855-
if (decodingInfo.error) {
856-
this.warn(
857-
`MediaCapabilities decodingInfo error: "${
858-
decodingInfo.error
859-
}" for level ${index} ${stringify(decodingInfo)}`,
860-
);
861-
} else if (!decodingInfo.supported) {
862-
this.warn(
863-
`Unsupported MediaCapabilities decodingInfo result for level ${index} ${stringify(
864-
decodingInfo,
865-
)}`,
866-
);
867-
if (index > -1 && levels.length > 1) {
868-
this.log(`Removing unsupported level ${index}`);
869-
this.hls.removeLevel(index);
870-
if (this.hls.loadLevel === -1) {
871-
this.hls.nextLoadLevel = 0;
848+
levelInfo.supportedPromise
849+
.then((decodingInfo) => {
850+
if (!this.hls) {
851+
return;
852+
}
853+
levelInfo.supportedResult = decodingInfo;
854+
const levels = this.hls.levels;
855+
const index = levels.indexOf(levelInfo);
856+
if (decodingInfo.error) {
857+
this.warn(
858+
`MediaCapabilities decodingInfo error: "${
859+
decodingInfo.error
860+
}" for level ${index} ${stringify(decodingInfo)}`,
861+
);
862+
} else if (!decodingInfo.supported) {
863+
this.warn(
864+
`Unsupported MediaCapabilities decodingInfo result for level ${index} ${stringify(
865+
decodingInfo,
866+
)}`,
867+
);
868+
if (index > -1 && levels.length > 1) {
869+
this.log(`Removing unsupported level ${index}`);
870+
this.hls.removeLevel(index);
871+
if (this.hls.loadLevel === -1) {
872+
this.hls.nextLoadLevel = 0;
873+
}
872874
}
875+
} else if (
876+
decodingInfo.decodingInfoResults.some(
877+
(info) =>
878+
info.smooth === false || info.powerEfficient === false,
879+
)
880+
) {
881+
this.log(
882+
`MediaCapabilities decodingInfo for level ${index} not smooth or powerEfficient: ${stringify(decodingInfo)}`,
883+
);
873884
}
874-
} else if (
875-
decodingInfo.decodingInfoResults.some(
876-
(info) =>
877-
info.smooth === false || info.powerEfficient === false,
878-
)
879-
) {
880-
this.log(
881-
`MediaCapabilities decodingInfo for level ${index} not smooth or powerEfficient: ${stringify(decodingInfo)}`,
885+
})
886+
.catch((error) => {
887+
this.warn(
888+
`Error handling MediaCapabilities decodingInfo: ${error}`,
882889
);
883-
}
884-
});
890+
});
885891
} else {
886892
levelInfo.supportedResult = SUPPORTED_INFO_DEFAULT;
887893
}

src/controller/buffer-controller.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ export default class BufferController extends Logger implements ComponentAPI {
343343
: null;
344344
const trackCount = trackNames ? trackNames.length : 0;
345345
const mediaSourceOpenCallback = () => {
346+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
346347
Promise.resolve().then(() => {
347348
if (this.media && this.mediaSourceOpenOrEnded) {
348349
this._onMediaSourceOpen();
@@ -417,6 +418,7 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe
417418
>;
418419
this.sourceBuffers[sbIndex] = sbTuple as any;
419420
if (sb.updating && this.operationQueue) {
421+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
420422
this.operationQueue.prependBlocker(type);
421423
}
422424
this.trackSourceBuffer(type, track);

src/controller/eme-controller.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ class EMEController extends Logger implements ComponentAPI {
357357
} else {
358358
this.warn(`Could not renew expired session. Missing pssh initData.`);
359359
}
360+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
360361
this.removeSession(mediaKeySessionContext);
361362
}
362363

@@ -407,7 +408,7 @@ class EMEController extends Logger implements ComponentAPI {
407408
keySystemsToAttempt: KeySystems[],
408409
): Promise<KeySystemFormats> {
409410
return new Promise((resolve, reject) => {
410-
return this.getKeySystemSelectionPromise(keySystemsToAttempt)
411+
this.getKeySystemSelectionPromise(keySystemsToAttempt)
411412
.then(({ keySystem }) => {
412413
const keySystemFormat = keySystemDomainToKeySystemFormat(keySystem);
413414
if (keySystemFormat) {
@@ -774,7 +775,9 @@ class EMEController extends Logger implements ComponentAPI {
774775
});
775776
} else if (messageType === 'license-release') {
776777
if (context.keySystem === KeySystems.FAIRPLAY) {
778+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
777779
this.updateKeySession(context, strToUtf8array('acknowledged'));
780+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
778781
this.removeSession(context);
779782
}
780783
} else {
@@ -864,6 +867,7 @@ class EMEController extends Logger implements ComponentAPI {
864867
.then(() => keyUsablePromise)
865868
.catch((error) => {
866869
licenseStatus.removeAllListeners();
870+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
867871
this.removeSession(context);
868872
throw error;
869873
})
@@ -1231,17 +1235,17 @@ class EMEController extends Logger implements ComponentAPI {
12311235
}
12321236
keySessionContext.licenseXhr = xhr;
12331237

1234-
this.setupLicenseXHR(xhr, url, keySessionContext, licenseChallenge).then(
1235-
({ xhr, licenseChallenge }) => {
1238+
this.setupLicenseXHR(xhr, url, keySessionContext, licenseChallenge)
1239+
.then(({ xhr, licenseChallenge }) => {
12361240
if (keySessionContext.keySystem == KeySystems.PLAYREADY) {
12371241
licenseChallenge = this.unpackPlayReadyKeyMessage(
12381242
xhr,
12391243
licenseChallenge,
12401244
);
12411245
}
12421246
xhr.send(licenseChallenge);
1243-
},
1244-
);
1247+
})
1248+
.catch(reject);
12451249
});
12461250
}
12471251

@@ -1407,7 +1411,7 @@ class EMEController extends Logger implements ComponentAPI {
14071411
() => reject(new Error(`MediaKeySession.remove() timeout`)),
14081412
8000,
14091413
);
1410-
mediaKeysSession.remove().then(resolve);
1414+
mediaKeysSession.remove().then(resolve).catch(reject);
14111415
})
14121416
: Promise.resolve();
14131417
return removePromise

src/controller/level-controller.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ export default class LevelController extends BasePlaylistController {
263263

264264
if (levels.length === 0) {
265265
// Dispatch error after MANIFEST_LOADED is done propagating
266+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
266267
Promise.resolve().then(() => {
267268
if (this.hls) {
268269
let message = 'no level with compatible codecs found in manifest';

src/controller/stream-controller.ts

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,26 +1185,34 @@ export default class StreamController
11851185

11861186
private _loadBitrateTestFrag(fragment: Fragment, level: Level) {
11871187
fragment.bitrateTest = true;
1188-
this._doFragLoad(fragment, level).then((data) => {
1189-
const { hls } = this;
1190-
const frag = data?.frag;
1191-
if (!frag || this.fragContextChanged(frag)) {
1192-
return;
1193-
}
1194-
level.fragmentError = 0;
1195-
this.state = State.IDLE;
1196-
this.startFragRequested = false;
1197-
this.bitrateTest = false;
1198-
const stats = frag.stats;
1199-
// Bitrate tests fragments are neither parsed nor buffered
1200-
stats.parsing.start =
1201-
stats.parsing.end =
1202-
stats.buffering.start =
1203-
stats.buffering.end =
1204-
self.performance.now();
1205-
hls.trigger(Events.FRAG_LOADED, data as FragLoadedData);
1206-
frag.bitrateTest = false;
1207-
});
1188+
this._doFragLoad(fragment, level)
1189+
.then((data) => {
1190+
const { hls } = this;
1191+
const frag = data?.frag;
1192+
if (!frag || this.fragContextChanged(frag)) {
1193+
return;
1194+
}
1195+
level.fragmentError = 0;
1196+
this.state = State.IDLE;
1197+
this.startFragRequested = false;
1198+
this.bitrateTest = false;
1199+
const stats = frag.stats;
1200+
// Bitrate tests fragments are neither parsed nor buffered
1201+
stats.parsing.start =
1202+
stats.parsing.end =
1203+
stats.buffering.start =
1204+
stats.buffering.end =
1205+
self.performance.now();
1206+
hls.trigger(Events.FRAG_LOADED, data as FragLoadedData);
1207+
frag.bitrateTest = false;
1208+
})
1209+
.catch((reason) => {
1210+
if (this.state === State.STOPPED || this.state === State.ERROR) {
1211+
return;
1212+
}
1213+
this.warn(reason);
1214+
this.resetFragmentLoading(fragment);
1215+
});
12081216
}
12091217

12101218
private _handleTransmuxComplete(transmuxResult: TransmuxerResult) {

src/demux/sample-aes.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,16 @@ class SampleAesDecrypter {
5656
encryptedData.byteOffset + encryptedData.length,
5757
);
5858

59-
this.decryptBuffer(encryptedBuffer).then((decryptedBuffer: ArrayBuffer) => {
60-
const decryptedData = new Uint8Array(decryptedBuffer);
61-
curUnit.set(decryptedData, 16);
59+
this.decryptBuffer(encryptedBuffer)
60+
.then((decryptedBuffer: ArrayBuffer) => {
61+
const decryptedData = new Uint8Array(decryptedBuffer);
62+
curUnit.set(decryptedData, 16);
6263

63-
if (!this.decrypter.isSync()) {
64-
this.decryptAacSamples(samples, sampleIndex + 1, callback);
65-
}
66-
});
64+
if (!this.decrypter.isSync()) {
65+
this.decryptAacSamples(samples, sampleIndex + 1, callback);
66+
}
67+
})
68+
.catch(callback);
6769
}
6870

6971
decryptAacSamples(
@@ -136,13 +138,15 @@ class SampleAesDecrypter {
136138
const decodedData = discardEPB(curUnit.data);
137139
const encryptedData = this.getAvcEncryptedData(decodedData);
138140

139-
this.decryptBuffer(encryptedData.buffer).then((decryptedBuffer) => {
140-
curUnit.data = this.getAvcDecryptedUnit(decodedData, decryptedBuffer);
141+
this.decryptBuffer(encryptedData.buffer)
142+
.then((decryptedBuffer) => {
143+
curUnit.data = this.getAvcDecryptedUnit(decodedData, decryptedBuffer);
141144

142-
if (!this.decrypter.isSync()) {
143-
this.decryptAvcSamples(samples, sampleIndex, unitIndex + 1, callback);
144-
}
145-
});
145+
if (!this.decrypter.isSync()) {
146+
this.decryptAvcSamples(samples, sampleIndex, unitIndex + 1, callback);
147+
}
148+
})
149+
.catch(callback);
146150
}
147151

148152
decryptAvcSamples(

0 commit comments

Comments
 (0)