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

Module info #15225

Closed
wants to merge 3 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
7 changes: 7 additions & 0 deletions docs/content/error/ng/aobj.ngdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@ngdoc error
@name ng:aobj
@fullName Invalid Argument
@description

The argument passed should be an object. Check the value that was passed to the function where
this error was thrown.
3 changes: 2 additions & 1 deletion src/AngularPublic.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,5 +265,6 @@ function publishExternalAPI(angular) {
$$cookieReader: $$CookieReaderProvider
});
}
]);
])
.info({ angularVersion: '"NG_VERSION_FULL"' });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not call it just version (since it's already namespaced by the module name).
Especially in other modules (ngAnimate, ngAria, etc).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I was leaning towards the idea that you would stamp your module with the version of Angular that it was designed for, rather than the version of the module itself...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me angularVersion seems more intuitive; otherwise I'd feel we're talking about the module version as Pete said.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tend to think it as the module version indeed. But it doesn't make any real difference. angularVersion works for me.

}
24 changes: 24 additions & 0 deletions src/auto/injector.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,28 @@ function annotate(fn, strictDi, name) {
* As an array of injection names, where the last item in the array is the function to call.
*/

/**
* @ngdoc property
* @name $injector#modules
* @type {Object}
* @description
* A hash containing all the modules that have been loaded into the
* $injector.
*
* You can use this property to find out information about a module via the
* {@link angular.Module#info `myModule.info(...)`} method.
*
* For example:
*
* ```
* var info = $injector.modules['ngAnimate'].info();
* ```
*
* **Do not use this property to attempt to modify the modules after the application
* has been bootstrapped.**
*/


/**
* @ngdoc method
* @name $injector#get
Expand Down Expand Up @@ -673,6 +695,7 @@ function createInjector(modulesToLoad, strictDi) {
instanceInjector = protoInstanceInjector;

providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
instanceInjector.modules = providerInjector.modules = createMap();
var runBlocks = loadModules(modulesToLoad);
instanceInjector = protoInstanceInjector.get('$injector');
instanceInjector.strictDi = strictDi;
Expand Down Expand Up @@ -768,6 +791,7 @@ function createInjector(modulesToLoad, strictDi) {
try {
if (isString(module)) {
moduleFn = angularModule(module);
instanceInjector.modules[module] = moduleFn;
runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
runInvokeQueue(moduleFn._invokeQueue);
runInvokeQueue(moduleFn._configBlocks);
Expand Down
42 changes: 42 additions & 0 deletions src/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ function setupModuleLoader(window) {
* @returns {angular.Module} new module with the {@link angular.Module} api.
*/
return function module(name, requires, configFn) {

var info = {};

var assertNotHasOwnProperty = function(name, context) {
if (name === 'hasOwnProperty') {
throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
Expand Down Expand Up @@ -114,6 +117,45 @@ function setupModuleLoader(window) {
_configBlocks: configBlocks,
_runBlocks: runBlocks,

/**
* @ngdoc method
* @name angular.Module#info
* @module ng
*
* @param {Object=} info Information about the module
* @returns {Object|Module} The current info object for this module if called as a getter,
* or `this` if called as a setter.
*
* @description
* Read and write custom information about this module.
* For example you could put the version of the module in here.
*
* ```js
* angular.module('myModule', []).info({ version: '1.0.0' });
* ```
*
* The version could then be read back out by accessing the module elsewhere:
*
* ```
* var version = angular.module('myModule').info().version;
* ```
*
* You can also retrieve this information during runtime via the
* {@link $injector#modules `$injector.modules`} property:
*
* ```js
* var version = $injector.modules['myModule'].info().version;
* ```
*/
info: function(value) {
if (isDefined(value)) {
if (!isObject(value)) throw ngMinErr('aobj', 'Argument \'{0}\' must be an object', 'value');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be value instead of 'value' (i.e. without the quotes), right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. I don't think so. The idea is that the error can be general and be used with other parameters names

Copy link
Member

@gkalpak gkalpak Oct 9, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. Then why do you need the {0}? Why don't you just write 'Argument \'value\' must be an object' directly? I guess in order to make it easier to recognize the part that is interchangeable.
👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not 100% sure but I think that the format string has to be consistent for all usage of the error.

info = value;
return this;
}
return info;
},

/**
* @ngdoc property
* @name angular.Module#requires
Expand Down
5 changes: 4 additions & 1 deletion src/loader.prefix
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@
*/
'use strict';
(function() {
function isFunction(value) {return typeof value === 'function';};
function isFunction(value) {return typeof value === 'function';}
function isDefined(value) {return typeof value !== 'undefined';}
function isObject(value) {return value !== null && typeof value === 'object';}

1 change: 1 addition & 0 deletions src/ngAnimate/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,7 @@ angular.module('ngAnimate', [], function initAngularHelpers() {
isFunction = angular.isFunction;
isElement = angular.isElement;
})
.info({ angularVersion: '"NG_VERSION_FULL"' })
.directive('ngAnimateSwap', ngAnimateSwapDirective)

.directive('ngAnimateChildren', $$AnimateChildrenDirective)
Expand Down
1 change: 1 addition & 0 deletions src/ngAria/aria.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
* {@link guide/accessibility Developer Guide}.
*/
var ngAriaModule = angular.module('ngAria', ['ng']).
info({ angularVersion: '"NG_VERSION_FULL"' }).
provider('$aria', $AriaProvider);

/**
Expand Down
1 change: 1 addition & 0 deletions src/ngCookies/cookies.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@


angular.module('ngCookies', ['ng']).
info({ angularVersion: '"NG_VERSION_FULL"' }).
/**
* @ngdoc provider
* @name $cookiesProvider
Expand Down
1 change: 1 addition & 0 deletions src/ngMessageFormat/messageFormatService.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ var toJson;
var $$stringify;

var module = window['angular']['module']('ngMessageFormat', ['ng']);
module['info']({ 'angularVersion': '"NG_VERSION_FULL"' });
module['factory']('$$messageFormat', $$MessageFormatFactory);
module['config'](['$provide', function($provide) {
$interpolateMinErr = window['angular']['$interpolateMinErr'];
Expand Down
1 change: 1 addition & 0 deletions src/ngMessages/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ angular.module('ngMessages', [], function initAngularHelpers() {
isString = angular.isString;
jqLite = angular.element;
})
.info({ angularVersion: '"NG_VERSION_FULL"' })

/**
* @ngdoc directive
Expand Down
5 changes: 3 additions & 2 deletions src/ngMock/angular-mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,7 @@ angular.mock.TzDate.prototype = Date.prototype;
* You need to require the `ngAnimateMock` module in your test suite for instance `beforeEach(module('ngAnimateMock'))`
*/
angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
.info({ angularVersion: '"NG_VERSION_FULL"' })

.config(['$provide', function($provide) {

Expand Down Expand Up @@ -2404,7 +2405,7 @@ angular.module('ngMock', ['ng']).provider({
$provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
$provide.decorator('$controller', createControllerDecorator($compileProvider));
$provide.decorator('$httpBackend', angular.mock.$httpBackendDecorator);
}]);
}]).info({ angularVersion: '"NG_VERSION_FULL"' });

/**
* @ngdoc module
Expand All @@ -2419,7 +2420,7 @@ angular.module('ngMock', ['ng']).provider({
*/
angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
$provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
}]);
}]).info({ angularVersion: '"NG_VERSION_FULL"' });

/**
* @ngdoc service
Expand Down
3 changes: 2 additions & 1 deletion src/ngParseExt/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,5 @@ function isValidIdentifierContinue(ch, cp) {
angular.module('ngParseExt', [])
.config(['$parseProvider', function($parseProvider) {
$parseProvider.setIdentifierFns(isValidIdentifierStart, isValidIdentifierContinue);
}]);
}])
.info({ angularVersion: '"NG_VERSION_FULL"' });
1 change: 1 addition & 0 deletions src/ngResource/resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ function shallowClearAndCopy(src, dst) {
*
*/
angular.module('ngResource', ['ng']).
info({ angularVersion: '"NG_VERSION_FULL"' }).
provider('$resource', function ResourceProvider() {
var PROTOCOL_AND_IPV6_REGEX = /^https?:\/\/\[[^\]]*][^/]*/;

Expand Down
1 change: 1 addition & 0 deletions src/ngRoute/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var noop;
/* global -ngRouteModule */
var ngRouteModule = angular.
module('ngRoute', []).
info({ angularVersion: '"NG_VERSION_FULL"' }).
provider('$route', $RouteProvider).
// Ensure `$route` will be instantiated in time to capture the initial `$locationChangeSuccess`
// event (unless explicitly disabled). This is necessary in case `ngView` is included in an
Expand Down
4 changes: 3 additions & 1 deletion src/ngSanitize/sanitize.js
Original file line number Diff line number Diff line change
Expand Up @@ -547,4 +547,6 @@ function sanitizeText(chars) {


// define ngSanitize module and register $sanitize service
angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
angular.module('ngSanitize', [])
.provider('$sanitize', $SanitizeProvider)
.info({ angularVersion: '"NG_VERSION_FULL"' });
2 changes: 2 additions & 0 deletions src/ngTouch/touch.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
/* global -ngTouch */
var ngTouch = angular.module('ngTouch', []);

ngTouch.info({ angularVersion: '"NG_VERSION_FULL"' });

ngTouch.provider('$touch', $TouchProvider);

function nodeName_(element) {
Expand Down
31 changes: 31 additions & 0 deletions test/auto/injectorSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,37 @@

/* globals support: false */

describe('injector.modules', function() {
it('should expose the loaded module info on the instance injector', function() {
var test1 = angular.module('test1', ['test2']).info({ version: '1.1' });
var test2 = angular.module('test2', []).info({ version: '1.2' });
module('test1');
inject(['$injector', function($injector) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OOC, is the array necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't we decide to make all our tests minification proof?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sure there was an issue about it

Copy link
Member

@gkalpak gkalpak Oct 9, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't remember anything like that (why would we)?
Maybe we have decided to make ngMocks minification-safe (because it makes it easier for some people to incorporate it into their workflow), but not our tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes you are right @gkalpak (as always) - I was thinking of this: #13542

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They don't have to be minification-proof but shouldn't we aim at making them pass in strict DI mode?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is not such thing as "strict DI" in tests :)
(Also none of the other tests are, so it is obviously not a problem.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is not such thing as "strict DI" in tests :)

What I meant by that is that we are manually annotating the injected functions, if strict DI is enabled.

expect(Object.keys($injector.modules)).toEqual(['ng', 'ngLocale', 'ngMock', 'test1', 'test2']);
expect($injector.modules['test1'].info()).toEqual({ version: '1.1' });
expect($injector.modules['test2'].info()).toEqual({ version: '1.2' });
}]);
});

it('should expose the loaded module info on the provider injector', function() {
var providerInjector;
var test1 = angular.module('test1', ['test2']).info({ version: '1.1' });
var test2 = angular.module('test2', [])
.info({ version: '1.2' })
.provider('test', ['$injector', function($injector) {
providerInjector = $injector;
return { $get: function() {} };
}]);
module('test1');
// needed to ensure that the provider blocks are executed
inject();

expect(Object.keys(providerInjector.modules)).toEqual(['ng', 'ngLocale', 'ngMock', 'test1', 'test2']);
expect(providerInjector.modules['test1'].info()).toEqual({ version: '1.1' });
expect(providerInjector.modules['test2'].info()).toEqual({ version: '1.2' });
});
});

describe('injector', function() {
var providers;
var injector;
Expand Down
31 changes: 31 additions & 0 deletions test/loaderSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,35 @@ describe('module loader', function() {
it('should expose `$$minErr` on the `angular` object', function() {
expect(window.angular.$$minErr).toEqual(jasmine.any(Function));
});

describe('Module', function() {
describe('info()', function() {
var theModule;

beforeEach(function() {
theModule = angular.module('theModule', []);
});

it('should default to an empty object', function() {
expect(theModule.info()).toEqual({});
});

it('should store the object passed as a param', function() {
theModule.info({ version: '1.2' });
expect(theModule.info()).toEqual({ version: '1.2' });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a test to verify it overwrites the previous info object.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

});

it('should throw if the parameter is not an object', function() {
expect(function() {
theModule.info('some text');
}).toThrowMinErr('ng', 'aobj');
});

it('should completely replace the previous info object', function() {
theModule.info({ value: 'X' });
theModule.info({ newValue: 'Y' });
expect(theModule.info()).toEqual({ newValue: 'Y' });
});
});
});
});