diff --git a/src/common/pluginloader.js b/src/common/pluginloader.js index 3cc55a6cc..45df21407 100644 --- a/src/common/pluginloader.js +++ b/src/common/pluginloader.js @@ -42,7 +42,7 @@ function injectIfNecessary (id, url, onload, onerror) { if (id in define.moduleMap) { onload(); } else { - onerror(); + onerror('Module not inserted to module map.'); } }, onerror); } @@ -79,19 +79,49 @@ function onScriptLoadingComplete (moduleList, finishPluginLoading) { function handlePluginsObject (path, moduleList, finishPluginLoading) { // Now inject the scripts. var scriptCounter = moduleList.length; + var modulesWithProblems = []; if (!scriptCounter) { finishPluginLoading(); return; } + + function callOnScriptLoadingComplete () { + var loadedModules = moduleList.filter(function (m) { + return modulesWithProblems.indexOf(m.id) === -1; + }); + onScriptLoadingComplete(loadedModules, finishPluginLoading); + } + function scriptLoadedCallback () { if (!--scriptCounter) { - onScriptLoadingComplete(moduleList, finishPluginLoading); + callOnScriptLoadingComplete(); + } + } + + function scriptLoadedErrorCallback (id, message, source, lineno, colno, error) { + modulesWithProblems.push(id); + if (typeof message !== 'undefined') { + var messageString = message; + if (typeof message !== 'string') { + messageString = JSON.stringify(message); + } + messageString = 'Could not load all functions. Error while loading module: "' + id + '". Module will be skipped. ' + messageString; + console.error(messageString); + // use this comment as search & replace marker to insert a more app specific error handling in your after_platform_add hook. + // Decide if the app can start even if plugin loading of a specific plugin has failed. + } + if (!--scriptCounter) { + callOnScriptLoadingComplete(); } } for (var i = 0; i < moduleList.length; i++) { - injectIfNecessary(moduleList[i].id, path + moduleList[i].file, scriptLoadedCallback); + var moduleId = moduleList[i].id; + // bound function to have the module id when the error occurs. + var boundErrorCallback = scriptLoadedErrorCallback.bind(null, moduleId); + injectIfNecessary(moduleId, path + moduleList[i].file, + scriptLoadedCallback, boundErrorCallback); } } diff --git a/test/test.pluginloader.js b/test/test.pluginloader.js index 72c37876e..80d3fa02d 100644 --- a/test/test.pluginloader.js +++ b/test/test.pluginloader.js @@ -105,4 +105,32 @@ describe('pluginloader', function () { done(); }); }); + it('Test#005 : If injecting a module causes an error then the module is skipped and plugin loading continues', function (done) { + define('cordova/plugin_list', function (require, exports, module) { + module.exports = [ + { file: 'some/pathToScriptWithErrors.js', id: 'some.id.whichWillNotLoad' }, + { file: 'some/path.js', id: 'some.id' } + ]; + }); + spyOn(console, 'error'); + injectScript.and.callFake(function (url, onload, onerror) { + if (url.indexOf('some/pathToScriptWithErrors.js') > -1) { + // fake a load error for one module. + onerror('An error message.'); + } else { + // modules need to be inserted to the modulemap otherwise plugin loading will fail + define('some.id', function (require, exports, module) { + }); + onload(); + } + }); + + pluginloader.load(() => { + expect(console.error).toHaveBeenCalledWith('Could not load all functions. Error while loading module: "some.id.whichWillNotLoad". Module will be skipped. An error message.'); + var moduleMap = define.moduleMap; + expect(moduleMap['some.id']).toBeDefined(); + expect(moduleMap['some.id.whichWillNotLoad']).not.toBeDefined(); + done(); + }); + }); });