Intent to Implement CommonJS Support

684 views
Skip to first unread message

Ben Clinkinbeard

unread,
Jan 13, 2015, 9:51:20 PM1/13/15
to ang...@googlegroups.com, angul...@googlegroups.com

Summary

Change the publish script to support idiomatic CommonJS usage when installed from npm.


Motivation

Now that the Angular modules are available on npm, supporting proper CommonJS use via require() is the next logical step. This use case is widely expected by developers using npm for dependency management.


Without this change, developers still have to use a script tag to include Angular modules from within the node_modules folder. This is extremely uncommon with npm packages, and does nothing to enable Browserify and webpack support.


Compatibility Risk

Little to no risk as only the publish shell script needs to be changed.


Ongoing Technical Constraints

There should be virtually no ongoing maintenance needed since it's just a publish script.


Plans for Implementation

Simple changes to the publish script will be made to remove the main field from the package.json files and generate an index.js file for each package so that it is used when requiring by name. The index.js file will require() the standard built file and export a value(s) appropriate for the module.


The core angular module will export the angular object.


The supporting modules will export their module name (ngAnimate, ngRoute, etc.) with the exception of angular-mocks, which exports 3 values: ngMock, ngMockE2E, ngAnimateMock


This would enable code akin to the following:


var angular = require('angular');

angular.module('app', [

require('angular-animate'),

require('angular-route'),

require('angular-mocks').ngMock

]);


Prior Art

The overwhelming expectation, when a developer installs a package from npm, is that they can then use require('package-name') to load it. This change will bring Angular in line with the rest of the npm ecosystem.

Caitlin Potter

unread,
Jan 13, 2015, 10:30:11 PM1/13/15
to ang...@googlegroups.com, angul...@googlegroups.com
LGTM, but we still need to establish the conventions that people will expect

Is an npm module equivalent to an angular module?

If so, `require('angular-animate').directive("myDirective", ...);` should register a new directive in the ngAnimate module. But, in this scenario, `require('angular')` doesn't give you what you'd expect, which would be the `angular` global object.

There is also the case where one angular module exports multiple modules, like ngMock.

One way to go would be `require('angular-mocks/e2e')` or `require('angular-mocks/animate')` for the e2e and animation mock modules, and `require('angular-mocks')` for the main unit test mocks.

Or alternatively, exporting an object containing properties which are the modules to be mocked.

I would love to hear feedback from people using angular + browserify to figure out the conventions that would be most useful there.

Leo Giovanetti

unread,
Jan 13, 2015, 10:31:46 PM1/13/15
to ang...@googlegroups.com, angul...@googlegroups.com
What an interesting approach to have a way to interconnect modules between each other in an easy way.

Is Browserify in the picture?

BTW, I've been working on having a similar model using RequireJS, described on my "Large AngularJS application" post series specifically the one where I talk about the components of an architecture I came up with.

Regards.

Ben Clinkinbeard

unread,
Jan 13, 2015, 10:53:59 PM1/13/15
to ang...@googlegroups.com, angul...@googlegroups.com
Just to clarify a few things:

Is an npm module equivalent to an angular module?

Technically, what gets published to npm are packages, so that is probably a helpful vocabulary distinction we can use. In this case, each npm package corresponds to 1 or more angular modules.

`require('angular-animate').directive("myDirective", ...);` should register a new directive in the ngAnimate module.

That code would not work as the module itself is not returned. `require('angular-animate')` will return the module name as a string: "ngAnimate". That would be the case for everything but angular itself.
 
But, in this scenario, `require('angular')` doesn't give you what you'd expect, which would be the `angular` global object.

It would.  `require('angular')` would return the global angular object.

One way to go would be `require('angular-mocks/e2e')` or `require('angular-mocks/animate')` for the e2e and animation mock modules, and `require('angular-mocks')` for the main unit test mocks.

I think the slash syntax could work, but I think I would expect the paths to match the module names of ngMock, ngMockE2E and ngAnimateMock since that would more closely match the other (non-core) modules. 

Or alternatively, exporting an object containing properties which are the modules to be mocked.

That is effectively what the current approach is, except we are exposing the names, not the modules themselves.


Caitlin Potter

unread,
Jan 13, 2015, 10:55:45 PM1/13/15
to ang...@googlegroups.com, angul...@googlegroups.com
Just so it's clear, we're talking about what people expect, not commenting on your patch itself where we've already had this exact discussion :)

I would like to find out what users are expecting from this, and what would make the most sense for them

Igor Minar

unread,
Jan 13, 2015, 10:58:49 PM1/13/15
to angul...@googlegroups.com, ang...@googlegroups.com
the proposal doesn't explain if this is going to be a separate package on npm from the main package, if so what's the name of the npm package?

I'm in favor of a commonjs build *in addition* rather than instead of the current build. Ideally it should be a separate package, or same package, but published under a version number with a qualifier.

for example:
ang...@1.3.8 -> vanilla angular.js
ang...@1.3.8+cjs -> cjs angular.js

Another alternative is support UMD and have only a single package/build, but we'd need to investigate the implications of that (compatibility, payload size, etc).

--
You received this message because you are subscribed to the Google Groups "angular-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angular-dev...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Ben Clinkinbeard

unread,
Jan 13, 2015, 11:01:54 PM1/13/15
to ang...@googlegroups.com, angul...@googlegroups.com
It would use the existing packages, but it doesn't affect any existing use cases. It is simply adding support for `require()` via Browserify and webpack.

Petter Häggholm

unread,
Jan 14, 2015, 2:25:16 AM1/14/15
to ang...@googlegroups.com, angul...@googlegroups.com


On Tuesday, 13 January 2015 19:53:59 UTC-8, Ben Clinkinbeard wrote:

`require('angular-animate').directive("myDirective", ...);` should register a new directive in the ngAnimate module.

That code would not work as the module itself is not returned. `require('angular-animate')` will return the module name as a string: "ngAnimate". That would be the case for everything but angular itself.
 
Could it, perhaps, support both? I’ve never hacked on Angular, but would it be very difficult to make the module dependency mechanism aware of this, checking its arguments, and (a) if they’re strings, proceed as usual; (b) if they’re modules, just grab the names?

(It would be extra nice if the same could be done for the DI mechanism, e.g. `require('angular').service('foo', function(require('ngResource'), …);`; but that looks like it might be a lot more involved.)

Geoff Goodman

unread,
Jan 14, 2015, 7:08:03 AM1/14/15
to ang...@googlegroups.com, angul...@googlegroups.com
Is there any concrete reason why you consider exporting a string as the better alternative to exporting the actual Angular module?

I've been wrapping Angular modules up in CommonJS for quite some time with Webpack. To register a dependency on another module is as easy as:

Angular.module("myModule", [ require("angular-animate").name ]);

This is the approach I took because within my own code, there are cases where I might want to add additional services, directives, etc to an already-defined module. If I can require() that module and be returned the actual module instance, this is quite clear and intuitive. However, if instead every require needed to instead be: var myExistingModule = angular.module(require("myExistingModule")) that seems to be an extra step of unnecessary indirection.

Here is a gist of the approach I use for packaging modules in Webpack for those who are curious: https://gist.github.com/ggoodman/149751343c22892b9871 Skip through to 'why go through the hassle' for an example of code using this approach.

You will also note that I developed a very simple loader for Webpack to allow me to make this approach work with modules not otherwise designed like this: https://github.com/filearts/ng-loader/blob/master/index.js

Geoff

Jaakko Puntila

unread,
Jan 14, 2015, 7:08:22 AM1/14/15
to ang...@googlegroups.com, angul...@googlegroups.com
I think it would be more surprising to get the name as a string instead of the angular module from e.g. `require('angular-route')` (Even though `require('angular')` would return the `angular` global object).

var angular = require('angular');
var ngRoute = require('angular-route');

angular.module('app', [
    ngRoute.name
]);

The most surprising thing would still be that I'd be thinking "I only required angular-route and referenced one string, I didn't tell it to register any modules." (This would be even bigger surprise if angular-route only exported a string). In CommonJS I expect that I'd have to do something like this:

var angular = require('angular');
var ngRoute = require('angular-route');

angular.use(ngRoute);

angular.module('app', [
    ngRoute.name
]);

So angular-route would expose a function for registering itself. The function would get the `angular` object as a parameter instead of relying on globals. angular.use could be as simple as:

angular.use = function (fn) {
    fn(angular);
};

Or it could be called angular.register or something, maybe even just do `ngRoute(angular);` in the application code.

Anyway, that's what I would expect to see in CommonJS/nodeland, but I don't know how feasible this would be in Angular.
 
The following would contradict with Angular's module system, but if I went full-node, I'd want to do this: :D

angular.module('app')
    .use(require('angular-route'))
    .use(require('./myModule'))
    .factory(...);

Ben Clinkinbeard

unread,
Jan 14, 2015, 8:20:36 AM1/14/15
to ang...@googlegroups.com
Is there any concrete reason why you consider exporting a string as the better alternative to exporting the actual Angular module?

No, I actually prefer and currently use the syntax you showed. `require('some-thing').name`

There was some concern expressed that providing the module would allow developers to modify "built-in" modules, but I think providing a more widely used and expected format is more important. If the `require('angular-route').name` format were enabled, people currently shimming the modules would essentially be able to remove the shimming infrastructure and leave their code untouched. That seems like a win to me.

Paul Everitt

unread,
Jan 14, 2015, 8:51:04 AM1/14/15
to ang...@googlegroups.com


On Wednesday, January 14, 2015 at 8:20:36 AM UTC-5, Ben Clinkinbeard wrote:
If the `require('angular-route').name` format were enabled, people currently shimming the modules would essentially be able to remove the shimming infrastructure and leave their code untouched. That seems like a win to me.

Very big +1 from me on this, for the "removing" reason. 

Andrew Schmadel

unread,
Jan 15, 2015, 10:45:10 AM1/15/15
to ang...@googlegroups.com
I also can't think of much precedent for CommonJS modules that silently do things behind the scenes, and subsequently just return a string.  

If we want to write our own angular modules with CommonJS, presumably we'd want to be able to write something like:
```js
require('ngApp').controller('homeCtrl', ctrlFunc);
```

In my experience, Angular's module system is a key weakness.  If we build compatibility with established module systems, large Angular will be able to immediately take advantage of the improved encapsulation and dependency-resolution that CJS promotes.  Similarly, this gradually moves toward the module system that will be used in Angular 2.x.

While CJS support is a great short-term goal, I believe that Angular 1.x needs to adopt (or, at least support) ES6 modules.  ES6 is the future, the module specification has been finalized, and the module syntax can be efficiently transpiled into ES5 code that can be used today.  The upgrade path to Angular 2 will not necessarily be smooth, but this is one area where we can certainly ease the transition.  

-- Andrew

Marcus Nielsen

unread,
Jan 15, 2015, 10:51:13 AM1/15/15
to ang...@googlegroups.com, angul...@googlegroups.com
I would expect var angular = require('angular') to work. And then module dependencies via var dependecies = [require('foo').name, require('bar').name].

Then only thing that is bothering me right now with browserify and angular is the clash between DI and CommonJS.

Personally I put my DDO inside my index.js and there set up my angular-specific code and names. 
// Inside a folder named mn-menu

var moduleName = 'mnMenu'
var controllerName = moduleName + 'Controller'
var dependencies = []
angular
.module(moduleName, dependencies).directive(moduleName, function (){
 
return {
   
template: require('./template.html'),
    controller
: controllerName
 
}
})
 
.controller(controllerName, require('./controller'))

If I do a folder for mn-footer, then I just copy that and replace the folder-name and the variable moduleName to 'mnFooter'. Everything else remains the same.

That's how I use it right now. 
Angular becomes the boilerplate code and my controller/factories become normal functions with DI that are exported via CommonJS.
 

Caitlin Potter

unread,
Jan 15, 2015, 11:21:47 AM1/15/15
to ang...@googlegroups.com, angul...@googlegroups.com
So,

People seem to be primarily in favor of exporting the entire module object

This still doesn't solve the problem with ngMock exporting multiple modules, and it doesn't protect any core modules from being polluted (although to be fair, they could be polluted anyways).

Igor, what if we had some "lock" feature which prevented new module items from being registered? That would basically solve the second problem. Then it's just figuring out a convention for the ngMock / multiple modules exported case


On Tuesday, 13 January 2015 21:51:20 UTC-5, Ben Clinkinbeard wrote:

Ben Clinkinbeard

unread,
Jan 15, 2015, 11:32:11 AM1/15/15
to ang...@googlegroups.com, angul...@googlegroups.com
it doesn't protect any core modules from being polluted (although to be fair, they could be polluted anyways).

I don't understand why this concern is connected to CommonJS usage. If it's never been a problem before, why would it be a problem now?

 convention for the ngMock / multiple modules exported

Just to provide a concrete proposal people can weigh in on, I (again?) propose the following.

```
require('angular-mocks/ngMock')
require('angular-mocks/ngMockE2E')
require('angular-mocks/ngAnimateMock')
``` 

This would allow consistent use of `.name` when registering the modules.

Paul Everitt

unread,
Jan 15, 2015, 12:03:11 PM1/15/15
to ang...@googlegroups.com


On Thursday, January 15, 2015 at 10:45:10 AM UTC-5, Andrew Schmadel wrote:
I also can't think of much precedent for CommonJS modules that silently do things behind the scenes, and subsequently just return a string.  

If we want to write our own angular modules with CommonJS, presumably we'd want to be able to write something like:
```js
require('ngApp').controller('homeCtrl', ctrlFunc);
```

In my experience, Angular's module system is a key weakness.  If we build compatibility with established module systems, large Angular will be able to immediately take advantage of the improved encapsulation and dependency-resolution that CJS promotes.  Similarly, this gradually moves toward the module system that will be used in Angular 2.x.

While CJS support is a great short-term goal, I believe that Angular 1.x needs to adopt (or, at least support) ES6 modules.  ES6 is the future, the module specification has been finalized, and the module syntax can be efficiently transpiled into ES5 code that can be used today.  The upgrade path to Angular 2 will not necessarily be smooth, but this is one area where we can certainly ease the transition. 

I certainly would be delighted to have the 1.x line take a crack at changing the module story. However, I hope that this small step by Ben can be on a different track. His is a "what tiny step can have a big effect without opening a big RequireJS/CommonJS/UMD/ES6 argument" and big changes in 1.x.

But just to support your point, smoothing the 2.x transition would be a valuable discussion, and modules is a VERY obvious candidate.

Paul Everitt

unread,
Jan 15, 2015, 12:07:30 PM1/15/15
to ang...@googlegroups.com, angul...@googlegroups.com


On Thursday, January 15, 2015 at 10:51:13 AM UTC-5, Marcus Nielsen wrote:
That's how I use it right now. 
Angular becomes the boilerplate code and my controller/factories become normal functions with DI that are exported via CommonJS.

That's the point I have arrived at also (thanks to inspiration from Ben and others.) I basically do NodeJS development, no Angular. The Angular parts are registered in the index.js for each subcomponents.

But testing that index.js is a jump back into shims, bundles, Karma as a DOM, etc. I *could* avoid that with Ben's forked angular-node (and forked mocks) but then my app is tied to his releases.

It feels like Ben is close to a small step that will radically change the equation for people developing large, componentized Angular applications.

Andrew Schmadel

unread,
Jan 16, 2015, 10:08:40 AM1/16/15
to ang...@googlegroups.com, angul...@googlegroups.com
Agreed.  I should have made my previous comment more clear: Ben's changes are a huge step in the right direction.

Is there a strong argument against using UMD?  The drawbacks of UMD feel a lot smaller than the drawbacks of publishing two separate versions of Angular.  

--Andrew

Ben Clinkinbeard

unread,
Jan 16, 2015, 10:32:15 AM1/16/15
to ang...@googlegroups.com, angul...@googlegroups.com
UMD is a Lie from one of the UMD authors. :)

Andrew Schmadel

unread,
Jan 16, 2015, 10:40:43 AM1/16/15
to ang...@googlegroups.com, angul...@googlegroups.com
That's a valid criticism of UMD as a module format.  However, in this instance we're simply looking to write some code that (safely) exports Angular as a CJS module or Browser global.

On Fri, Jan 16, 2015 at 10:32 AM, Ben Clinkinbeard <ben.clin...@gmail.com> wrote:
UMD is a Lie from one of the UMD authors. :)

--
You received this message because you are subscribed to a topic in the Google Groups "AngularJS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/angular/i5by29Okh48/unsubscribe.
To unsubscribe from this group and all its topics, send an email to angular+u...@googlegroups.com.
To post to this group, send email to ang...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.

For more options, visit https://groups.google.com/d/optout.



--
-- 
Andrew Schmadel
Learning Objects, Inc.
1528 Connecticut Avenue NW
Washington, DC 20036

Ben Clinkinbeard

unread,
Jan 16, 2015, 10:47:32 AM1/16/15
to ang...@googlegroups.com
Maybe I'm not understanding what you're asking then? Angular already exposes a browser global, and we're not trying to change that. Making the whole of Angular UMD-aware, where it only exposes the global if CommonJS is not supported, is far beyond the scope of this proposal.

Pete Bacon Darwin

unread,
Jan 19, 2015, 5:34:57 AM1/19/15
to ang...@googlegroups.com
What is the benefit of this being in the build/release process?
Can we not simply manually update the package.json and index.js in each of the "bower-angular*" repositories once to what they need to be and then leave them alone?
The only thing that is a potential concern is if we create a new module that needs a bower repository - that it will also need to have its package.json and index.js tweaked - but I think that we should have a process for creating new modules any way, no?

Pete Bacon Darwin

unread,
Jan 19, 2015, 6:52:35 AM1/19/15
to Caitlin Potter, angul...@googlegroups.com, ang...@googlegroups.com
From looking at the scripts, they assume that there is already a GH repo setup for the new module. So there are, at least, the additional steps of creating the GH repo and setting its security settings appropriately.

Also, I don't see where the original bower.json file is created in the release scripts; so I suspect that this might also be a manual step that needs to be done when adding a new module. Did I miss this in the scripts somewhere?

If all the files really were generated correctly from scratch every time then we could prevent the problem we had with the last releases from occurring again by deleting the contents of the working folder after cloning before generating/copying in the appropriate files.

This doesn't seem to be the case, so either we should do that or, instead, automate (or semi-automate and document) the process of adding a new module to the AngularJS project, so that it includes providing the appropriate index.js and bower.json files.

My preference is to update the publication script to generate/copy all the correct files for a build on every run and make sure that we clean the folder before doing this.

An aside: It is annoying to look at the diffs of the bower repositories when we alternate between releases of different branches since so much changes in each one. This may (I am not 100% up on the internals of git) also add unnecessarily to the size of the bower git repositories.  In any case, how about we change the bower publishing process so that in addition to tagging these commits with the version number, we also ensure that they are added to an appropriate branch rather than master? What do you think?

On 19 January 2015 at 11:25, Caitlin Potter <caitpo...@gmail.com> wrote:
All we need to do is add it to the publish scripts, it’s trivial

On Jan 19, 2015, at 6:16 AM, Pete Bacon Darwin <pe...@bacondarwin.com> wrote:

What is the process right now for creating a new module in the angular.js repository?

On 19 January 2015 at 10:49, Caitlin Potter <caitpo...@gmail.com> wrote:
That’s exactly the reason why it should be in the release process, it’s much easier if it’s all dealt with in one place



--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angular+u...@googlegroups.com.

Francisco Guijarro

unread,
Jan 20, 2015, 4:10:25 PM1/20/15
to ang...@googlegroups.com, angul...@googlegroups.com
I came here from this GitHub issue: https://github.com/angular/angular.js/issues/10805#event-221306745

I believe that the proposed solutions are just superfluous patches to what the real problem is:
- Angular already comes with a module system, but it is not standard compliant, and it is just a sub set of the other module systems out there (Es6, Common.js, AMD)

I would like to get rid of Angular's module system in favor of more feature full Module systems.
I believe that Angular 2.0 brings new ideas to the table, you can check this Video by Vojta Jina: https://www.youtube.com/watch?v=_OGGsf1ZXMs

What are the chances to implement this into Angular 1.x? I believe that is not but totally feasible.

Here is the proposed syntax for Angular 2.0 Dependency Injection with Es6 modules (based on the video by Votja):

Import {Grinder} from './grinder';

// Manually annotate the function
coffeeMaker.annotations = [
   new Inject(Grinder)
];

function coffeeMaker(grinder) {
...
}

export coffeeMaker;


This of course transpiles into Common.js and AMD.
And this is how I imagine using it with Angular 1.x today (and Common.js)


var Grinder = require('./grinder');

// Manually annotate the function
coffeeMaker.$inject = [Grinder];
// The below is necessary in my mind to make it easier to hook to the standard component definition
coffeemaker.$type = 'factory';

function coffeeMaker(grinder) {
...
}

module.exports.coffeeMaker = coffeeMaker;


And maybe basing on some interface like this we can hook up to Angular's DI.
I will try to make some experiments with this.
What do you guys think?

Thanks!
Fran

Ben Clinkinbeard

unread,
Jan 20, 2015, 4:23:32 PM1/20/15
to ang...@googlegroups.com, angul...@googlegroups.com
What do you guys think?

I think completely overhauling Angular's module system is a massive undertaking far beyond the intended scope of this proposal. I don't understand why the referenced GitHub issue is seen as intersecting with this proposal.

The modules are already being published to npm, which carries with it an overwhelming expectation of CommonJS compatibility. Can't we meet that baseline expectation before embarking on an epic effort to modify something as fundamental as the Angular module system?

Paul Everitt

unread,
Jan 20, 2015, 4:24:57 PM1/20/15
to ang...@googlegroups.com, angul...@googlegroups.com

I think it would be great Angular's future module system (ES6) was in 1.x. However, I don't think this ticket is the place to discuss it. This ticket is a tiny step that has a big win.

Requiring ES6 in Angular 1.x and ripping out the existing module system is a big, invasive amount of work that needs a long architectural discussion. Plus, it would make all Angular 1.x projects do some notable rewriting and adopt a new toolchain (transpiling.) If there's an Angular 1.5, or 1.6, or something after the upcoming 1.4, perhaps it could be discussed there.

My two cents.

--Paul

Caitlin Potter

unread,
Jan 20, 2015, 4:25:48 PM1/20/15
to ang...@googlegroups.com, angul...@googlegroups.com
I suggested commenting here, because I think we can do a bit more to improve interop with CommonJS. That said, yes, completely replacing the module system with something else is not likely to be doable for 1.x.

--

Andrew Schmadel

unread,
Jan 20, 2015, 4:54:34 PM1/20/15
to ang...@googlegroups.com, angul...@googlegroups.com
Yep.   This ticket is strictly about making it possible to load Angular in a CJS environment without jumping through any hoops.

I agree with many of the others in this thread who would like to use a fully-featured module loader with Angular, but that's a separate discussion.  Implementing basic CJS compatibility is a low-risk, and low-effort first step toward that eventual goal.

--
You received this message because you are subscribed to a topic in the Google Groups "AngularJS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/angular/i5by29Okh48/unsubscribe.
To unsubscribe from this group and all its topics, send an email to angular+u...@googlegroups.com.

To post to this group, send email to ang...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/d/optout.

Paul Everitt

unread,
Jan 20, 2015, 5:00:04 PM1/20/15
to ang...@googlegroups.com, angul...@googlegroups.com
Just out of curiosity, what's the process on this discussion period? Is there an end date for when input is finished? I'd love for this to get into the 1.4 release cycle, or even 1.3.x if there's going to be another release.

Are there points from Pete's two notes that need to be digested?

Caitlin Potter

unread,
Jan 20, 2015, 5:07:42 PM1/20/15
to ang...@googlegroups.com, angul...@googlegroups.com
We’re sort of making it up as we go — but the main goal is to have multiple core team members say “sounds good, lets do that”. There’s no real “time limit”, but things would move faster if we got there within a week.

There are a few things blocking this:

For me, I want to figure out solid conventions that will satisfy people, so that we aren’t shipping anything unexpected or surprising. It should fit nicely into browserify users workflow.

Igor sounded like he wasn’t keen on making it the “default” npm release, since it technically changes things for people who were happily using what was shipped on npm already.

There might have been other issues, I’m going from memory here =)

Most of the discussion does seem to be circling around things which are a bit out of scope for this topic, but it was bound to happen. It would definitely be helpful to focus on what will best serve angular developers within the scope of CJS support on npm, though.

On Jan 20, 2015, at 5:00 PM, Paul Everitt <paulwe...@gmail.com> wrote:

Just out of curiosity, what's the process on this discussion period? Is there an end date for when input is finished? I'd love for this to get into the 1.4 release cycle, or even 1.3.x if there's going to be another release.

Are there points from Pete's two notes that need to be digested?

Andrew Schmadel

unread,
Jan 20, 2015, 5:18:55 PM1/20/15
to ang...@googlegroups.com, angul...@googlegroups.com

Igor seemed interested in the prospect of preserving the existence of a single “distribution” of Angular (hence my previous comments about UMD). IMO, even in spite of UMD’s drawbacks, this allows us to ship a single Angular release immediately, with no surprises for existing users.

Any breaking changes (such as publishing a CJS-only version of Angular on NPM) would need to go in 1.4.

At the end of the day, we want developers to be able to npm install angular, write require('angular'), and have it work without any surprises.

~Andrew

Caitlin Potter

unread,
Jan 20, 2015, 5:23:39 PM1/20/15
to ang...@googlegroups.com, angul...@googlegroups.com
the proposal doesn't explain if this is going to be a separate package on npm from the main package, if so what's the name of the npm package?
>
> I'm in favor of a commonjs build *in addition* rather than instead of the current build. Ideally it should be a separate package, or same package, but published under a > version number with a qualifier.

When I read this, I’m seeing “I don’t want this to replace the current npm release, it should be a different package or tag”.

Ben Clinkinbeard

unread,
Jan 20, 2015, 6:17:41 PM1/20/15
to ang...@googlegroups.com, angul...@googlegroups.com
There is no reason to add CommonJS support in a separate package or tag, because the proposed changes don't break any existing workflows. These changes are purely additive.

Currently, require('angular'), require('angular-route'), etc. don't return anything, and only produce side effects. Adding CommonJS support in the manner we are (removing the main field from package.json and relying on the resolution algorithm to default to index.js) just makes it so require() does return something, in addition to the side effects currently in place.

Again, the proposed changes do not break any existing workflows.

Adam DiCarlo

unread,
Jan 20, 2015, 7:03:26 PM1/20/15
to ang...@googlegroups.com, angul...@googlegroups.com
CJS support?! Sweet! I'd prefer returning the module itself; it at least doesn't feel as kludgey as returning the name string.

In the large app I work on (~170 Angular JS files), we're using RequireJS and returning module names, and it's always rubbed me the wrong way.

FWIW, in our app:
* We don't reopen a single Angular module.
* Each file is its own Angular module
* Files usually only define a single Angular service/directive/etc. (with, of course, exceptions that are mostly from early development).

Adam

Daniel Lidström

unread,
Jan 21, 2015, 7:52:11 AM1/21/15
to ang...@googlegroups.com, angul...@googlegroups.com
Hi Adam

May I ask what problems you are solving with RequireJS and putting each file in its own Angular module? Even 170 source files could be bundled, minimized, compressed, and loaded up front, don't you think? And why so many modules? I'm just being curious.

Adam DiCarlo

unread,
Jan 21, 2015, 3:17:46 PM1/21/15
to ang...@googlegroups.com, angul...@googlegroups.com
May I ask what problems you are solving with RequireJS and putting each file in its own Angular module? Even 170 source files could be bundled, minimized, compressed, and loaded up front, don't you think? And why so many modules? I'm just being curious.

Sure! We *are* bundling and minifying. We have 4 entry points (and 4 entry point JS bundles), a "frameworks" bundle, and a "heavy feature we need to lazy-load" bundle.

We see Angular modules as boilerplate/overhead. We had started with "everything attaches itself to the 'MainApp' module" pattern and that worked until we needed to make another separate app (Admin section) where we needed to reuse some pieces from the main app. So having everything attach itself to the 'MainApp' module just didn't work for us anymore because it would require the 'Admin' module to depend on 'MainApp' and thus needlessly load *all* MainApp's dependencies.

So we started having each JS module define its own Angular module, returning the module name, and using the 'angular.module(module.id, [require('controllers/SomeController'), ...])' pattern.

Adam

Paul Everitt

unread,
Jan 21, 2015, 5:15:34 PM1/21/15
to ang...@googlegroups.com, angul...@googlegroups.com


On Wednesday, January 21, 2015 at 7:52:11 AM UTC-5, Daniel Lidström wrote:
Hi Adam

May I ask what problems you are solving with RequireJS and putting each file in its own Angular module? Even 170 source files could be bundled, minimized, compressed, and loaded up front, don't you think? And why so many modules? I'm just being curious.

I will give my two cents on this. Some the same as Adam's response.

Ben Clinkinbeard (author of this PR) has been promoting a style of Angular development which I have a adopted to great effect in my larger projects. Like other style guides (John Papa, Todd Motta) I have subdirectories for each component (e.g. src/auth) rather than each Angular concept (e.g. src/controllers). Each of these component is its own Angular module. But each are also standalone CommonJS/NodeJS modules with an index.js to setup the component. Each file in the component has a require() for its dependencies. browserify can then walk the tree and bundle everything up.

The fun part, though, is that Angular only appears in the component's index.js file. My services, controllers, etc. don't do anything like angular.module('my component').service('MyService', MyService). They are pure NodeJS files. I can then test them with Mocha, use require() for dependencies. Basically, future-proof them and gain a tremendous productivity speedup. By breaking things up like this, and *especially* by not hauling in Angular, mocking the $injector and module() in my unit tests, and running through Karma, I feel like I am not squashed by the magic.

There are some unit tests that still need Angular, though. Like directives, or the wiring in index.js, or my ui-router states. Ben's angular-node npm package tries to address this by using jsdom when you are in a NodeJS context.

Daniel Lidström

unread,
Jan 22, 2015, 8:46:57 AM1/22/15
to ang...@googlegroups.com, angul...@googlegroups.com
Your arguments are making perfect sense to me. Paul and Adam, many thanks for sharing your thoughts!

Caitlin Potter

unread,
Feb 13, 2015, 4:54:07 PM2/13/15
to ang...@googlegroups.com, angul...@googlegroups.com
Pinging this thread, because I don't think any conclusions have been made yet.

I don't personally care a whole lot which the conventions end up being, it's fine with me to do whatever. But, some other core team folks do need to OK the approach so that this can be done, or decide that it's not worth doing at all.

The iffy pieces:

- Default npm packages vs alternative package or tagged release
- Export whole module object vs export module name vs some other alternative (it's inconsistent no matter what if require('angular') returns the angular module).
- What to do about files which export multiple modules

Some feedback to close this thread would be great.

Francisco Guijarro

unread,
Feb 13, 2015, 5:01:15 PM2/13/15
to ang...@googlegroups.com, angul...@googlegroups.com
I believe that is a common good practice to use a single angular module per file, but we could use CJS exports object to do that, so we have full flexibility.
```
angular.module('example', [
  require('myFile').myMod1.name
  require('myFile').myMod2.name
]);
```
I think it's pretty accepted to export a whole Angular module and then use it's name property.

What Im saying is not rocket science but comes from experience and from what people said in this thread.
Also, I will prefer that Angular itself becomes an npm package and embraces the Node.js philosophy as a full, and it will be easier maintain than a whole separate repo with only exported versions.

It will be nice see Angular to adopt Common.js internally too.

Fran

--
You received this message because you are subscribed to a topic in the Google Groups "AngularJS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/angular/i5by29Okh48/unsubscribe.
To unsubscribe from this group and all its topics, send an email to angular+u...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages