Skip to content

Commit b06176d

Browse files
committed
feat(loadModules) : options to customize raising errors
When some error occurs while loading a module, an error will raise containing the original error stack. Sometimes the final error contains a lot of stacks which make it difficult to read. closes angular#14744
1 parent bc09c31 commit b06176d

File tree

7 files changed

+119
-34
lines changed

7 files changed

+119
-34
lines changed

src/.eslintrc.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515
"splice": false,
1616
"push": false,
1717
"toString": false,
18-
"minErrConfig": false,
18+
"errConfigObj": false,
1919
"errorHandlingConfig": false,
20+
"loadModulesErrorConfig":false,
2021
"isValidObjectMaxDepth": false,
2122
"ngMinErr": false,
2223
"_angular": false,

src/Angular.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
splice,
1111
push,
1212
toString,
13-
minErrConfig,
13+
errConfigObj,
1414
errorHandlingConfig,
1515
isValidObjectMaxDepth,
1616
ngMinErr,

src/auto/injector.js

+23-16
Original file line numberDiff line numberDiff line change
@@ -830,8 +830,7 @@ function createInjector(modulesToLoad, strictDi) {
830830
provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
831831
}
832832
}
833-
834-
try {
833+
function tryBlock() {
835834
if (isString(module)) {
836835
moduleFn = angularModule(module);
837836
instanceInjector.modules[module] = moduleFn;
@@ -845,20 +844,28 @@ function createInjector(modulesToLoad, strictDi) {
845844
} else {
846845
assertArgFn(module, 'module');
847846
}
848-
} catch (e) {
849-
if (isArray(module)) {
850-
module = module[module.length - 1];
851-
}
852-
if (e.message && e.stack && e.stack.indexOf(e.message) === -1) {
853-
// Safari & FF's stack traces don't contain error.message content
854-
// unlike those of Chrome and IE
855-
// So if stack doesn't contain message, we create a new string that contains both.
856-
// Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
857-
// eslint-disable-next-line no-ex-assign
858-
e = e.message + '\n' + e.stack;
859-
}
860-
throw $injectorMinErr('modulerr', 'Failed to instantiate module {0} due to:\n{1}',
861-
module, e.stack || e.message || e);
847+
}
848+
849+
if (!errorHandlingConfig().isModuleError) {
850+
tryBlock();
851+
} else {
852+
try {
853+
tryBlock();
854+
} catch (e) {
855+
if (isArray(module)) {
856+
module = module[module.length - 1];
857+
}
858+
if (errorHandlingConfig().isModuleStack && e.message && e.stack && e.stack.indexOf(e.message) === -1) {
859+
// Safari & FF's stack traces don't contain error.message content
860+
// unlike those of Chrome and IE
861+
// So if stack doesn't contain message, we create a new string that contains both.
862+
// Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
863+
// eslint-disable-next-line no-ex-assign
864+
e = e.message + '\n' + e.stack;
865+
}
866+
throw $injectorMinErr('modulerr', 'Failed to instantiate module {0} due to:\n{1}',
867+
module,errorHandlingConfig().isModuleStack ? (e.stack || e.message || e) : e.message);
868+
}
862869
}
863870
});
864871
return runBlocks;

src/minErr.js

+20-9
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
'use strict';
22

33
/* exported
4-
minErrConfig,
4+
errConfigObj,
55
errorHandlingConfig,
66
isValidObjectMaxDepth
77
*/
88

9-
var minErrConfig = {
9+
var errConfigObj = {
1010
objectMaxDepth: 5,
11-
isUrlParameters:true
11+
isUrlParameters:true,
12+
isModuleStack:true,
13+
isModuleError:true
1214
};
13-
1415
/**
1516
* @ngdoc function
1617
* @name angular.errorHandlingConfig
@@ -33,17 +34,27 @@ var minErrConfig = {
3334
* Default: 5
3435
* * `isUrlParameters` **{Boolean}** - Specifies wether the generated url error will contain the paramters or not.
3536
* Default: true
37+
* * `isModuleStack` **{Boolean}** - Specifies wether the generated error for a module has a stack
38+
* Default: true
39+
* * `isModuleError` **{Boolean}** - Specifies wether the error will be rethrown for each module
40+
* Default: true
3641
*/
3742
function errorHandlingConfig(config) {
3843
if (isObject(config)) {
3944
if (isDefined(config.objectMaxDepth)) {
40-
minErrConfig.objectMaxDepth = isValidObjectMaxDepth(config.objectMaxDepth) ? config.objectMaxDepth : NaN;
45+
errConfigObj.objectMaxDepth = isValidObjectMaxDepth(config.objectMaxDepth) ? config.objectMaxDepth : NaN;
4146
}
4247
if (isDefined(config.isUrlParameters) && isBoolean(config.isUrlParameters)) {
43-
minErrConfig.isUrlParameters = config.isUrlParameters;
48+
errConfigObj.isUrlParameters = config.isUrlParameters;
49+
}
50+
if (isDefined(config.isModuleStack) && isBoolean(config.isModuleStack)) {
51+
errConfigObj.isModuleStack = config.isModuleStack;
52+
}
53+
if (isDefined(config.isModuleError) && isBoolean(config.isModuleError)) {
54+
errConfigObj.isModuleError = config.isModuleError;
4455
}
4556
} else {
46-
return minErrConfig;
57+
return errConfigObj;
4758
}
4859
}
4960

@@ -94,7 +105,7 @@ function minErr(module, ErrorConstructor) {
94105
template = arguments[1],
95106
message = '[' + (module ? module + ':' : '') + code + '] ',
96107
templateArgs = sliceArgs(arguments, 2).map(function(arg) {
97-
return toDebugString(arg, minErrConfig.objectMaxDepth);
108+
return toDebugString(arg, errConfigObj.objectMaxDepth);
98109
}),
99110
paramPrefix, i;
100111

@@ -110,7 +121,7 @@ function minErr(module, ErrorConstructor) {
110121

111122
message += '\nhttp://errors.angularjs.org/"NG_VERSION_FULL"/' +
112123
(module ? module + '/' : '') + code;
113-
if (minErrConfig.isUrlParameters) {
124+
if (errConfigObj.isUrlParameters) {
114125
for (i = 0, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
115126
message += paramPrefix + 'p' + i + '=' + encodeURIComponent(templateArgs[i]);
116127
}

test/.eslintrc.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
/* angular.js */
2727
"angular": false,
28-
"minErrConfig": false,
28+
"errConfigObj": false,
2929
"errorHandlingConfig": false,
3030
"msie": false,
3131
"jqLite": false,

test/auto/injectorSpec.js

+34
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,13 @@ describe('injector', function() {
940940

941941

942942
describe('error handling', function() {
943+
var obj = angular.copy(errorHandlingConfig());
944+
945+
afterEach(function() {
946+
errorHandlingConfig({isModuleStack:obj.isModuleStack});
947+
errorHandlingConfig({isModuleError:obj.isModuleError});
948+
});
949+
943950
it('should handle wrong argument type', function() {
944951
expect(function() {
945952
createInjector([
@@ -957,6 +964,33 @@ describe('injector', function() {
957964
}).toThrowMinErr('$injector', 'modulerr', /Failed to instantiate module .+ due to:\n.*MyError/);
958965
});
959966

967+
it('should just rethrow the exception if ´isModuleError´ is false', function() {
968+
expect(function() {
969+
errorHandlingConfig({isModuleError:false});
970+
createInjector([function() {
971+
throw new Error('MyError');
972+
}], {});
973+
}).toThrow(new Error('MyError'));
974+
});
975+
976+
it('should not generate the stack for the failed module if ´isModuleStack´ is false', function() {
977+
errorHandlingConfig({isModuleStack:false});
978+
expect(function() {
979+
createInjector([function() {
980+
throw new Error('MyError');
981+
}], {});
982+
983+
}).not.toThrowMinErr('$injector', 'modulerr', /(\w+@|at\s+\w+).+:\d+:\d+/);
984+
});
985+
986+
it('should generate the stack if ´isModuleStack´ option is true', function() {
987+
errorHandlingConfig({isModuleStack:true});
988+
expect(function() {
989+
createInjector([function() {
990+
throw new Error('MyError');
991+
}], {});
992+
}).toThrowMinErr('$injector', 'modulerr', /(\w+@|at\s+\w+).+:\d+:\d+/);
993+
});
960994

961995
it('should decorate the missing service error with module name', function() {
962996
angular.module('TestModule', [], function(xyzzy) {});

test/minErrSpec.js

+38-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
'use strict';
22

33
describe('errors', function() {
4-
var originalObjectMaxDepthInErrorMessage = minErrConfig.objectMaxDepth;
5-
var originalIsUrlParameters = minErrConfig.isUrlParameters;
4+
var originalObj = angular.copy(errConfigObj);
5+
66
afterEach(function() {
7-
minErrConfig.objectMaxDepth = originalObjectMaxDepthInErrorMessage;
8-
minErrConfig.isUrlParameters = originalIsUrlParameters;
7+
errConfigObj.objectMaxDepth = originalObj.objectMaxDepth;
8+
errConfigObj.isUrlParameters = originalObj.isUrlParameters;
9+
errConfigObj.isModuleError = originalObj.isModuleError;
10+
errConfigObj.isModuleStack = originalObj.isModuleStack;
911
});
1012

1113
describe('errorHandlingConfig', function() {
@@ -21,7 +23,7 @@ describe('errors', function() {
2123

2224
it('should not change objectMaxDepth when undefined is supplied', function() {
2325
errorHandlingConfig({objectMaxDepth: undefined});
24-
expect(errorHandlingConfig().objectMaxDepth).toBe(originalObjectMaxDepthInErrorMessage);
26+
expect(errorHandlingConfig().objectMaxDepth).toBe(originalObj.objectMaxDepth);
2527
});
2628

2729
they('should set objectMaxDepth to NaN when $prop is supplied',
@@ -45,7 +47,37 @@ describe('errors', function() {
4547
});
4648
it('should not change its value when non-boolean is supplied', function() {
4749
errorHandlingConfig({isUrlParameters:123});
48-
expect(errorHandlingConfig().isUrlParameters).toBe(originalIsUrlParameters);
50+
expect(errorHandlingConfig().isUrlParameters).toBe(originalObj.isUrlParameters);
51+
});
52+
});
53+
describe('isModuleStack',function() {
54+
it('should get default isModuleStack', function() {
55+
expect(errorHandlingConfig().isModuleStack).toBe(true);
56+
});
57+
it('should set isModuleStack', function() {
58+
errorHandlingConfig({isModuleStack:false});
59+
expect(errorHandlingConfig().isModuleStack).toBe(false);
60+
errorHandlingConfig({isModuleStack:true});
61+
expect(errorHandlingConfig().isModuleStack).toBe(true);
62+
});
63+
it('should not change its value when non-boolean is supplied', function() {
64+
errorHandlingConfig({isModuleStack:123});
65+
expect(errorHandlingConfig().isModuleStack).toBe(originalObj.isModuleStack);
66+
});
67+
});
68+
describe('isModuleError',function() {
69+
it('should get default isModuleError', function() {
70+
expect(errorHandlingConfig().isModuleError).toBe(true);
71+
});
72+
it('should set isModuleError', function() {
73+
errorHandlingConfig({isModuleError:false});
74+
expect(errorHandlingConfig().isModuleError).toBe(false);
75+
errorHandlingConfig({isModuleError:true});
76+
expect(errorHandlingConfig().isModuleError).toBe(true);
77+
});
78+
it('should not change its value when non-boolean is supplied', function() {
79+
errorHandlingConfig({isModuleError:123});
80+
expect(errorHandlingConfig().isModuleError).toBe(originalObj.isModuleError);
4981
});
5082
});
5183

0 commit comments

Comments
 (0)