Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Failed module error #16299

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
"splice": false,
"push": false,
"toString": false,
"minErrConfig": false,
"errConfigObj": false,
"errorHandlingConfig": false,
"loadModulesErrorConfig":false,
"isValidObjectMaxDepth": false,
"ngMinErr": false,
"_angular": false,
Expand Down
2 changes: 1 addition & 1 deletion src/Angular.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
splice,
push,
toString,
minErrConfig,
errConfigObj,
errorHandlingConfig,
isValidObjectMaxDepth,
ngMinErr,
Expand Down
39 changes: 23 additions & 16 deletions src/auto/injector.js
Original file line number Diff line number Diff line change
Expand Up @@ -830,8 +830,7 @@ function createInjector(modulesToLoad, strictDi) {
provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
}
}

try {
function tryBlock() {
if (isString(module)) {
moduleFn = angularModule(module);
instanceInjector.modules[module] = moduleFn;
Expand All @@ -845,20 +844,28 @@ function createInjector(modulesToLoad, strictDi) {
} else {
assertArgFn(module, 'module');
}
} catch (e) {
if (isArray(module)) {
module = module[module.length - 1];
}
if (e.message && e.stack && e.stack.indexOf(e.message) === -1) {
// Safari & FF's stack traces don't contain error.message content
// unlike those of Chrome and IE
// So if stack doesn't contain message, we create a new string that contains both.
// Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
// eslint-disable-next-line no-ex-assign
e = e.message + '\n' + e.stack;
}
throw $injectorMinErr('modulerr', 'Failed to instantiate module {0} due to:\n{1}',
module, e.stack || e.message || e);
}

if (!errorHandlingConfig().isModuleError) {
tryBlock();
} else {
try {
tryBlock();
} catch (e) {
if (isArray(module)) {
module = module[module.length - 1];
}
if (errorHandlingConfig().isModuleStack && e.message && e.stack && e.stack.indexOf(e.message) === -1) {
// Safari & FF's stack traces don't contain error.message content
// unlike those of Chrome and IE
// So if stack doesn't contain message, we create a new string that contains both.
// Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
// eslint-disable-next-line no-ex-assign
e = e.message + '\n' + e.stack;
}
throw $injectorMinErr('modulerr', 'Failed to instantiate module {0} due to:\n{1}',
module,errorHandlingConfig().isModuleStack ? (e.stack || e.message || e) : e.message);
}
}
});
return runBlocks;
Expand Down
39 changes: 29 additions & 10 deletions src/minErr.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
'use strict';

/* exported
minErrConfig,
errConfigObj,
errorHandlingConfig,
isValidObjectMaxDepth
*/

var minErrConfig = {
objectMaxDepth: 5
var errConfigObj = {
objectMaxDepth: 5,
isUrlParameters:true,
isModuleStack:true,
isModuleError:true
};

/**
* @ngdoc function
* @name angular.errorHandlingConfig
Expand All @@ -30,14 +32,29 @@ var minErrConfig = {
* * `objectMaxDepth` **{Number}** - The max depth for stringifying objects. Setting to a
* non-positive or non-numeric value, removes the max depth limit.
* Default: 5
* * `isUrlParameters` **{Boolean}** - Specifies wether the generated url error will contain the paramters or not.
* Default: true
* * `isModuleStack` **{Boolean}** - Specifies wether the generated error for a module has a stack
* Default: true
* * `isModuleError` **{Boolean}** - Specifies wether the error will be rethrown for each module
* Default: true
*/
function errorHandlingConfig(config) {
if (isObject(config)) {
if (isDefined(config.objectMaxDepth)) {
minErrConfig.objectMaxDepth = isValidObjectMaxDepth(config.objectMaxDepth) ? config.objectMaxDepth : NaN;
errConfigObj.objectMaxDepth = isValidObjectMaxDepth(config.objectMaxDepth) ? config.objectMaxDepth : NaN;
}
if (isDefined(config.isUrlParameters) && isBoolean(config.isUrlParameters)) {
errConfigObj.isUrlParameters = config.isUrlParameters;
}
if (isDefined(config.isModuleStack) && isBoolean(config.isModuleStack)) {
errConfigObj.isModuleStack = config.isModuleStack;
}
if (isDefined(config.isModuleError) && isBoolean(config.isModuleError)) {
errConfigObj.isModuleError = config.isModuleError;
}
} else {
return minErrConfig;
return errConfigObj;
}
}

Expand All @@ -50,6 +67,7 @@ function isValidObjectMaxDepth(maxDepth) {
return isNumber(maxDepth) && maxDepth > 0;
}


/**
* @description
*
Expand Down Expand Up @@ -87,7 +105,7 @@ function minErr(module, ErrorConstructor) {
template = arguments[1],
message = '[' + (module ? module + ':' : '') + code + '] ',
templateArgs = sliceArgs(arguments, 2).map(function(arg) {
return toDebugString(arg, minErrConfig.objectMaxDepth);
return toDebugString(arg, errConfigObj.objectMaxDepth);
}),
paramPrefix, i;

Expand All @@ -103,9 +121,10 @@ function minErr(module, ErrorConstructor) {

message += '\nhttp://errors.angularjs.org/"NG_VERSION_FULL"/' +
(module ? module + '/' : '') + code;

for (i = 0, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
message += paramPrefix + 'p' + i + '=' + encodeURIComponent(templateArgs[i]);
if (errConfigObj.isUrlParameters) {
for (i = 0, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
message += paramPrefix + 'p' + i + '=' + encodeURIComponent(templateArgs[i]);
}
}

return new ErrorConstructor(message);
Expand Down
2 changes: 1 addition & 1 deletion test/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

/* angular.js */
"angular": false,
"minErrConfig": false,
"errConfigObj": false,
"errorHandlingConfig": false,
"msie": false,
"jqLite": false,
Expand Down
34 changes: 34 additions & 0 deletions test/auto/injectorSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,13 @@ describe('injector', function() {


describe('error handling', function() {
var obj = angular.copy(errorHandlingConfig());

afterEach(function() {
errorHandlingConfig({isModuleStack:obj.isModuleStack});
errorHandlingConfig({isModuleError:obj.isModuleError});
});

it('should handle wrong argument type', function() {
expect(function() {
createInjector([
Expand All @@ -957,6 +964,33 @@ describe('injector', function() {
}).toThrowMinErr('$injector', 'modulerr', /Failed to instantiate module .+ due to:\n.*MyError/);
});

it('should just rethrow the exception if ´isModuleError´ is false', function() {
expect(function() {
errorHandlingConfig({isModuleError:false});
createInjector([function() {
throw new Error('MyError');
}], {});
}).toThrow(new Error('MyError'));
});

it('should not generate the stack for the failed module if ´isModuleStack´ is false', function() {
errorHandlingConfig({isModuleStack:false});
expect(function() {
createInjector([function() {
throw new Error('MyError');
}], {});

}).not.toThrowMinErr('$injector', 'modulerr', /(\w+@|at\s+\w+).+:\d+:\d+/);
});

it('should generate the stack if ´isModuleStack´ option is true', function() {
errorHandlingConfig({isModuleStack:true});
expect(function() {
createInjector([function() {
throw new Error('MyError');
}], {});
}).toThrowMinErr('$injector', 'modulerr', /(\w+@|at\s+\w+).+:\d+:\d+/);
});

it('should decorate the missing service error with module name', function() {
angular.module('TestModule', [], function(xyzzy) {});
Expand Down
95 changes: 78 additions & 17 deletions test/minErrSpec.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,86 @@
'use strict';

describe('errors', function() {
var originalObjectMaxDepthInErrorMessage = minErrConfig.objectMaxDepth;
var originalObj = angular.copy(errConfigObj);

afterEach(function() {
minErrConfig.objectMaxDepth = originalObjectMaxDepthInErrorMessage;
errConfigObj.objectMaxDepth = originalObj.objectMaxDepth;
errConfigObj.isUrlParameters = originalObj.isUrlParameters;
errConfigObj.isModuleError = originalObj.isModuleError;
errConfigObj.isModuleStack = originalObj.isModuleStack;
});

describe('errorHandlingConfig', function() {
it('should get default objectMaxDepth', function() {
expect(errorHandlingConfig().objectMaxDepth).toBe(5);
describe('objectMaxDepth',function() {
it('should get default objectMaxDepth', function() {
expect(errorHandlingConfig().objectMaxDepth).toBe(5);
});

it('should set objectMaxDepth', function() {
errorHandlingConfig({objectMaxDepth: 3});
expect(errorHandlingConfig().objectMaxDepth).toBe(3);
});

it('should not change objectMaxDepth when undefined is supplied', function() {
errorHandlingConfig({objectMaxDepth: undefined});
expect(errorHandlingConfig().objectMaxDepth).toBe(originalObj.objectMaxDepth);
});

they('should set objectMaxDepth to NaN when $prop is supplied',
[NaN, null, true, false, -1, 0], function(maxDepth) {
errorHandlingConfig({objectMaxDepth: maxDepth});
expect(errorHandlingConfig().objectMaxDepth).toBeNaN();
}
);
});

it('should set objectMaxDepth', function() {
errorHandlingConfig({objectMaxDepth: 3});
expect(errorHandlingConfig().objectMaxDepth).toBe(3);
});

it('should not change objectMaxDepth when undefined is supplied', function() {
errorHandlingConfig({objectMaxDepth: undefined});
expect(errorHandlingConfig().objectMaxDepth).toBe(originalObjectMaxDepthInErrorMessage);
describe('isUrlParameters',function() {
it('should get default isUrlParameters', function() {
expect(errorHandlingConfig().isUrlParameters).toBe(true);
});
it('should set isUrlParameters', function() {
errorHandlingConfig({isUrlParameters:false});
expect(errorHandlingConfig().isUrlParameters).toBe(false);
errorHandlingConfig({isUrlParameters:true});
expect(errorHandlingConfig().isUrlParameters).toBe(true);
});
it('should not change its value when non-boolean is supplied', function() {
errorHandlingConfig({isUrlParameters:123});
expect(errorHandlingConfig().isUrlParameters).toBe(originalObj.isUrlParameters);
});
});
describe('isModuleStack',function() {
it('should get default isModuleStack', function() {
expect(errorHandlingConfig().isModuleStack).toBe(true);
});
it('should set isModuleStack', function() {
errorHandlingConfig({isModuleStack:false});
expect(errorHandlingConfig().isModuleStack).toBe(false);
errorHandlingConfig({isModuleStack:true});
expect(errorHandlingConfig().isModuleStack).toBe(true);
});
it('should not change its value when non-boolean is supplied', function() {
errorHandlingConfig({isModuleStack:123});
expect(errorHandlingConfig().isModuleStack).toBe(originalObj.isModuleStack);
});
});
describe('isModuleError',function() {
it('should get default isModuleError', function() {
expect(errorHandlingConfig().isModuleError).toBe(true);
});
it('should set isModuleError', function() {
errorHandlingConfig({isModuleError:false});
expect(errorHandlingConfig().isModuleError).toBe(false);
errorHandlingConfig({isModuleError:true});
expect(errorHandlingConfig().isModuleError).toBe(true);
});
it('should not change its value when non-boolean is supplied', function() {
errorHandlingConfig({isModuleError:123});
expect(errorHandlingConfig().isModuleError).toBe(originalObj.isModuleError);
});
});

they('should set objectMaxDepth to NaN when $prop is supplied',
[NaN, null, true, false, -1, 0], function(maxDepth) {
errorHandlingConfig({objectMaxDepth: maxDepth});
expect(errorHandlingConfig().objectMaxDepth).toBeNaN();
}
);
});

describe('minErr', function() {
Expand Down Expand Up @@ -164,5 +217,13 @@ describe('errors', function() {
expect(testError('acode', 'aproblem', 'a', 'b', 'value with space').message)
.toMatch(/^[\s\S]*\?p0=a&p1=b&p2=value%20with%20space$/);
});

it('should not generate URL query parameters when isUrlParameters is false', function() {

errorHandlingConfig({isUrlParameters:false});
expect(testError('acode', 'aproblem', 'a', 'b', 'c').message)
.not.toContain('?p0=a&p1=b&p2=c');
});

});
});