mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-16 23:30:23 +00:00
feat($injector): provide API for retrieving function annotations
This commit is contained in:
parent
416a783040
commit
4361efb03b
2 changed files with 131 additions and 38 deletions
|
|
@ -42,19 +42,32 @@ var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
|
|||
var FN_ARG_SPLIT = /,/;
|
||||
var FN_ARG = /^\s*(_?)(.+?)\1\s*$/;
|
||||
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
|
||||
function inferInjectionArgs(fn) {
|
||||
assertArgFn(fn);
|
||||
if (!fn.$inject) {
|
||||
var args = fn.$inject = [];
|
||||
var fnText = fn.toString().replace(STRIP_COMMENTS, '');
|
||||
var argDecl = fnText.match(FN_ARGS);
|
||||
forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
|
||||
arg.replace(FN_ARG, function(all, underscore, name){
|
||||
args.push(name);
|
||||
function annotate(fn) {
|
||||
var $inject,
|
||||
fnText,
|
||||
argDecl,
|
||||
last;
|
||||
|
||||
if (typeof fn == 'function') {
|
||||
if (!($inject = fn.$inject)) {
|
||||
$inject = [];
|
||||
fnText = fn.toString().replace(STRIP_COMMENTS, '');
|
||||
argDecl = fnText.match(FN_ARGS);
|
||||
forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
|
||||
arg.replace(FN_ARG, function(all, underscore, name){
|
||||
$inject.push(name);
|
||||
});
|
||||
});
|
||||
});
|
||||
fn.$inject = $inject;
|
||||
}
|
||||
} else if (isArray(fn)) {
|
||||
last = fn.length - 1;
|
||||
assertArgFn(fn[last], 'fn')
|
||||
$inject = fn.slice(0, last);
|
||||
} else {
|
||||
assertArgFn(fn, 'fn', true);
|
||||
}
|
||||
return fn.$inject;
|
||||
return $inject;
|
||||
}
|
||||
|
||||
///////////////////////////////////////
|
||||
|
|
@ -152,6 +165,87 @@ function inferInjectionArgs(fn) {
|
|||
* @returns {Object} new instance of `Type`.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.module.AUTO.$injector#annotate
|
||||
* @methodOf angular.module.AUTO.$injector
|
||||
*
|
||||
* @description
|
||||
* Returns an array of service names which the function is requesting for injection. This API is used by the injector
|
||||
* to determine which services need to be injected into the function when the function is invoked. There are three
|
||||
* ways in which the function can be annotated with the needed dependencies.
|
||||
*
|
||||
* # Argument names
|
||||
*
|
||||
* The simplest form is to extract the dependencies from the arguments of the function. This is done by converting
|
||||
* the function into a string using `toString()` method and extracting the argument names.
|
||||
* <pre>
|
||||
* // Given
|
||||
* function MyController($scope, $route) {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* // Then
|
||||
* expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
|
||||
* </pre>
|
||||
*
|
||||
* This method does not work with code minfication / obfuscation. For this reason the following annotation strategies
|
||||
* are supported.
|
||||
*
|
||||
* # The `$injector` property
|
||||
*
|
||||
* If a function has an `$inject` property and its value is an array of strings, then the strings represent names of
|
||||
* services to be injected into the function.
|
||||
* <pre>
|
||||
* // Given
|
||||
* var MyController = function(obfuscatedScope, obfuscatedRoute) {
|
||||
* // ...
|
||||
* }
|
||||
* // Define function dependencies
|
||||
* MyController.$inject = ['$scope', '$route'];
|
||||
*
|
||||
* // Then
|
||||
* expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
|
||||
* </pre>
|
||||
*
|
||||
* # The array notation
|
||||
*
|
||||
* It is often desirable to inline Injected functions and that's when setting the `$inject` property is very
|
||||
* inconvenient. In these situations using the array notation to specify the dependencies in a way that survives
|
||||
* minification is a better choice:
|
||||
*
|
||||
* <pre>
|
||||
* // We wish to write this (not minification / obfuscation safe)
|
||||
* injector.invoke(function($compile, $rootScope) {
|
||||
* // ...
|
||||
* });
|
||||
*
|
||||
* // We are forced to write break inlining
|
||||
* var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
|
||||
* // ...
|
||||
* };
|
||||
* tmpFn.$inject = ['$compile', '$rootScope'];
|
||||
* injector.invoke(tempFn);
|
||||
*
|
||||
* // To better support inline function the inline annotation is supported
|
||||
* injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
|
||||
* // ...
|
||||
* }]);
|
||||
*
|
||||
* // Therefore
|
||||
* expect(injector.annotate(
|
||||
* ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
|
||||
* ).toEqual(['$compile', '$rootScope']);
|
||||
* </pre>
|
||||
*
|
||||
* @param {function|Array.<string|Function>} fn Function for which dependent service names need to be retrieved as described
|
||||
* above.
|
||||
*
|
||||
* @returns {Array.<string>} The names of the services which the function requires.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
|
|
@ -454,23 +548,11 @@ function createInjector(modulesToLoad) {
|
|||
|
||||
function invoke(fn, self, locals){
|
||||
var args = [],
|
||||
$inject,
|
||||
length,
|
||||
$inject = annotate(fn),
|
||||
length, i,
|
||||
key;
|
||||
|
||||
if (typeof fn == 'function') {
|
||||
$inject = inferInjectionArgs(fn);
|
||||
length = $inject.length;
|
||||
} else {
|
||||
if (isArray(fn)) {
|
||||
$inject = fn;
|
||||
length = $inject.length - 1;
|
||||
fn = $inject[length];
|
||||
}
|
||||
assertArgFn(fn, 'fn');
|
||||
}
|
||||
|
||||
for(var i = 0; i < length; i++) {
|
||||
for(i = 0, length = $inject.length; i < length; i++) {
|
||||
key = $inject[i];
|
||||
args.push(
|
||||
locals && locals.hasOwnProperty(key)
|
||||
|
|
@ -478,6 +560,11 @@ function createInjector(modulesToLoad) {
|
|||
: getService(key, path)
|
||||
);
|
||||
}
|
||||
if (!fn.$inject) {
|
||||
// this means that we must be an array.
|
||||
fn = fn[length];
|
||||
}
|
||||
|
||||
|
||||
// Performance optimization: http://jsperf.com/apply-vs-call-vs-invoke
|
||||
switch (self ? -1 : args.length) {
|
||||
|
|
@ -510,7 +597,8 @@ function createInjector(modulesToLoad) {
|
|||
return {
|
||||
invoke: invoke,
|
||||
instantiate: instantiate,
|
||||
get: getService
|
||||
get: getService,
|
||||
annotate: annotate
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,11 +123,11 @@ describe('injector', function() {
|
|||
it('should return $inject', function() {
|
||||
function fn() {}
|
||||
fn.$inject = ['a'];
|
||||
expect(inferInjectionArgs(fn)).toBe(fn.$inject);
|
||||
expect(inferInjectionArgs(function() {})).toEqual([]);
|
||||
expect(inferInjectionArgs(function () {})).toEqual([]);
|
||||
expect(inferInjectionArgs(function () {})).toEqual([]);
|
||||
expect(inferInjectionArgs(function /* */ () {})).toEqual([]);
|
||||
expect(annotate(fn)).toBe(fn.$inject);
|
||||
expect(annotate(function() {})).toEqual([]);
|
||||
expect(annotate(function () {})).toEqual([]);
|
||||
expect(annotate(function () {})).toEqual([]);
|
||||
expect(annotate(function /* */ () {})).toEqual([]);
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -142,43 +142,48 @@ describe('injector', function() {
|
|||
*/
|
||||
_c,
|
||||
/* {some type} */ d) { extraParans();}
|
||||
expect(inferInjectionArgs($f_n0)).toEqual(['$a', 'b_', '_c', 'd']);
|
||||
expect(annotate($f_n0)).toEqual(['$a', 'b_', '_c', 'd']);
|
||||
expect($f_n0.$inject).toEqual(['$a', 'b_', '_c', 'd']);
|
||||
});
|
||||
|
||||
|
||||
it('should strip leading and trailing underscores from arg name during inference', function() {
|
||||
function beforeEachFn(_foo_) { /* foo = _foo_ */ };
|
||||
expect(inferInjectionArgs(beforeEachFn)).toEqual(['foo']);
|
||||
expect(annotate(beforeEachFn)).toEqual(['foo']);
|
||||
});
|
||||
|
||||
|
||||
it('should handle no arg functions', function() {
|
||||
function $f_n0() {}
|
||||
expect(inferInjectionArgs($f_n0)).toEqual([]);
|
||||
expect(annotate($f_n0)).toEqual([]);
|
||||
expect($f_n0.$inject).toEqual([]);
|
||||
});
|
||||
|
||||
|
||||
it('should handle no arg functions with spaces in the arguments list', function() {
|
||||
function fn( ) {}
|
||||
expect(inferInjectionArgs(fn)).toEqual([]);
|
||||
expect(annotate(fn)).toEqual([]);
|
||||
expect(fn.$inject).toEqual([]);
|
||||
});
|
||||
|
||||
|
||||
it('should handle args with both $ and _', function() {
|
||||
function $f_n0($a_) {}
|
||||
expect(inferInjectionArgs($f_n0)).toEqual(['$a_']);
|
||||
expect(annotate($f_n0)).toEqual(['$a_']);
|
||||
expect($f_n0.$inject).toEqual(['$a_']);
|
||||
});
|
||||
|
||||
|
||||
it('should throw on non function arg', function() {
|
||||
expect(function() {
|
||||
inferInjectionArgs({});
|
||||
annotate({});
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
|
||||
it('should publish annotate API', function() {
|
||||
expect(injector.annotate).toBe(annotate);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue