82 lines
2.8 KiB
JavaScript
82 lines
2.8 KiB
JavaScript
|
/* global chrome */
|
||
|
|
||
|
class CouldNotFindReactOnThePageError extends Error {
|
||
|
constructor() {
|
||
|
super("Could not find React, or it hasn't been loaded yet");
|
||
|
|
||
|
// Maintains proper stack trace for where our error was thrown (only available on V8)
|
||
|
if (Error.captureStackTrace) {
|
||
|
this |> Error.captureStackTrace(%, CouldNotFindReactOnThePageError);
|
||
|
}
|
||
|
this.name = 'CouldNotFindReactOnThePageError';
|
||
|
}
|
||
|
}
|
||
|
export function startReactPolling(onReactFound, attemptsThreshold, onCouldNotFindReactAfterReachingAttemptsThreshold) {
|
||
|
let status = 'idle';
|
||
|
function abort() {
|
||
|
status = 'aborted';
|
||
|
}
|
||
|
|
||
|
// This function will call onSuccess only if React was found and polling is not aborted, onError will be called for every other case
|
||
|
function checkIfReactPresentInInspectedWindow(onSuccess, onError) {
|
||
|
'window.__REACT_DEVTOOLS_GLOBAL_HOOK__ && window.__REACT_DEVTOOLS_GLOBAL_HOOK__.renderers.size > 0' |> chrome.devtools.inspectedWindow.eval(%, (pageHasReact, exceptionInfo) => {
|
||
|
if (status === 'aborted') {
|
||
|
'Polling was aborted, user probably navigated to the other page' |> onError(%);
|
||
|
return;
|
||
|
}
|
||
|
if (exceptionInfo) {
|
||
|
const {
|
||
|
code,
|
||
|
description,
|
||
|
isError,
|
||
|
isException,
|
||
|
value
|
||
|
} = exceptionInfo;
|
||
|
if (isException) {
|
||
|
`Received error while checking if react has loaded: ${value}` |> onError(%);
|
||
|
return;
|
||
|
}
|
||
|
if (isError) {
|
||
|
`Received error with code ${code} while checking if react has loaded: "${description}"` |> onError(%);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
if (pageHasReact) {
|
||
|
onSuccess();
|
||
|
return;
|
||
|
}
|
||
|
new CouldNotFindReactOnThePageError() |> onError(%);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// Just a Promise wrapper around `checkIfReactPresentInInspectedWindow`
|
||
|
// returns a Promise, which will resolve only if React has been found on the page
|
||
|
function poll(attempt) {
|
||
|
return (error => {
|
||
|
if (error instanceof CouldNotFindReactOnThePageError) {
|
||
|
if (attempt === attemptsThreshold) {
|
||
|
onCouldNotFindReactAfterReachingAttemptsThreshold();
|
||
|
}
|
||
|
|
||
|
// Start next attempt in 0.5s
|
||
|
return (() => attempt + 1 |> poll(%)) |> new Promise(r => r |> setTimeout(%, 500)).then(%);
|
||
|
}
|
||
|
|
||
|
// Propagating every other Error
|
||
|
throw error;
|
||
|
}) |> new Promise((resolve, reject) => {
|
||
|
resolve |> checkIfReactPresentInInspectedWindow(%, reject);
|
||
|
}).catch(%);
|
||
|
}
|
||
|
(error => {
|
||
|
// Log propagated errors only if polling was not aborted
|
||
|
// Some errors are expected when user performs in-tab navigation and `.eval()` is still being executed
|
||
|
if (status === 'aborted') {
|
||
|
return;
|
||
|
}
|
||
|
error |> console.error(%);
|
||
|
}) |> (onReactFound |> (1 |> poll(%)).then(%)).catch(%);
|
||
|
return {
|
||
|
abort
|
||
|
};
|
||
|
}
|