Add functions to wait for modules to be loaded

This commit is contained in:
Samuel Elliott 2018-06-24 19:56:05 +01:00
parent c8ca4fcfce
commit e2b68788bd
No known key found for this signature in database
GPG Key ID: 8420C7CDE43DC4D6
2 changed files with 66 additions and 11 deletions

View File

@ -273,19 +273,21 @@ export class ReactComponents {
}
export class ReactAutoPatcher {
/**
* Wait for React to be loaded and patch it's createElement to store all unknown components.
* Also patches of some known components.
*/
static async autoPatch() {
await this.ensureReact();
this.React = {};
this.React.unpatchCreateElement = MonkeyPatch('BD:ReactComponents:createElement', 'React').before('createElement', (component, args) => {
ReactComponents.push(args[0]);
});
const React = await WebpackModules.waitForModuleByName('React');
this.unpatchCreateElement = MonkeyPatch('BD:ReactComponents:createElement', React).before('createElement', (component, args) => ReactComponents.push(args[0]));
this.patchComponents();
}
static async ensureReact() {
while (!window.webpackJsonp || !WebpackModules.getModuleByName('React')) await new Promise(resolve => setTimeout(resolve, 10));
}
/**
* Patches a few known components.
*/
static patchComponents() {
return Promise.all([
this.patchMessage(),

View File

@ -186,10 +186,11 @@ class WebpackModules {
* Finds a module using a filter function.
* @param {Function} filter A function to use to filter modules
* @param {Boolean} first Whether to return only the first matching module
* @param {Array} modules An array of modules to search in
* @return {Any}
*/
static getModule(filter, first = true) {
const modules = this.getAllModules();
static getModule(filter, first = true, _modules) {
const modules = _modules || this.getAllModules();
const rm = [];
for (let index in modules) {
if (!modules.hasOwnProperty(index)) continue;
@ -331,6 +332,58 @@ class WebpackModules {
this.modulesLoadingCache.push(module);
}
static waitForWebpackRequire() {
return Utils.until(() => this.require, 10);
}
/**
* Waits for a module to load.
* This only returns a single module, as it can't guarentee there are no more modules that could
* match the filter, which is pretty much what that would be asking for.
* @param {Function} filter The name of a known module or a filter function
* @return {Any}
*/
static async waitForModule(filter) {
const module = this.getModule(filter);
if (module) return module;
while (this.require.m.length > this.require.c.length) {
const additionalModules = await Events.once('modules-loaded');
const module = this.getModule(filter, true, additionalModules);
if (module) return module;
}
throw new Error('All modules have now been loaded. None match the passed filter.');
}
/**
* Finds a module by it's name.
* @param {String} name The name of the module
* @param {Function} fallback A function to use to filter modules if not finding a known module
* @return {Any}
*/
static async waitForModuleByName(name, fallback) {
if (Cache.hasOwnProperty(name)) return Cache[name];
if (KnownModules.hasOwnProperty(name)) fallback = KnownModules[name];
if (!fallback) return undefined;
const module = await this.waitForModule(fallback, true);
return module ? Cache[name] = module : undefined;
}
static waitForModuleByDisplayName(props) {
return this.waitForModule(Filters.byDisplayName(props));
}
static waitForModuleByRegex(props) {
return this.waitForModule(Filters.byCode(props));
}
static waitForModuleByProps(props) {
return this.waitForModule(Filters.byProperties(props));
}
static waitForModuleByPrototypes(props) {
return this.waitForModule(Filters.byPrototypeFields(props));
}
/**
* Returns all loaded modules.
* @return {Array}