Skip to content

Commit 5f37828

Browse files
committed
refactor(angular2app): separate tree creation for each step of the build
1 parent 21bb5bf commit 5f37828

File tree

2 files changed

+198
-93
lines changed

2 files changed

+198
-93
lines changed

lib/broccoli/angular2-app.js

+182-93
Original file line numberDiff line numberDiff line change
@@ -8,96 +8,39 @@ var Funnel = require('broccoli-funnel');
88
var mergeTrees = require('broccoli-merge-trees');
99
var uglify = require('broccoli-uglify-js');
1010
var Project = require('ember-cli/lib/models/project');
11+
var sourceDir = 'src';
1112

1213
module.exports = Angular2App;
1314

1415
function Angular2App(defaults, options) {
1516
this._initProject();
1617
this._notifyAddonIncluded();
17-
this.options = options;
18+
this.options = options || {};
1819
}
1920

20-
Angular2App.prototype.toTree = function () {
21-
var sourceDir = 'src';
22-
23-
var sourceTree = new Funnel('src', {
24-
include: ['*.ts', '**/*.ts', '**/*.d.ts'],
25-
destDir: 'src'
26-
});
27-
28-
var typingsTree = new Funnel('typings', {
29-
include: ['browser.d.ts', 'browser/**'],
30-
destDir: 'typings'
31-
});
32-
33-
var vendorNpmFiles = [
34-
'systemjs/dist/system-polyfills.js',
35-
'systemjs/dist/system.src.js',
36-
'es6-shim/es6-shim.js',
37-
'angular2/bundles/angular2-polyfills.js',
38-
'rxjs/bundles/Rx.js',
39-
'angular2/bundles/angular2.dev.js',
40-
'angular2/bundles/http.dev.js',
41-
'angular2/bundles/router.dev.js',
42-
'angular2/bundles/upgrade.dev.js'
43-
];
44-
45-
if (this.options && this.options.vendorNpmFiles) {
46-
vendorNpmFiles = vendorNpmFiles.concat(this.options.vendorNpmFiles);
47-
}
48-
49-
var tsconfig = JSON.parse(fs.readFileSync('src/tsconfig.json', 'utf-8'));
50-
// Add all spec files to files. We need this because spec files are their own entry
51-
// point.
52-
fs.readdirSync(sourceDir).forEach(function addPathRecursive(name) {
53-
const filePath = path.join(sourceDir, name);
54-
if (filePath.match(/\.spec\.[jt]s$/)) {
55-
tsconfig.files.push(name);
56-
} else if (fs.statSync(filePath).isDirectory()) {
57-
// Recursively call this function with the full sub-path.
58-
fs.readdirSync(filePath).forEach(function (n) {
59-
addPathRecursive(path.join(name, n));
60-
});
61-
}
62-
});
63-
64-
// Because the tsconfig does not include the source directory, add this as the first path
65-
// element.
66-
tsconfig.files = tsconfig.files.map(name => path.join(sourceDir, name));
67-
68-
var srcAndTypingsTree = mergeTrees([sourceTree, typingsTree]);
69-
var tsTree = new compileWithTypescript(srcAndTypingsTree, tsconfig);
70-
71-
var tsTreeExcludes = ['*.d.ts', 'tsconfig.json'];
72-
var excludeSpecFiles = '**/*.spec.*';
7321

74-
if (isProduction) {
75-
tsTreeExcludes.push(excludeSpecFiles);
76-
tsTree = uglify(tsTree);
77-
}
78-
79-
tsTree = new Funnel(tsTree, {
80-
srcDir: 'src',
81-
exclude: tsTreeExcludes
82-
});
83-
84-
var tsSrcTree = new Funnel(sourceDir, {
85-
include: ['**/*.ts'],
86-
allowEmpty: true
87-
});
88-
89-
var assetTree = new Funnel(sourceDir, {
90-
include: ['**/*.*'],
91-
exclude: ['**/*.ts', '**/*.js'],
92-
allowEmpty: true
93-
});
94-
95-
var vendorNpmTree = new Funnel('node_modules', {
96-
include: vendorNpmFiles,
97-
destDir: 'vendor'
98-
});
22+
/**
23+
Create and return the app build system tree that:
24+
- Get the `assets` tree
25+
- Get the TS tree
26+
- Get the TS src tree
27+
- Get the index.html tree
28+
- Get the NPM modules tree
29+
- Apply/remove stuff based on the environment (dev|prod)
30+
- Return the app trees to be extended
31+
32+
@public
33+
@method toTree
34+
@return {Array<Tree>} The app trees that can be used to extend the build
35+
*/
36+
Angular2App.prototype.toTree = function () {
37+
var assetTree = this._getAssetsTree();
38+
var tsTree = this._getTsTree();
39+
var tsSrcTree = this._getTsSrcTree();
40+
var indexTree = this._getIndexTree();
41+
var vendorNpmTree = this._getVendorNpmTree();
9942

100-
var buildTrees = [assetTree, tsTree, this.index(), vendorNpmTree];
43+
var buildTrees = [assetTree, tsTree, indexTree, vendorNpmTree];
10144

10245
if (!isProduction) {
10346
buildTrees.push(tsSrcTree);
@@ -108,17 +51,14 @@ Angular2App.prototype.toTree = function () {
10851
return mergeTrees([merged, new SwManifest([merged])]);
10952
};
11053

54+
11155
/**
11256
@private
11357
@method _initProject
11458
@param {Object} options
11559
*/
11660
Angular2App.prototype._initProject = function () {
11761
this.project = Project.closestSync(process.cwd());
118-
119-
/*if (options.configPath) {
120-
this.project.configPath = function() { return options.configPath; };
121-
}*/
12262
};
12363

12464
/**
@@ -140,6 +80,7 @@ Angular2App.prototype._notifyAddonIncluded = function () {
14080
}, this);
14181
};
14282

83+
14384
/**
14485
Loads and initializes addons for this project.
14586
Calls initializeAddons on the Project.
@@ -151,6 +92,7 @@ Angular2App.prototype.initializeAddons = function () {
15192
this.project.initializeAddons();
15293
};
15394

95+
15496
/**
15597
Returns the content for a specific type (section) for index.html.
15698
@@ -167,12 +109,12 @@ Angular2App.prototype.initializeAddons = function () {
167109
types (eg. 'some-addon-section').
168110
169111
@private
170-
@method contentFor
112+
@method _contentFor
171113
@param {RegExP} match Regular expression to match against
172114
@param {String} type Type of content
173115
@return {String} The content.
174116
*/
175-
Angular2App.prototype.contentFor = function (match, type) {
117+
Angular2App.prototype._contentFor = function (match, type) {
176118
var content = [];
177119

178120
/*switch (type) {
@@ -194,15 +136,16 @@ Angular2App.prototype.contentFor = function (match, type) {
194136
return content.join('\n');
195137
};
196138

139+
197140
/**
198141
@private
199-
@method _configReplacePatterns
200-
@return
142+
@method _getReplacePatterns
143+
@return Array<Pattern> Replace patterns
201144
*/
202-
Angular2App.prototype._configReplacePatterns = function () {
145+
Angular2App.prototype._getReplacePatterns = function () {
203146
return [{
204147
match: /\{\{content-for ['"](.+)["']\}\}/g,
205-
replacement: isProduction ? '' : this.contentFor.bind(this)
148+
replacement: isProduction ? '' : this._contentFor.bind(this)
206149
}];
207150
};
208151

@@ -211,10 +154,10 @@ Angular2App.prototype._configReplacePatterns = function () {
211154
Returns the tree for app/index.html
212155
213156
@private
214-
@method index
157+
@method _getIndexTree
215158
@return {Tree} Tree for app/index.html
216159
*/
217-
Angular2App.prototype.index = function () {
160+
Angular2App.prototype._getIndexTree = function () {
218161
var htmlName = 'index.html';
219162
var files = [
220163
'index.html'
@@ -228,6 +171,152 @@ Angular2App.prototype.index = function () {
228171

229172
return configReplace(index, {
230173
files: [htmlName],
231-
patterns: this._configReplacePatterns()
174+
patterns: this._getReplacePatterns()
175+
});
176+
};
177+
178+
179+
/**
180+
Returns the source root dir tree
181+
182+
@private
183+
@method _getSourceTree
184+
@return {Tree} Tree for the src dir
185+
*/
186+
Angular2App.prototype._getSourceTree = function () {
187+
return new Funnel('src', {
188+
include: ['*.ts', '**/*.ts', '**/*.d.ts'],
189+
destDir: 'src'
232190
});
233191
};
192+
193+
194+
/**
195+
Returns the typings tree
196+
197+
@private
198+
@method _getTypingsTree
199+
@return {Tree} Tree for the src dir
200+
*/
201+
Angular2App.prototype._getTypingsTree = function () {
202+
return new Funnel('typings', {
203+
include: ['browser.d.ts', 'browser/**'],
204+
destDir: 'typings'
205+
});
206+
};
207+
208+
209+
/**
210+
Returns the TS tree
211+
212+
@private
213+
@method _getTsTree
214+
@return {Tree} Tree for TypeScript files
215+
*/
216+
Angular2App.prototype._getTsTree = function () {
217+
var typingsTree = this._getTypingsTree();
218+
var sourceTree = this._getSourceTree();
219+
220+
var tsconfig = JSON.parse(fs.readFileSync('src/tsconfig.json', 'utf-8'));
221+
// Add all spec files to files. We need this because spec files are their own entry
222+
// point.
223+
fs.readdirSync(sourceDir).forEach(function addPathRecursive(name) {
224+
const filePath = path.join(sourceDir, name);
225+
if (filePath.match(/\.spec\.[jt]s$/)) {
226+
tsconfig.files.push(name);
227+
} else if (fs.statSync(filePath).isDirectory()) {
228+
// Recursively call this function with the full sub-path.
229+
fs.readdirSync(filePath).forEach(function (n) {
230+
addPathRecursive(path.join(name, n));
231+
});
232+
}
233+
});
234+
235+
// Because the tsconfig does not include the source directory, add this as the first path
236+
// element.
237+
tsconfig.files = tsconfig.files.map(name => path.join(sourceDir, name));
238+
239+
var srcAndTypingsTree = mergeTrees([sourceTree, typingsTree]);
240+
var tsTree = new compileWithTypescript(srcAndTypingsTree, tsconfig);
241+
242+
var tsTreeExcludes = ['*.d.ts', 'tsconfig.json'];
243+
var excludeSpecFiles = '**/*.spec.*';
244+
245+
if (isProduction) {
246+
tsTreeExcludes.push(excludeSpecFiles);
247+
tsTree = uglify(tsTree);
248+
}
249+
250+
tsTree = new Funnel(tsTree, {
251+
srcDir: 'src',
252+
exclude: tsTreeExcludes
253+
});
254+
255+
return tsTree;
256+
};
257+
258+
259+
/**
260+
Returns the `vendorNpm` tree by merging the CLI dependencies plus the ones
261+
passed by the user
262+
263+
@private
264+
@method getVendorNpmTree
265+
@return {Tree} The NPM tree.
266+
*/
267+
Angular2App.prototype._getVendorNpmTree = function () {
268+
var vendorNpmFiles = [
269+
'systemjs/dist/system-polyfills.js',
270+
'systemjs/dist/system.src.js',
271+
'es6-shim/es6-shim.js',
272+
'angular2/bundles/angular2-polyfills.js',
273+
'rxjs/bundles/Rx.js',
274+
'angular2/bundles/angular2.dev.js',
275+
'angular2/bundles/http.dev.js',
276+
'angular2/bundles/router.dev.js',
277+
'angular2/bundles/upgrade.dev.js'
278+
];
279+
280+
if (this.options.vendorNpmFiles) {
281+
vendorNpmFiles = vendorNpmFiles.concat(this.options.vendorNpmFiles);
282+
}
283+
284+
var vendorNpmTree = new Funnel('node_modules', {
285+
include: vendorNpmFiles,
286+
destDir: 'vendor'
287+
});
288+
289+
return vendorNpmTree;
290+
};
291+
292+
293+
/**
294+
Returns the `assets` tree
295+
296+
@private
297+
@method getAssetsTree
298+
@return {Tree} The assets tree.
299+
*/
300+
Angular2App.prototype._getAssetsTree = function () {
301+
return new Funnel(sourceDir, {
302+
include: ['**/*.*'],
303+
exclude: ['**/*.ts', '**/*.js'],
304+
allowEmpty: true
305+
});
306+
};
307+
308+
309+
/**
310+
Returns the `tsSrc` tree
311+
312+
@private
313+
@method getTsSrcTree
314+
@return {Tree} The TS src tree.
315+
*/
316+
Angular2App.prototype._getTsSrcTree = function () {
317+
return new Funnel(sourceDir, {
318+
include: ['**/*.ts'],
319+
allowEmpty: true
320+
});
321+
};
322+

tests/e2e/e2e_workflow.spec.js

+16
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,22 @@ describe('Basic end-to-end Workflow', function () {
7373
});
7474
});
7575

76+
it('Supports production builds via `ng build --envinroment=production`', function() {
77+
this.timeout(420000);
78+
79+
return ng(['build', '--environment=production', '--silent'])
80+
.then(function () {
81+
expect(existsSync(path.join(process.cwd(), 'dist'))).to.be.equal(true);
82+
})
83+
.then(function () {
84+
// Also does not create new things in GIT.
85+
expect(sh.exec('git status --porcelain').output).to.be.equal('');
86+
})
87+
.catch(() => {
88+
throw new Error('Build failed.');
89+
});
90+
});
91+
7692
it('Produces a service worker manifest after initial build', function () {
7793
var manifestPath = path.join(process.cwd(), 'dist', 'manifest.appcache');
7894
expect(existsSync(manifestPath)).to.be.equal(true);

0 commit comments

Comments
 (0)