Skip to content

Commit 84017f8

Browse files
committed
feat(prod): add bundling (angular#549)
1 parent 78e040c commit 84017f8

File tree

6 files changed

+86
-26
lines changed

6 files changed

+86
-26
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export {environment} from './environment';
2+
export {<%= jsComponentName %>AppComponent} from './<%= htmlComponentName %>.component';

addon/ng2/blueprints/ng2/files/__path__/main.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import { bootstrap } from '@angular/platform-browser-dynamic';
2-
import { enableProdMode } from '@angular/core';
3-
import { environment } from './app/environment';
4-
import { <%= jsComponentName %>AppComponent } from './app/<%= htmlComponentName %>.component';
1+
import {bootstrap} from '@angular/platform-browser-dynamic';
2+
import {enableProdMode} from '@angular/core';
3+
import {<%= jsComponentName %>AppComponent, environment} from './app/';
54

65
if (environment.production) {
76
enableProdMode();
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/* jshint node: true, esversion: 6 */
2+
'use strict';
3+
4+
const Plugin = require('broccoli-caching-writer');
5+
const Builder = require('systemjs-builder');
6+
const fse = require('fs-extra');
7+
const path = require('path');
8+
9+
class BundlePlugin extends Plugin {
10+
constructor(inputNodes, options) {
11+
super(inputNodes, {});
12+
options = options || {};
13+
this.options = options;
14+
}
15+
16+
build() {
17+
var relativeRoot = path.relative(process.cwd(), this.inputPaths[0]);
18+
var builder = new Builder(relativeRoot, `${relativeRoot}/system-config.js`);
19+
return builder.bundle('main - [app/**/*]',
20+
`${this.outputPath}/main.js`, {
21+
minify: true
22+
})
23+
.then(() => builder.bundle('app - (app/**/*.js - [app/**/*.js])',
24+
`${this.outputPath}/app/index.js`, {
25+
minify: true
26+
}))
27+
.then(() => fse.copySync(`${this.inputPaths[0]}/system-config.js`,
28+
`${this.outputPath}/system-config.js`));
29+
}
30+
}
31+
32+
module.exports = BundlePlugin;

lib/broccoli/angular2-app.js

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
11
'use strict';
22
const path = require('path');
3+
const fs = require('fs');
34

45
const isProduction = require('./is-production');
56
const BroccoliPlugin = require('broccoli-writer');
67
const BroccoliConfigReplace = require('./broccoli-config-replace');
78
const BroccoliTypescript = require('./broccoli-typescript');
89
const BroccoliSwManifest = require('./service-worker-manifest').default;
10+
const BundlePlugin = require('./angular-broccoli-bundle');
911
const BroccoliFunnel = require('broccoli-funnel');
1012
const BroccoliMergeTrees = require('broccoli-merge-trees');
11-
const BroccoliUglify = require('broccoli-uglify-js');
13+
const BroccoliSource = require('broccoli-source');
14+
const UnwatchedDir = BroccoliSource.UnwatchedDir;
1215
const Project = require('ember-cli/lib/models/project');
1316
const config = require('../../addon/ng2/models/config');
1417

15-
1618
class Angular2App extends BroccoliPlugin {
1719
constructor(project, inputNode, options) {
1820
const ngConfig = config.CliConfig.fromProject();
19-
21+
2022
if (!options) {
2123
options = inputNode;
2224
inputNode = null;
@@ -117,7 +119,7 @@ class Angular2App extends BroccoliPlugin {
117119
}
118120
}));
119121
}
120-
122+
121123
// Add the public folder in.
122124
buildTrees.push(new BroccoliFunnel(this._inputNode, {
123125
include: ['public/**/*'],
@@ -131,7 +133,12 @@ class Angular2App extends BroccoliPlugin {
131133
}));
132134

133135
var merged = new BroccoliMergeTrees(buildTrees, { overwrite: true });
134-
merged = new BroccoliMergeTrees([merged, new BroccoliSwManifest([merged])]);
136+
137+
if (isProduction) {
138+
merged = this._getBundleTree(merged);
139+
merged = new BroccoliMergeTrees([merged, new BroccoliSwManifest([merged])]);
140+
}
141+
135142
return new BroccoliFunnel(merged, {
136143
destDir: this._destDir
137144
});
@@ -279,7 +286,6 @@ class Angular2App extends BroccoliPlugin {
279286

280287
if (isProduction) {
281288
tsTreeExcludes.push(excludeSpecFiles);
282-
tsTree = BroccoliUglify(tsTree);
283289
}
284290

285291
tsTree = new BroccoliFunnel(tsTree, {
@@ -307,7 +313,7 @@ class Angular2App extends BroccoliPlugin {
307313
vendorNpmFiles = vendorNpmFiles.concat(this._options.vendorNpmFiles);
308314
}
309315

310-
return new BroccoliFunnel('node_modules', {
316+
return new BroccoliFunnel(new UnwatchedDir('node_modules'), {
311317
include: vendorNpmFiles,
312318
destDir: 'vendor',
313319
name: 'vendor'
@@ -357,6 +363,25 @@ class Angular2App extends BroccoliPlugin {
357363
getDestinationPath: () => 'environment.ts'
358364
});
359365
}
366+
367+
_getBundleTree(preBundleTree){
368+
var indexFile = path.join(this._sourceDir, 'index.html');
369+
var indexContent = fs.readFileSync(indexFile, 'utf8');
370+
var scriptTagVendorFiles = indexContent.match(/vendor\/[^"']*\.js/gi);
371+
372+
var scriptTree = new BroccoliFunnel(preBundleTree, {
373+
include: scriptTagVendorFiles
374+
});
375+
376+
var nonJsTree = new BroccoliFunnel(preBundleTree, {
377+
exclude: ['**/*.js', '**/*.js.map']
378+
});
379+
var jsTree = new BroccoliFunnel(preBundleTree, {
380+
include: ['**/*.js']
381+
});
382+
383+
return BroccoliMergeTrees([nonJsTree, scriptTree, new BundlePlugin([jsTree])], { overwrite: true });
384+
}
360385
}
361386

362387
module.exports = Angular2App;

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"broccoli-concat": "^2.2.0",
3636
"broccoli-funnel": "^1.0.1",
3737
"broccoli-merge-trees": "^1.1.1",
38-
"broccoli-uglify-js": "^0.1.3",
38+
"broccoli-source": "^1.1.0",
3939
"broccoli-writer": "^0.1.1",
4040
"chalk": "^1.1.3",
4141
"ember-cli": "2.5.0",
@@ -50,6 +50,7 @@
5050
"shelljs": "^0.7.0",
5151
"silent-error": "^1.0.0",
5252
"symlink-or-copy": "^1.0.3",
53+
"systemjs-builder": "^0.15.16",
5354
"typescript": "^1.8.10",
5455
"typings": "^0.8.1"
5556
},

tests/e2e/e2e_workflow.spec.js

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,25 @@ describe('Basic end-to-end Workflow', function () {
6464
// stuck to the first build done
6565
sh.exec('ng build --environment=production --silent');
6666
expect(existsSync(path.join(process.cwd(), 'dist'))).to.be.equal(true);
67-
var envPath = path.join(process.cwd(), 'dist', 'app', 'environment.js');
68-
var envContent = fs.readFileSync(envPath, { encoding: 'utf8' });
69-
expect(envContent).to.include('production:true');
67+
var appBundlePath = path.join(process.cwd(), 'dist', 'app', 'index.js');
68+
var appBundleContent = fs.readFileSync(appBundlePath, { encoding: 'utf8' });
69+
// production: true minimized turns into production:!0
70+
expect(appBundleContent).to.include('production:!0');
7071
// Also does not create new things in GIT.
7172
expect(sh.exec('git status --porcelain').output).to.be.equal(undefined);
7273
});
7374

75+
it('Produces a service worker manifest after production build', function () {
76+
var manifestPath = path.join(process.cwd(), 'dist', 'manifest.appcache');
77+
expect(existsSync(manifestPath)).to.be.equal(true);
78+
// Read the worker.
79+
//TODO: Commenting this out because it makes eslint fail(need to figure out why this expect was commented out)
80+
// var lines = fse.readFileSync(manifestPath, {encoding: 'utf8'}).trim().split('\n');
81+
82+
// Check that a few critical files have been detected.
83+
// expect(lines).to.include(`${path.sep}index.html`);
84+
});
85+
7486
it('Can run `ng build` in created project', function () {
7587
this.timeout(420000);
7688

@@ -87,17 +99,6 @@ describe('Basic end-to-end Workflow', function () {
8799
});
88100
});
89101

90-
it('Produces a service worker manifest after initial build', function () {
91-
var manifestPath = path.join(process.cwd(), 'dist', 'manifest.appcache');
92-
expect(existsSync(manifestPath)).to.be.equal(true);
93-
// Read the worker.
94-
//TODO: Commenting this out because it makes eslint fail(need to figure out why this expect was commented out)
95-
// var lines = fse.readFileSync(manifestPath, {encoding: 'utf8'}).trim().split('\n');
96-
97-
// Check that a few critical files have been detected.
98-
// expect(lines).to.include(`${path.sep}index.html`);
99-
});
100-
101102
it('Perform `ng test` after initial build', function () {
102103
this.timeout(420000);
103104

0 commit comments

Comments
 (0)