Skip to content

Commit 5dd169d

Browse files
committed
feat(@angular-devkit/build-webpack): add support for file replacements
This should replace the environment concept with a more flexible design.
1 parent aa6ae8e commit 5dd169d

File tree

4 files changed

+90
-18
lines changed

4 files changed

+90
-18
lines changed

packages/angular_devkit/build_webpack/src/browser/index.ts

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
98
import {
109
BuildEvent,
1110
Builder,
1211
BuilderConfiguration,
1312
BuilderContext,
1413
} from '@angular-devkit/architect';
15-
import { Path, getSystemPath, normalize, resolve, virtualFs } from '@angular-devkit/core';
14+
import { Path, getSystemPath, join, normalize, resolve, virtualFs } from '@angular-devkit/core';
1615
import * as fs from 'fs';
1716
import { Observable } from 'rxjs/Observable';
1817
import { concat as concatObservable } from 'rxjs/observable/concat';
@@ -90,11 +89,7 @@ export interface BrowserBuilderOptions {
9089
styles: ExtraEntryPoint[];
9190
stylePreprocessorOptions: { includePaths: string[] };
9291

93-
// Some options are not needed anymore.
94-
// app?: string; // apps aren't used with build facade
95-
96-
// TODO: figure out what to do about these.
97-
environment?: string; // Maybe replace with 'fileReplacement' object?
92+
fileReplacements: { from: string; to: string; }[];
9893
}
9994

10095
export interface AssetPattern {
@@ -130,7 +125,7 @@ export class BrowserBuilder implements Builder<BrowserBuilderOptions> {
130125

131126
return concatObservable(
132127
options.deleteOutputPath
133-
? this._deleteOutputDir(root, normalize(options.outputPath))
128+
? this._deleteOutputDir(root, normalize(options.outputPath), this.context.host)
134129
: empty<BuildEvent>(),
135130
new Observable(obs => {
136131
// Ensure Build Optimizer is only used with AOT.
@@ -207,9 +202,22 @@ export class BrowserBuilder implements Builder<BrowserBuilderOptions> {
207202
);
208203
}
209204

210-
buildWebpackConfig(root: Path, projectRoot: Path, options: BrowserBuilderOptions) {
205+
buildWebpackConfig(
206+
root: Path,
207+
projectRoot: Path,
208+
options: BrowserBuilderOptions,
209+
) {
211210
let wco: WebpackConfigOptions;
212211

212+
const host = new virtualFs.AliasHost(this.context.host as virtualFs.Host<fs.Stats>);
213+
214+
options.fileReplacements.forEach(({from, to}) => {
215+
host.aliases.set(
216+
join(root, normalize(from)),
217+
join(root, normalize(to)),
218+
);
219+
});
220+
213221
// TODO: make target defaults into configurations instead
214222
// options = this.addTargetDefaults(options);
215223

@@ -268,24 +276,24 @@ export class BrowserBuilder implements Builder<BrowserBuilderOptions> {
268276

269277
if (wco.appConfig.main || wco.appConfig.polyfills) {
270278
const typescriptConfigPartial = wco.buildOptions.aot
271-
? getAotConfig(wco, this.context.host as virtualFs.Host<fs.Stats>)
272-
: getNonAotConfig(wco, this.context.host as virtualFs.Host<fs.Stats>);
279+
? getAotConfig(wco, host)
280+
: getNonAotConfig(wco, host);
273281
webpackConfigs.push(typescriptConfigPartial);
274282
}
275283

276284
return webpackMerge(webpackConfigs);
277285
}
278286

279-
private _deleteOutputDir(root: Path, outputPath: Path): Observable<void> {
287+
private _deleteOutputDir(root: Path, outputPath: Path, host: virtualFs.Host): Observable<void> {
280288
const resolvedOutputPath = resolve(root, outputPath);
281289
if (resolvedOutputPath === root) {
282290
throw new Error('Output path MUST not be project root directory!');
283291
}
284292

285-
return this.context.host.exists(resolvedOutputPath).pipe(
293+
return host.exists(resolvedOutputPath).pipe(
286294
switchMap(exists => {
287295
if (exists) {
288-
return this.context.host.delete(resolvedOutputPath);
296+
return host.delete(resolvedOutputPath);
289297
} else {
290298
return empty<void>();
291299
}

packages/angular_devkit/build_webpack/src/browser/schema.json

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,21 @@
5858
"description": "Defines the optimization level of the build.",
5959
"default": false
6060
},
61-
"environment": {
62-
"type": "string",
63-
"description": "Defines the build environment."
61+
"fileReplacements": {
62+
"description": "Replace files with other files in the build.",
63+
"type": "array",
64+
"items": {
65+
"type": "object",
66+
"properties": {
67+
"from": {
68+
"type": "string"
69+
},
70+
"to": {
71+
"type": "string"
72+
}
73+
}
74+
},
75+
"default": []
6476
},
6577
"outputPath": {
6678
"type": "string",
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import { join, normalize, virtualFs } from '@angular-devkit/core';
10+
import { tap } from 'rxjs/operators';
11+
import { browserTargetSpec, host, runTargetSpec } from '../utils';
12+
13+
14+
describe('Browser Builder', () => {
15+
const outputPath = normalize('dist');
16+
17+
beforeEach(done => host.initialize().subscribe(undefined, done.fail, done));
18+
afterEach(done => host.restore().subscribe(undefined, done.fail, done));
19+
20+
it('allows file replacements', (done) => {
21+
host.writeMultipleFiles({
22+
'src/meaning-too.ts': 'export var meaning = 42;',
23+
'src/meaning.ts': `export var meaning = 10;`,
24+
25+
'src/main.ts': `
26+
import { meaning } from './meaning';
27+
28+
console.log(meaning);
29+
`,
30+
});
31+
32+
const overrides = {
33+
fileReplacements: [
34+
{
35+
from: normalize('/src/meaning.ts'),
36+
to: normalize('/src/meaning-too.ts'),
37+
},
38+
],
39+
};
40+
41+
runTargetSpec(host, browserTargetSpec, overrides).pipe(
42+
tap((buildEvent) => expect(buildEvent.success).toBe(true)),
43+
tap(() => {
44+
const fileName = join(outputPath, 'main.js');
45+
expect(virtualFs.fileBufferToString(host.scopedSync().read(fileName)))
46+
.toMatch(/meaning\s*=\s*42/);
47+
expect(virtualFs.fileBufferToString(host.scopedSync().read(fileName)))
48+
.not.toMatch(/meaning\s*=\s*10/);
49+
}),
50+
).subscribe(undefined, done.fail, done);
51+
}, 30000);
52+
});

packages/angular_devkit/core/src/virtual-fs/host/alias_spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ describe('AliasHost', () => {
4343
const aHost = new AliasHost(host);
4444
aHost.read(normalize('/some/folder/file'))
4545
.subscribe(x => expect(x).toBe(content));
46-
aHost.aliases.set(normalize('/some'), normalize('/other');
46+
aHost.aliases.set(normalize('/some'), normalize('/other'));
4747

4848
// This file will not exist because /other/path does not exist.
4949
try {

0 commit comments

Comments
 (0)