New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace System.register bundle with a CJS bundle #4278

Closed
vsavkin opened this Issue Sep 21, 2015 · 34 comments

Comments

@vsavkin
Contributor

vsavkin commented Sep 21, 2015

Most developer workflows are based around CJS (Browserify or WebPack) and not System.js. Since System.js can consume cjs bundles, I suggest we replace our System.register bundle with a CJS bundle. In this case, developers that use Browserify or WebPack will be able to use it, and it should still work with System.

Notes:

  • System.register has a lot of overhead comparing to CJS. My naive measurements showed the difference is about 18%.
  • When using a CJS bundle, you cannot important submodudles, only "angular2/angular2". I think this is an advantage cause it prevents users from depending on private API.

Finally, once we produce a CJS bundle, I think we should change our Getting Started guide to use WebPack instead of System.js. The reason is WebPack provides a lot better experience out of the box (including TypeScript and Babel).

The WebPack config for an Angular 2 project written with TypeScript is about 10 lines long:

module.exports = {
  resolve: {
    root: __dirname,
    extensions: ['.ts', '.js']
  },
  entry: ["webpackapp"],
  output: {filename: "webappapp_bundle.js"},
  externals: {'angular2/angular2': "angular2"},
  devtool: 'source-map',
  module: {
    loaders: [
      { test: /\.ts$/, loader: 'ts-loader' }
    ]
  }
}

You get source maps, dev server with live reload, production/dev modes -- a very good first impression and a clear path to production setting.

/cc @IgorMinar @mhevery @rkirov @jeffbcross

@johnpapa

This comment has been minimized.

Show comment
Hide comment
@johnpapa

johnpapa Sep 21, 2015

Contributor

Great topic! Thanks for raising this.

You say "Most developer workflows are based around CJS (Browserify or WebPack) and not System.js. " ... I am not sure how that is being measured. I find that most developers I interact with have very little experience with any of these, thus making any guess on who the leader is a pure guess.

I believe there should be a few goals for the module loader:

  1. Loads modules
  2. Supports smart bundling (bundles that load as a user progresses through the app)
  3. Performs well
  4. Ease of use / configuration

Evaluating these should include a side by side example of the options showing the pros and cons of each, along with the ease of use.

We need a proven guided path that works with readable and understandable configuration. Why? Handing someone a sample config with 100+ lines in it causes eyes to glaze over, as I've seen. The other side is a black box "let me do it all for you" technique. This is also problematic as when things go wrong the user has little insight, which makes it difficult to diagnose and fix. So having a consistent choice and guide in the docs will be super helpful.

WebPack ... nice all in one solution, but it has a lot of moving parts. Is this too much at once for those already learning a high concept count new wave of JavaScript + Angular2? Performance seems OK. Some folks like this, others find it a magical black box.

SystemJS ... less moving parts than WebPack, but configuration can get a little confusing. Add JSPM for bundling and its great, but it is also now 100+ lines of config. Performance concerns over size the loader.

CJS/Browserify ... though many have never used it, it seems the "oldest" of the group. Is it still up to snuff for everything we need it to do?

I do not see a clear leader here, but I do believe it should stick to the 4 goals above as a guiding force.

Finally ... the code should work with all of these. It may seem obvious, but I don't want to make assumptions.

Contributor

johnpapa commented Sep 21, 2015

Great topic! Thanks for raising this.

You say "Most developer workflows are based around CJS (Browserify or WebPack) and not System.js. " ... I am not sure how that is being measured. I find that most developers I interact with have very little experience with any of these, thus making any guess on who the leader is a pure guess.

I believe there should be a few goals for the module loader:

  1. Loads modules
  2. Supports smart bundling (bundles that load as a user progresses through the app)
  3. Performs well
  4. Ease of use / configuration

Evaluating these should include a side by side example of the options showing the pros and cons of each, along with the ease of use.

We need a proven guided path that works with readable and understandable configuration. Why? Handing someone a sample config with 100+ lines in it causes eyes to glaze over, as I've seen. The other side is a black box "let me do it all for you" technique. This is also problematic as when things go wrong the user has little insight, which makes it difficult to diagnose and fix. So having a consistent choice and guide in the docs will be super helpful.

WebPack ... nice all in one solution, but it has a lot of moving parts. Is this too much at once for those already learning a high concept count new wave of JavaScript + Angular2? Performance seems OK. Some folks like this, others find it a magical black box.

SystemJS ... less moving parts than WebPack, but configuration can get a little confusing. Add JSPM for bundling and its great, but it is also now 100+ lines of config. Performance concerns over size the loader.

CJS/Browserify ... though many have never used it, it seems the "oldest" of the group. Is it still up to snuff for everything we need it to do?

I do not see a clear leader here, but I do believe it should stick to the 4 goals above as a guiding force.

Finally ... the code should work with all of these. It may seem obvious, but I don't want to make assumptions.

@pkozlowski-opensource

This comment has been minimized.

Show comment
Hide comment
@pkozlowski-opensource

pkozlowski-opensource Sep 21, 2015

Member

@vsavkin I will be talking about modules / module loading and tools during AngularConnect so this topic is close to my hart and I was planning to discuss more about it with @rkirov. For now I've started to gathering my thoughts in https://docs.google.com/document/d/13crBXngwx9f7pcn0DeniZn12XqnbJuyiuh5dbT5nz64/edit?usp=sharing (comments welcomed!)

Now, regarding the issue at hand: while I agree with all the mentioned pbs of SystemJS (size + re-export perf) I would love to properly evaluate alternatives before changing. What are the possibilities and trade-offs?

I must say that personally I don't have warm thoughts about CJS (why to put people on the format / tooling / workflows that goes away?) nor WebPack (actually I hear many negative voices claiming that it is complex to use / has large config files for things that are more than "hello world" examples). Actually I was staying away from WebPack so far since just from looking at its documentation it seems like there is gazzilion of options). But I also hear that many people use / like it.

Anyway, I was about to schedule a meeting with @rkirov to discuss those topics in the context of my AngularConnect talk, so it would be great if you guys could keep me in the loop of those discussions.

Member

pkozlowski-opensource commented Sep 21, 2015

@vsavkin I will be talking about modules / module loading and tools during AngularConnect so this topic is close to my hart and I was planning to discuss more about it with @rkirov. For now I've started to gathering my thoughts in https://docs.google.com/document/d/13crBXngwx9f7pcn0DeniZn12XqnbJuyiuh5dbT5nz64/edit?usp=sharing (comments welcomed!)

Now, regarding the issue at hand: while I agree with all the mentioned pbs of SystemJS (size + re-export perf) I would love to properly evaluate alternatives before changing. What are the possibilities and trade-offs?

I must say that personally I don't have warm thoughts about CJS (why to put people on the format / tooling / workflows that goes away?) nor WebPack (actually I hear many negative voices claiming that it is complex to use / has large config files for things that are more than "hello world" examples). Actually I was staying away from WebPack so far since just from looking at its documentation it seems like there is gazzilion of options). But I also hear that many people use / like it.

Anyway, I was about to schedule a meeting with @rkirov to discuss those topics in the context of my AngularConnect talk, so it would be great if you guys could keep me in the loop of those discussions.

@rkirov

This comment has been minimized.

Show comment
Hide comment
@rkirov

rkirov Sep 21, 2015

Contributor

There are two separate proposals here:

  • replacing the bundling tool we use in this repo.
  • changing getting-started on angular.io, which naturally extends into picking a recommended module loader for angular2.

I think we can move forward on the first one purely based on code-size, since a CJS bundle is consumable by every tool, including System.js. I would like to see browserify as a comparison against webpack here and then go forward with one of the two. System-js-builder has bitten us a few times due to the module sniffing with regexps (see https://github.com/angular/angular/blob/master/gulpfile.js#L1026), so I would like to try something else. Also, by definition bundles are opaque objects so switching to different tool to produce it has little cost.

The second proposal is a lot more contentious and needs a lot more research. I think that if we are going to support angular CLI, our getting started guide should be using that. The question then becomes what does the CLI scaffold use?

Contributor

rkirov commented Sep 21, 2015

There are two separate proposals here:

  • replacing the bundling tool we use in this repo.
  • changing getting-started on angular.io, which naturally extends into picking a recommended module loader for angular2.

I think we can move forward on the first one purely based on code-size, since a CJS bundle is consumable by every tool, including System.js. I would like to see browserify as a comparison against webpack here and then go forward with one of the two. System-js-builder has bitten us a few times due to the module sniffing with regexps (see https://github.com/angular/angular/blob/master/gulpfile.js#L1026), so I would like to try something else. Also, by definition bundles are opaque objects so switching to different tool to produce it has little cost.

The second proposal is a lot more contentious and needs a lot more research. I think that if we are going to support angular CLI, our getting started guide should be using that. The question then becomes what does the CLI scaffold use?

@pkozlowski-opensource

This comment has been minimized.

Show comment
Hide comment
@pkozlowski-opensource

pkozlowski-opensource Sep 21, 2015

Member

Totally agree with @rkirov that we should separate discussion about the format / tools used to produce bundles on our side vs. what we recommend to ng2 users. And I would be really careful about recommending WebPack at this point, this should be consisted with CLI efforts. Still few remarks:

I think we can move forward on the first one purely based on code-size

Is this the only trade-off? Do we've got the same semantic of ES6 modules?
So I would really love to see a short comparison of the 2 formats (I can dig into few items if we decide that it is worthwhile)

System-js-builder has bitten us a few times due to the module sniffing with regexps

True, although we are using quite an old version and we've never took time to update.
Having said this I'm all for trying sth else provided that:

  • it can be loaded / consumed by the SystemJS loader
  • it is not an automatic recommendation for our users
Member

pkozlowski-opensource commented Sep 21, 2015

Totally agree with @rkirov that we should separate discussion about the format / tools used to produce bundles on our side vs. what we recommend to ng2 users. And I would be really careful about recommending WebPack at this point, this should be consisted with CLI efforts. Still few remarks:

I think we can move forward on the first one purely based on code-size

Is this the only trade-off? Do we've got the same semantic of ES6 modules?
So I would really love to see a short comparison of the 2 formats (I can dig into few items if we decide that it is worthwhile)

System-js-builder has bitten us a few times due to the module sniffing with regexps

True, although we are using quite an old version and we've never took time to update.
Having said this I'm all for trying sth else provided that:

  • it can be loaded / consumed by the SystemJS loader
  • it is not an automatic recommendation for our users
@vsavkin

This comment has been minimized.

Show comment
Hide comment
@vsavkin

vsavkin Sep 21, 2015

Contributor

@johnpapa

You say "Most developer workflows are based around CJS (Browserify or WebPack) and not System.js. " ... I am not sure how that is being measured. I find that most developers I interact with have very little experience with any of these.

You are probably right in saying that most developers do not user bundlers at all. But early adopters and innovators, who are going to play with Angular 2 first, probably use bundlers. Just one survey http://ashleynolan.co.uk/blog/frontend-tooling-survey-2015-results (WebPack + Browserify = 30%, JSPM = 2.5%)

I think there is no significant downside in switching to a CJS bundle. The SystemJS workflow still works, but we also enable other workflows. Why would not we try to enable these workflows?

Evaluating these should include a side by side example of the options showing the pros and cons of each, along with the ease of use.

Agree. We need to do a more careful evaluation. We should consider:

  • Easy of use
  • The maturity of the community
  • The file size of the bundle
  • Number of enabled workflows (flexibility)
  • etc
Contributor

vsavkin commented Sep 21, 2015

@johnpapa

You say "Most developer workflows are based around CJS (Browserify or WebPack) and not System.js. " ... I am not sure how that is being measured. I find that most developers I interact with have very little experience with any of these.

You are probably right in saying that most developers do not user bundlers at all. But early adopters and innovators, who are going to play with Angular 2 first, probably use bundlers. Just one survey http://ashleynolan.co.uk/blog/frontend-tooling-survey-2015-results (WebPack + Browserify = 30%, JSPM = 2.5%)

I think there is no significant downside in switching to a CJS bundle. The SystemJS workflow still works, but we also enable other workflows. Why would not we try to enable these workflows?

Evaluating these should include a side by side example of the options showing the pros and cons of each, along with the ease of use.

Agree. We need to do a more careful evaluation. We should consider:

  • Easy of use
  • The maturity of the community
  • The file size of the bundle
  • Number of enabled workflows (flexibility)
  • etc
@jeffbcross

This comment has been minimized.

Show comment
Hide comment
@jeffbcross

jeffbcross Sep 21, 2015

Contributor

Is this the only trade-off? Do we've got the same semantic of ES6 modules?

@pkozlowski-opensource for Angular itself, what ES6 semantics do we need in our output? The internal imports/exports are already designed to not require cyclic dependency resolution, since we are already supporting CJS.

Contributor

jeffbcross commented Sep 21, 2015

Is this the only trade-off? Do we've got the same semantic of ES6 modules?

@pkozlowski-opensource for Angular itself, what ES6 semantics do we need in our output? The internal imports/exports are already designed to not require cyclic dependency resolution, since we are already supporting CJS.

@robwormald

This comment has been minimized.

Show comment
Hide comment
@robwormald

robwormald Sep 22, 2015

Member

My 2c - We should be moving away from bundling as a internal thing anyway. ES5 users will of course need a pre-bundled ES5 build (and I don't have much of an opinion there, since whatever mechanic we use to build it is irrelevant to the end user - they just use the ng global). Modern users (who are quite used to using browserify/webpack/etc) likely don't need the handholding, and us building massive bundles to be consumed by them complicates things.

Outputs should be:

  • ES5 global bundle(s) - built whatever makes it fastest
  • CJS source files - not bundled
  • ES6 source files - not bundled.

We can make recommendations and tutorials and the like, but I don't think bundling for "modern" environments is in scope. As long as relative paths work, users can use whatever they please.

Member

robwormald commented Sep 22, 2015

My 2c - We should be moving away from bundling as a internal thing anyway. ES5 users will of course need a pre-bundled ES5 build (and I don't have much of an opinion there, since whatever mechanic we use to build it is irrelevant to the end user - they just use the ng global). Modern users (who are quite used to using browserify/webpack/etc) likely don't need the handholding, and us building massive bundles to be consumed by them complicates things.

Outputs should be:

  • ES5 global bundle(s) - built whatever makes it fastest
  • CJS source files - not bundled
  • ES6 source files - not bundled.

We can make recommendations and tutorials and the like, but I don't think bundling for "modern" environments is in scope. As long as relative paths work, users can use whatever they please.

@robwormald

This comment has been minimized.

Show comment
Hide comment
@robwormald

robwormald Sep 22, 2015

Member

Further - as we move closer to beta, if we continue to distribute a big System.register bundle (which @guybedford has more or less explicitly stated as a bad idea) users will begin to expect that to be an "angular responsibility"

It's useful for plunkers (which was the original stated use case for the big bundle) but not something we want to own going forward, and certainly not recommend as we start using docs.

Member

robwormald commented Sep 22, 2015

Further - as we move closer to beta, if we continue to distribute a big System.register bundle (which @guybedford has more or less explicitly stated as a bad idea) users will begin to expect that to be an "angular responsibility"

It's useful for plunkers (which was the original stated use case for the big bundle) but not something we want to own going forward, and certainly not recommend as we start using docs.

@pkozlowski-opensource

This comment has been minimized.

Show comment
Hide comment
@pkozlowski-opensource

pkozlowski-opensource Sep 22, 2015

Member

The internal imports/exports are already designed to not require cyclic dependency resolution, since we are already supporting CJS.

@jeffbcross oh, right. I was under the impression that we still got some cyclic dependencies. But from what you are saying we should be safe here. So the only other item would be importing things from "outside" of angular2/angular2.

The other item I would like to test before moving to CJS is what is the impact on the loader (more files / bigger files to include) and its configuration. Will play with this on my end.

Member

pkozlowski-opensource commented Sep 22, 2015

The internal imports/exports are already designed to not require cyclic dependency resolution, since we are already supporting CJS.

@jeffbcross oh, right. I was under the impression that we still got some cyclic dependencies. But from what you are saying we should be safe here. So the only other item would be importing things from "outside" of angular2/angular2.

The other item I would like to test before moving to CJS is what is the impact on the loader (more files / bigger files to include) and its configuration. Will play with this on my end.

@pkozlowski-opensource

This comment has been minimized.

Show comment
Hide comment
@pkozlowski-opensource

pkozlowski-opensource Sep 22, 2015

Member

My 2c - We should be moving away from bundling as a internal thing anyway.

@robwormald could you elaborate? What is the alternative proposal? People doing the bundling on their own? Or serving un-packaged ng2 with HTTP2 "magic"? Or sth else?

As the framework user I would love to use a bundled framework version even if writing in ES6 / TS. Bundling your own version of ng2 is pain in a neck (downloading, configuring tools etc.). IMO it is too much to expect from an average user that wants to play with the framework.

On top of this there is very little value in bundling things on your own as, AFAIK, we don't have tools that could tree-shake for JS / TS.

Modern users (who are quite used to using browserify/webpack/etc) likely don't need the handholding, and us building massive bundles to be consumed by them complicates things.

Not sure what you mean by "Modern users" here, but you can see from the discussion on gitter / issues opened here that many UI devs starting with ng2 are not equipped with knowledge / time / patience to setup bundling on their end. We definitively shouldn't block "power users" from having their own setup, but IMO we should have a good / easy story foe newcomers.

It's useful for plunkers (which was the original stated use case for the big bundle) but not something we want to own going forward, and certainly not recommend as we start using docs.

Once again, I'm not sure I understand what is the alternative that you are proposing but if it is one of:

  • people bundling ng2 on their own
  • people forced to configure server / use browsers with HTTP2 support

it would be a big concern for me and I would consider it as setting a bar very high for a popular UI framework that could be used by relative beginners.

Member

pkozlowski-opensource commented Sep 22, 2015

My 2c - We should be moving away from bundling as a internal thing anyway.

@robwormald could you elaborate? What is the alternative proposal? People doing the bundling on their own? Or serving un-packaged ng2 with HTTP2 "magic"? Or sth else?

As the framework user I would love to use a bundled framework version even if writing in ES6 / TS. Bundling your own version of ng2 is pain in a neck (downloading, configuring tools etc.). IMO it is too much to expect from an average user that wants to play with the framework.

On top of this there is very little value in bundling things on your own as, AFAIK, we don't have tools that could tree-shake for JS / TS.

Modern users (who are quite used to using browserify/webpack/etc) likely don't need the handholding, and us building massive bundles to be consumed by them complicates things.

Not sure what you mean by "Modern users" here, but you can see from the discussion on gitter / issues opened here that many UI devs starting with ng2 are not equipped with knowledge / time / patience to setup bundling on their end. We definitively shouldn't block "power users" from having their own setup, but IMO we should have a good / easy story foe newcomers.

It's useful for plunkers (which was the original stated use case for the big bundle) but not something we want to own going forward, and certainly not recommend as we start using docs.

Once again, I'm not sure I understand what is the alternative that you are proposing but if it is one of:

  • people bundling ng2 on their own
  • people forced to configure server / use browsers with HTTP2 support

it would be a big concern for me and I would consider it as setting a bar very high for a popular UI framework that could be used by relative beginners.

@rkirov rkirov referenced this issue Sep 22, 2015

Closed

add common js bundles output #4315

1 of 3 tasks complete
@pkozlowski-opensource

This comment has been minimized.

Show comment
Hide comment
@pkozlowski-opensource

pkozlowski-opensource Sep 23, 2015

Member

@vsavkin @rkirov so I've played a bit with bundling angular2 using CJS instead of System.register. Using webpack to generate a minified bundle I can also see ~20% size difference in favor of CJS.

The funny thing is that most of the overhead seems to be coming from ... paths usage in the System.register format. Indeed we have this: System.register("path", ["dep1path", "dep2path"] and our paths go like "angular2/src/core/facade/lang" or even longer... This all adds a lot of strings to the bundle - strings that can't be minified / renamed. WebPack is using numerical module ids which are pretty useless for importing individual modules, but make the output much smaller....

So for now it seems like the main trade-off would be, as Victor pointed out:

When using a CJS bundle, you cannot important submodudles, only "angular2/angular2". I think this is an advantage cause it prevents users from depending on private API.

I'm going to play with the CJS bundle and SystemJS loader a bit more to see if it doesn't generate other issues but yeh, from the initial investigation it looks like there are fundamental differences between the 2 formats that push us into a size vs. sub-module imports trade-off.

Member

pkozlowski-opensource commented Sep 23, 2015

@vsavkin @rkirov so I've played a bit with bundling angular2 using CJS instead of System.register. Using webpack to generate a minified bundle I can also see ~20% size difference in favor of CJS.

The funny thing is that most of the overhead seems to be coming from ... paths usage in the System.register format. Indeed we have this: System.register("path", ["dep1path", "dep2path"] and our paths go like "angular2/src/core/facade/lang" or even longer... This all adds a lot of strings to the bundle - strings that can't be minified / renamed. WebPack is using numerical module ids which are pretty useless for importing individual modules, but make the output much smaller....

So for now it seems like the main trade-off would be, as Victor pointed out:

When using a CJS bundle, you cannot important submodudles, only "angular2/angular2". I think this is an advantage cause it prevents users from depending on private API.

I'm going to play with the CJS bundle and SystemJS loader a bit more to see if it doesn't generate other issues but yeh, from the initial investigation it looks like there are fundamental differences between the 2 formats that push us into a size vs. sub-module imports trade-off.

@mhevery

This comment has been minimized.

Show comment
Hide comment
@mhevery

mhevery Sep 23, 2015

Member

#4278 (comment) goes to show that we need a bundler which removes all internal traces of import structure, and just gives us a single bundle. Seems like savings are 20%+ and I think it would be even higher if we would remove all of the iifs which are generated from the files.

Member

mhevery commented Sep 23, 2015

#4278 (comment) goes to show that we need a bundler which removes all internal traces of import structure, and just gives us a single bundle. Seems like savings are 20%+ and I think it would be even higher if we would remove all of the iifs which are generated from the files.

@guybedford

This comment has been minimized.

Show comment
Hide comment
@guybedford

guybedford Sep 23, 2015

Have you looked into https://github.com/rollup/rollup? It does these sorts of static optimizations - we will be integrating it into SystemJS builder by default for static bundles with optimization soon (systemjs/builder#205). Certainly optimized builds are preferable to bundling - bundling is only necessary when specifically wanting to populate a loader registry with that level of granularity for module re-use between different packages or with dynamic loading.

guybedford commented Sep 23, 2015

Have you looked into https://github.com/rollup/rollup? It does these sorts of static optimizations - we will be integrating it into SystemJS builder by default for static bundles with optimization soon (systemjs/builder#205). Certainly optimized builds are preferable to bundling - bundling is only necessary when specifically wanting to populate a loader registry with that level of granularity for module re-use between different packages or with dynamic loading.

@pkozlowski-opensource

This comment has been minimized.

Show comment
Hide comment
@pkozlowski-opensource

pkozlowski-opensource Sep 23, 2015

Member

@guybedford I had a brief look at https://github.com/rollup/rollup few weeks back it looked like it at the very early stage back then... Would be keen on having another look at it.

@guybedford would you mind if I reach out to you when I've got more findings regarding System.register vs. CJS? Personally I love all the goodies of System.register but we hit some issues with it, as you can see from this discussion:

  • size overhead
  • re-export perf impact (probably fixed in the latest builder / format update)
  • we are also hitting some issues with the TS impl as it the latest one to introduce the format

Still, I love the flexibility and correctness / completeness of the System.register format so would be keen on sharing more detailed feedback if you are interested.

Member

pkozlowski-opensource commented Sep 23, 2015

@guybedford I had a brief look at https://github.com/rollup/rollup few weeks back it looked like it at the very early stage back then... Would be keen on having another look at it.

@guybedford would you mind if I reach out to you when I've got more findings regarding System.register vs. CJS? Personally I love all the goodies of System.register but we hit some issues with it, as you can see from this discussion:

  • size overhead
  • re-export perf impact (probably fixed in the latest builder / format update)
  • we are also hitting some issues with the TS impl as it the latest one to introduce the format

Still, I love the flexibility and correctness / completeness of the System.register format so would be keen on sharing more detailed feedback if you are interested.

@guybedford

This comment has been minimized.

Show comment
Hide comment
@guybedford

guybedford Sep 23, 2015

@pkozlowski-opensource sure I'm more than happy to discuss further. Yes exactly as mentioned to @robwormald System.register is not a distribution format, rather a static optimized module (which is what SystemJS SFX builds are becoming as we integrate optimizations like rollup) or separate raw ES modules are best to provide to users for further use in their own environments.

guybedford commented Sep 23, 2015

@pkozlowski-opensource sure I'm more than happy to discuss further. Yes exactly as mentioned to @robwormald System.register is not a distribution format, rather a static optimized module (which is what SystemJS SFX builds are becoming as we integrate optimizations like rollup) or separate raw ES modules are best to provide to users for further use in their own environments.

@todoubaba

This comment has been minimized.

Show comment
Hide comment
@todoubaba

todoubaba Sep 23, 2015

Contributor

Inline typescript helper functions such as __decorate, __metadata, __param take up 6.4% in angular2.min.js.

Contributor

todoubaba commented Sep 23, 2015

Inline typescript helper functions such as __decorate, __metadata, __param take up 6.4% in angular2.min.js.

@pkozlowski-opensource

This comment has been minimized.

Show comment
Hide comment
@pkozlowski-opensource
Member

pkozlowski-opensource commented Sep 24, 2015

@todoubaba yes, you are right. Discussion in Microsoft/TypeScript#3364 (comment)

@weswigham

This comment has been minimized.

Show comment
Hide comment
@weswigham

weswigham Sep 24, 2015

You may be interested in this work. I've actually been testing it against angular. Angular's input on bundling in the TS compiler would always be appreciated.

weswigham commented Sep 24, 2015

You may be interested in this work. I've actually been testing it against angular. Angular's input on bundling in the TS compiler would always be appreciated.

@pkozlowski-opensource

This comment has been minimized.

Show comment
Hide comment
@pkozlowski-opensource

pkozlowski-opensource Oct 3, 2015

Member

OK, I did some more experiments with the CJS bundling and here is the summary of my findings:

  • CJS output is ~20% smaller as compared to System.register
  • The main difference in size between CJS and System.register comes from the fact that with CJS we don't expose internal structure of the bundle - that is - one can't import from any "file" - only from public API exported on the top-level bundle
  • As soon as we drop System.register and don't allow importing from "private" paths we start to have a pb with non-core modules (http, router) which are importing "private" modules. I'm not sure if existing bundlers can handle this situation - to be checked, see discussion in #4495
  • As of today http bundle doesn't use bundle arithmetic and does include entire angular2 - I believe that this is a bug
Member

pkozlowski-opensource commented Oct 3, 2015

OK, I did some more experiments with the CJS bundling and here is the summary of my findings:

  • CJS output is ~20% smaller as compared to System.register
  • The main difference in size between CJS and System.register comes from the fact that with CJS we don't expose internal structure of the bundle - that is - one can't import from any "file" - only from public API exported on the top-level bundle
  • As soon as we drop System.register and don't allow importing from "private" paths we start to have a pb with non-core modules (http, router) which are importing "private" modules. I'm not sure if existing bundlers can handle this situation - to be checked, see discussion in #4495
  • As of today http bundle doesn't use bundle arithmetic and does include entire angular2 - I believe that this is a bug
@johnpapa

This comment has been minimized.

Show comment
Hide comment
@johnpapa

johnpapa Oct 3, 2015

Contributor

We can compile TS to CJS or SystemJS. Then we can use SystemJS to load either format. That way you can use whichever bundle is better

Have you tested both of these to see which pros and cons?

Contributor

johnpapa commented Oct 3, 2015

We can compile TS to CJS or SystemJS. Then we can use SystemJS to load either format. That way you can use whichever bundle is better

Have you tested both of these to see which pros and cons?

@pkozlowski-opensource

This comment has been minimized.

Show comment
Hide comment
@pkozlowski-opensource

pkozlowski-opensource Oct 3, 2015

Member

System.register is not a distribution format, rather a static optimized module (which is what SystemJS SFX builds are becoming as we integrate optimizations like rollup) or separate raw ES modules are best to provide to users for further use in their own environments.

@guybedford would you mind elaborating on this? My worry is that if we tell our users: "hey, here are raw (unpackaged) files, you can use those to bundle those with your application" we are immediately telling them to use one of the bundlers (JSPM, WebPack or sth else). My fear is that it might be too much for some users as judging from supporting people. For example we've released a new version yesterday and today there were several people struggling with their setup as we've changed rxjs dependency...

Member

pkozlowski-opensource commented Oct 3, 2015

System.register is not a distribution format, rather a static optimized module (which is what SystemJS SFX builds are becoming as we integrate optimizations like rollup) or separate raw ES modules are best to provide to users for further use in their own environments.

@guybedford would you mind elaborating on this? My worry is that if we tell our users: "hey, here are raw (unpackaged) files, you can use those to bundle those with your application" we are immediately telling them to use one of the bundlers (JSPM, WebPack or sth else). My fear is that it might be too much for some users as judging from supporting people. For example we've released a new version yesterday and today there were several people struggling with their setup as we've changed rxjs dependency...

@pkozlowski-opensource

This comment has been minimized.

Show comment
Hide comment
@pkozlowski-opensource

pkozlowski-opensource Oct 3, 2015

Member

We can compile TS to CJS or SystemJS.

Of course. This is not an issue

Then we can use SystemJS to load either format. That way you can use whichever bundle is better

I'm not sure there is clear "better" here

Have you tested both of these to see which pros and cons?

Yes, this is what I'm doing right now. For now it goes pretty much like this:

System.register:

  • can import from any "file" / path
  • big

CJS:

  • smaller than CJS
  • can't import from any "file" / path which might be a pb for router / http bundles

Next step: check if we can have "proper" bundle arithmetic with the way we import "private" paths

Member

pkozlowski-opensource commented Oct 3, 2015

We can compile TS to CJS or SystemJS.

Of course. This is not an issue

Then we can use SystemJS to load either format. That way you can use whichever bundle is better

I'm not sure there is clear "better" here

Have you tested both of these to see which pros and cons?

Yes, this is what I'm doing right now. For now it goes pretty much like this:

System.register:

  • can import from any "file" / path
  • big

CJS:

  • smaller than CJS
  • can't import from any "file" / path which might be a pb for router / http bundles

Next step: check if we can have "proper" bundle arithmetic with the way we import "private" paths

@johnpapa

This comment has been minimized.

Show comment
Hide comment
@johnpapa

johnpapa Oct 3, 2015

Contributor

OK, it sounded as if you were importing with CJS (maybe browserify) and SystemJS. My mistake.

Contributor

johnpapa commented Oct 3, 2015

OK, it sounded as if you were importing with CJS (maybe browserify) and SystemJS. My mistake.

@guybedford

This comment has been minimized.

Show comment
Hide comment
@guybedford

guybedford Oct 3, 2015

Using bundles as a distribution format is not encouraged because bundles contain environment-specific names - that is they assume what names are going to be used in the users' environment loader. For maximum portability usually users should consume separate anonymous modules like you get with CommonJS, AMD or ES6 files.

SFX bundles are going to be optimized further to be a small as format as possible like a CommonJS bundle. Then if you want to be able to provide an angular bundle, that still exhibits public dependencies on http / router / rx etc, that is exactly where a loader is necessary as it allows these public dependencies to be shared without duplication.

Ideally a build would be made of the "main entry points" (through whatever means) that then depends on these external dependencies as an ES6 / CommonJS / AMD / UMD or global module itself:

var angular = (function(http, router, typescriptRuntime) {
  // ... bundled code for angular main entry point given above externals ...
})(require('angular/http'), 
  require('angular/router'), 
  require('typescript-runtime') // ideally this should be a shared dependency too
);
module.exports = angular;

Where http and router are themselves consumable compacted builds with just external / public dependencies listed.

This way the internal modules are all optimized to the smallest possible build, but the public modules and external dependencies remain as separate anonymous modules through this tree-compacting optimization.

For example SystemJS builder can create something like this via builder.buildStatic('angular - http - angular/router', { format: 'cjs' }). Currently a normal CommonJS or ES6 bundler like rollup with an external dependency wrapping would likely provide a smaller overhead.

Then in terms of consuming the file, npm package.json config can set the dependencies, and configurations can be created for SystemJS, jspm and others as well to maximise consumption compatibility.

It is certainly an option to just compile everything into CommonJS or System.register as separate files, and that is a great starting point, but the best performance and footprint would be through some internal compacting optimizations like these.

Sorry for the long-winded comment - just let me know if I can help further at all.

guybedford commented Oct 3, 2015

Using bundles as a distribution format is not encouraged because bundles contain environment-specific names - that is they assume what names are going to be used in the users' environment loader. For maximum portability usually users should consume separate anonymous modules like you get with CommonJS, AMD or ES6 files.

SFX bundles are going to be optimized further to be a small as format as possible like a CommonJS bundle. Then if you want to be able to provide an angular bundle, that still exhibits public dependencies on http / router / rx etc, that is exactly where a loader is necessary as it allows these public dependencies to be shared without duplication.

Ideally a build would be made of the "main entry points" (through whatever means) that then depends on these external dependencies as an ES6 / CommonJS / AMD / UMD or global module itself:

var angular = (function(http, router, typescriptRuntime) {
  // ... bundled code for angular main entry point given above externals ...
})(require('angular/http'), 
  require('angular/router'), 
  require('typescript-runtime') // ideally this should be a shared dependency too
);
module.exports = angular;

Where http and router are themselves consumable compacted builds with just external / public dependencies listed.

This way the internal modules are all optimized to the smallest possible build, but the public modules and external dependencies remain as separate anonymous modules through this tree-compacting optimization.

For example SystemJS builder can create something like this via builder.buildStatic('angular - http - angular/router', { format: 'cjs' }). Currently a normal CommonJS or ES6 bundler like rollup with an external dependency wrapping would likely provide a smaller overhead.

Then in terms of consuming the file, npm package.json config can set the dependencies, and configurations can be created for SystemJS, jspm and others as well to maximise consumption compatibility.

It is certainly an option to just compile everything into CommonJS or System.register as separate files, and that is a great starting point, but the best performance and footprint would be through some internal compacting optimizations like these.

Sorry for the long-winded comment - just let me know if I can help further at all.

@pkozlowski-opensource

This comment has been minimized.

Show comment
Hide comment
@pkozlowski-opensource

pkozlowski-opensource Oct 7, 2015

Member

Ok, so @guybedford was kind enough to spend some time with @robwormald and me yesterday to discuss this topic in great details (once again, thank you Guy so much for your time!). The main outcomes of this discussion from my point of view are:

  • System.register format is not the best long-term idea for out bundles. Arguments are: bundle size, runtime perf hit and "polluting" registry with "private" / internal ng2 modules (paths)
  • CJS bundles would solve all the above problems and would work with the large CJS tooling ecosystem
  • if we want to properly share code between core, http and router using CJS bundles we need to create an additional, shared bundle (angular2.common.js ?) that would have all the code shared by core, http and router. This bundle needs to have its proper public API on which other parts of the framework would depend upon
  • it might be challenging to produce "correct" (ones that don't duplicate code) CJS bundles given the way our code is structured right now
Member

pkozlowski-opensource commented Oct 7, 2015

Ok, so @guybedford was kind enough to spend some time with @robwormald and me yesterday to discuss this topic in great details (once again, thank you Guy so much for your time!). The main outcomes of this discussion from my point of view are:

  • System.register format is not the best long-term idea for out bundles. Arguments are: bundle size, runtime perf hit and "polluting" registry with "private" / internal ng2 modules (paths)
  • CJS bundles would solve all the above problems and would work with the large CJS tooling ecosystem
  • if we want to properly share code between core, http and router using CJS bundles we need to create an additional, shared bundle (angular2.common.js ?) that would have all the code shared by core, http and router. This bundle needs to have its proper public API on which other parts of the framework would depend upon
  • it might be challenging to produce "correct" (ones that don't duplicate code) CJS bundles given the way our code is structured right now
@pkozlowski-opensource

This comment has been minimized.

Show comment
Hide comment
@pkozlowski-opensource

pkozlowski-opensource Oct 9, 2015

Member

@weswigham thnx for pointing out bundling-related work on the TS side. I've just went through entire conversation in Microsoft/TypeScript#4434 and I think that many corner cases that are discussed there (ex. bundled module importing sth from another bundled file, dynamic loads from bundles) are very relevant to the bundling problem we are discussing here.

I want to go over the Microsoft/TypeScript#4434 once again, but my first reaction is that while System.register format has many downsides (as pointed out here) it makes it very easy to create bundles of any shape / content by simply concatenating named System.register calls.

True, the side effect of this is that we are "polluting" global module registry with "internal" module paths, but having all the paths in the registry makes it super easy to load modules from different bundles (both statically and dynamically).

So we've got a bit of catch 22 here: we want to have some info about internal bundle structure, but don't want to expose it outside of the bundle boundary. It feels like a bit like a set of contradictory requirements...

Member

pkozlowski-opensource commented Oct 9, 2015

@weswigham thnx for pointing out bundling-related work on the TS side. I've just went through entire conversation in Microsoft/TypeScript#4434 and I think that many corner cases that are discussed there (ex. bundled module importing sth from another bundled file, dynamic loads from bundles) are very relevant to the bundling problem we are discussing here.

I want to go over the Microsoft/TypeScript#4434 once again, but my first reaction is that while System.register format has many downsides (as pointed out here) it makes it very easy to create bundles of any shape / content by simply concatenating named System.register calls.

True, the side effect of this is that we are "polluting" global module registry with "internal" module paths, but having all the paths in the registry makes it super easy to load modules from different bundles (both statically and dynamically).

So we've got a bit of catch 22 here: we want to have some info about internal bundle structure, but don't want to expose it outside of the bundle boundary. It feels like a bit like a set of contradictory requirements...

pkozlowski-opensource added a commit to pkozlowski-opensource/angular that referenced this issue Oct 11, 2015

pkozlowski-opensource added a commit to pkozlowski-opensource/angular that referenced this issue Oct 26, 2015

@pkozlowski-opensource

This comment has been minimized.

Show comment
Hide comment
@pkozlowski-opensource

pkozlowski-opensource Oct 26, 2015

Member

Quick update based on the work @rkirov and myself were doing today:

We can see 3 different workflows applicable for people using CJS-based tools / workflows:

  • combine application code and angular2 from sources in one bundle (SFX) that people can load via a script tag. This is how people use WebPack with ng2 today
  • combine application code and angular2 bundle in one bundle. For this to work we would have to publish ng2 bundle
  • build bundles with the application code only and use ng2 as an external dependency. In this case we still need to prepare ng2 bundle (UMD).

We've tested all those workflows and the outcome can be summarized quickly as:

  • WebPack configuration is very similar for each presented case, there is no benefit in using bundles (config is not simpler)
  • before we can produce "proper" (no code duplication) ng2 bundles we need to solve a problem of "shared" dependencies which is not trivial
Member

pkozlowski-opensource commented Oct 26, 2015

Quick update based on the work @rkirov and myself were doing today:

We can see 3 different workflows applicable for people using CJS-based tools / workflows:

  • combine application code and angular2 from sources in one bundle (SFX) that people can load via a script tag. This is how people use WebPack with ng2 today
  • combine application code and angular2 bundle in one bundle. For this to work we would have to publish ng2 bundle
  • build bundles with the application code only and use ng2 as an external dependency. In this case we still need to prepare ng2 bundle (UMD).

We've tested all those workflows and the outcome can be summarized quickly as:

  • WebPack configuration is very similar for each presented case, there is no benefit in using bundles (config is not simpler)
  • before we can produce "proper" (no code duplication) ng2 bundles we need to solve a problem of "shared" dependencies which is not trivial

@mhevery mhevery added this to the Beta milestone Oct 27, 2015

@mhevery mhevery added this to the Beta milestone Oct 27, 2015

@mhevery mhevery removed the hotlist: beta label Oct 27, 2015

@pkozlowski-opensource pkozlowski-opensource self-assigned this Oct 27, 2015

@pkozlowski-opensource

This comment has been minimized.

Show comment
Hide comment
@pkozlowski-opensource

pkozlowski-opensource Oct 27, 2015

Member

OK, so after another round of discussions with @IgorMinar and @rkirov here are the decisions regarding CJS bundles:

  • we are going to produce UMD bundles so those can be consumed by more tools / workflows and replace SFX bundles in the longer run
  • we are going to duplicate shared facades code in each and every bundle
  • RxJS won't be bundled but it will be a 3rd party dependency (same as zone.js)
  • We won't provide UMD bundles that would include zone.js, Reflect.metadata or any other external dependency. Those will have to be provided by users (via <script> tag)
Member

pkozlowski-opensource commented Oct 27, 2015

OK, so after another round of discussions with @IgorMinar and @rkirov here are the decisions regarding CJS bundles:

  • we are going to produce UMD bundles so those can be consumed by more tools / workflows and replace SFX bundles in the longer run
  • we are going to duplicate shared facades code in each and every bundle
  • RxJS won't be bundled but it will be a 3rd party dependency (same as zone.js)
  • We won't provide UMD bundles that would include zone.js, Reflect.metadata or any other external dependency. Those will have to be provided by users (via <script> tag)
@rolandjitsu

This comment has been minimized.

Show comment
Hide comment
@rolandjitsu

rolandjitsu Oct 27, 2015

@pkozlowski-opensource I like the idea of not bundling deps, but having the users include them 👍

rolandjitsu commented Oct 27, 2015

@pkozlowski-opensource I like the idea of not bundling deps, but having the users include them 👍

@vsavkin

This comment has been minimized.

Show comment
Hide comment
@vsavkin

vsavkin Nov 11, 2015

Contributor

Closing the issue.

The proposed plan is to:

  • create a sfx/umd bundle containing core + common.
  • create a sfx/umd bundle containing core + common + htttp + router.

These bundles can be used with System or WebPack.

Contributor

vsavkin commented Nov 11, 2015

Closing the issue.

The proposed plan is to:

  • create a sfx/umd bundle containing core + common.
  • create a sfx/umd bundle containing core + common + htttp + router.

These bundles can be used with System or WebPack.

@vsavkin vsavkin closed this Nov 11, 2015

@pkozlowski-opensource

This comment has been minimized.

Show comment
Hide comment
@pkozlowski-opensource

pkozlowski-opensource Nov 12, 2015

Member

This topic is now tracked in #5223

Member

pkozlowski-opensource commented Nov 12, 2015

This topic is now tracked in #5223

@mgechev mgechev referenced this issue Nov 20, 2015

Closed

Production build #256

0 of 4 tasks complete
@born2net

This comment has been minimized.

Show comment
Hide comment
@born2net

born2net Jan 3, 2016

my 2 c, bundling to a large single file is a horrible idea.. I am not a fan of Webpack at all... I think a large scale app needs to be fully modulated and load async as the user traverses through the application. This leads to a much better user experience... this is exactly what I did in http://ng2.javascriptninja.io and so I hope Webpack will not become the standard ng2 of doing things...

regards

Sean.

born2net commented Jan 3, 2016

my 2 c, bundling to a large single file is a horrible idea.. I am not a fan of Webpack at all... I think a large scale app needs to be fully modulated and load async as the user traverses through the application. This leads to a much better user experience... this is exactly what I did in http://ng2.javascriptninja.io and so I hope Webpack will not become the standard ng2 of doing things...

regards

Sean.

@gdi2290

This comment has been minimized.

Show comment
Hide comment
@gdi2290

gdi2290 Jan 4, 2016

Member

There're a lot of benefits for going with Webpack not even mentioning Webpack 2 which has RollUp built-in and uses ES6 syntax. For one, a lot of conventions for the build process of Angular 2 can live in a single plugin. That plugin can do many things that are considered best practice such as #6105, template precompilation, initial precompiled view, precompiled index.html with ES6 support, enableProdMode, dead code elimination, and tree-shaking (with version 2), better universal support, lazy include polyfills, async loading, chunking. In fact, dynamic Type injection if the developer is too lazy to import files, and you can even support not having to write decorators by having webpack infer the component's api while using the compiler during build time.

Member

gdi2290 commented Jan 4, 2016

There're a lot of benefits for going with Webpack not even mentioning Webpack 2 which has RollUp built-in and uses ES6 syntax. For one, a lot of conventions for the build process of Angular 2 can live in a single plugin. That plugin can do many things that are considered best practice such as #6105, template precompilation, initial precompiled view, precompiled index.html with ES6 support, enableProdMode, dead code elimination, and tree-shaking (with version 2), better universal support, lazy include polyfills, async loading, chunking. In fact, dynamic Type injection if the developer is too lazy to import files, and you can even support not having to write decorators by having webpack infer the component's api while using the compiler during build time.

@ansarizafar

This comment has been minimized.

Show comment
Hide comment
@ansarizafar

ansarizafar Jan 4, 2016

+1 for Webpack 2

ansarizafar commented Jan 4, 2016

+1 for Webpack 2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment