Skip to content

Commit

Permalink
feat(injector): optionally disable automatic function annotation
Browse files Browse the repository at this point in the history
This modifies the injector to prevent automatic annotation from occurring for a given injector.

This works by passing a `true` as the 3rd or 4th parameter to createInjector(), or alternatively
to angular.module.

```js
angular.module("name", ["dependencies", "otherdeps"], configFn, true)
  .provider("$willBreak", function() {
    this.$get = function($rootScope) {
    };
  })
  .run(["$willBreak", function($willBreak) {
    // This block will never run because the noMagic flag was set to true, and the $willBreak
    // '$get' function does not have an explicit annotation.
  }]);
```

This will only affect functions with an arity greater than 0, and without an $inject property.

Related: angular#6717
  • Loading branch information
caitp committed Apr 7, 2014
1 parent f452416 commit 5eeb164
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 7 deletions.
14 changes: 9 additions & 5 deletions src/auto/injector.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ var FN_ARG_SPLIT = /,/;
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var $injectorMinErr = minErr('$injector');
function annotate(fn) {
function annotate(fn, noMagic) {
var $inject,
fnText,
argDecl,
Expand All @@ -76,6 +76,9 @@ function annotate(fn) {
if (!($inject = fn.$inject)) {
$inject = [];
if (fn.length) {
if (noMagic) {
throw $injectorMinErr('nomagic', '');
}
fnText = fn.toString().replace(STRIP_COMMENTS, '');
argDecl = fnText.match(FN_ARGS);
forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
Expand Down Expand Up @@ -587,7 +590,8 @@ function annotate(fn) {
*/


function createInjector(modulesToLoad) {
function createInjector(modulesToLoad, noMagic) {
noMagic = (noMagic === true);
var INSTANTIATING = {},
providerSuffix = 'Provider',
path = [],
Expand All @@ -605,13 +609,13 @@ function createInjector(modulesToLoad) {
providerInjector = (providerCache.$injector =
createInternalInjector(providerCache, function() {
throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
})),
}, noMagic)),
instanceCache = {},
instanceInjector = (instanceCache.$injector =
createInternalInjector(instanceCache, function(servicename) {
var provider = providerInjector.get(servicename + providerSuffix);
return instanceInjector.invoke(provider.$get, provider);
}));
}, noMagic));


forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });
Expand Down Expand Up @@ -745,7 +749,7 @@ function createInjector(modulesToLoad) {

function invoke(fn, self, locals){
var args = [],
$inject = annotate(fn),
$inject = annotate(fn, noMagic),
length, i,
key;

Expand Down
14 changes: 13 additions & 1 deletion src/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,26 @@ function setupModuleLoader(window) {
* {@link angular.Module#config Module#config()}.
* @returns {module} new module with the {@link angular.Module} api.
*/
return function module(name, requires, configFn) {
return function module(name, requires, configFn, noMagic) {
var assertNotHasOwnProperty = function(name, context) {
if (name === 'hasOwnProperty') {
throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
}
};

assertNotHasOwnProperty(name, 'module');

if (typeof configFn === "boolean") {
noMagic = configFn;
configFn = undefined;
} else if (arguments.length < 4) {
noMagic = false;
}

if (noMagic !== true) {
noMagic = false;
}

if (requires && modules.hasOwnProperty(name)) {
modules[name] = null;
}
Expand Down
7 changes: 6 additions & 1 deletion src/ngMock/angular-mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -2156,7 +2156,12 @@ if(window.jasmine || window.mocha) {
modules.unshift('ng');
var injector = currentSpec.$injector;
if (!injector) {
injector = currentSpec.$injector = angular.injector(modules);
var noMagic = modules.indexOf(true);
if (noMagic >= 0) {
modules.splice(noMagic, 1);
noMagic = true;
}
injector = currentSpec.$injector = angular.injector(modules, noMagic);
}
for(var i = 0, ii = blockFns.length; i < ii; i++) {
try {
Expand Down
38 changes: 38 additions & 0 deletions test/auto/injectorSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -864,3 +864,41 @@ describe('injector', function() {
});
});
});

describe('$injector noMagic', function() {
it('should throw if magic annotation is used by service', function() {
module(['$provide', function($provide) {
$provide.service({
'$test': function() { return this; },
'$test2': function($test) { return this; }
});
}], true);
expect (function() {
inject(['$test2', function(test2) {}]);
}).toThrowMinErr('$injector', 'nomagic');
});


it('should throw if magic annotation is used by provider', function() {
module(['$provide', function($provide) {
$provide.provider({
'$test': function() { this.$get = function($rootScope) { return $rootScope; }; },
});
}], true);
expect (function() {
inject(['$test', function(test) {}]);
}).toThrowMinErr('$injector', 'nomagic');
});


it('should throw if magic annotation is used by factory', function() {
module(['$provide', function($provide) {
$provide.factory({
'$test': function($rootScope) { return function() {} },
});
}], true);
expect (function() {
inject(['$test', function(test) {}]);
}).toThrowMinErr('$injector', 'nomagic');
});
});

0 comments on commit 5eeb164

Please sign in to comment.