Skip to content

Commit f8b5a99

Browse files
filipesilvahansl
authored andcommitted
feat(@angular-devkit/build-webpack): add package
1 parent 2ab8b76 commit f8b5a99

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+902
-40
lines changed

.monorepo.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,16 @@
121121
"hash": "bea6085c247179eedf6a2d312ea5509b",
122122
"snapshotRepo": "angular/angular-devkit-build-angular-builds"
123123
},
124+
"@angular-devkit/build-webpack": {
125+
"name": "Build Webpack",
126+
"links": [
127+
{
128+
"label": "README",
129+
"url": "https://github.com/angular/devkit/blob/master/packages/angular_devkit/build_webpack/README.md"
130+
}
131+
],
132+
"version": "0.0.0"
133+
},
124134
"@angular-devkit/core": {
125135
"name": "Core",
126136
"links": [

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ This is a monorepo which contains many packages:
5656
**Build Angular** | [`@angular-devkit/build-angular`](https://npmjs.com/package/@angular-devkit/build-angular) | [![latest](https://img.shields.io/npm/v/%40angular-devkit%2Fbuild-angular/latest.svg)](https://npmjs.com/package/@angular-devkit/build-angular) | [![README](https://img.shields.io/badge/README--green.svg)](https://github.com/angular/devkit/blob/master/packages/angular_devkit/build_angular/README.md) [![snapshot](https://img.shields.io/badge/snapshot--blue.svg)](https://github.com/angular/angular-devkit-build-angular-builds)
5757
**Build NgPackagr** | [`@angular-devkit/build-ng-packagr`](https://npmjs.com/package/@angular-devkit/build-ng-packagr) | [![latest](https://img.shields.io/npm/v/%40angular-devkit%2Fbuild-ng-packagr/latest.svg)](https://npmjs.com/package/@angular-devkit/build-ng-packagr) | [![README](https://img.shields.io/badge/README--green.svg)](https://github.com/angular/devkit/blob/master/packages/angular_devkit/build_ng_packagr/README.md) [![snapshot](https://img.shields.io/badge/snapshot--blue.svg)](https://github.com/angular/angular-devkit-build-ng-packagr-builds)
5858
**Build Optimizer** | [`@angular-devkit/build-optimizer`](https://npmjs.com/package/@angular-devkit/build-optimizer) | [![latest](https://img.shields.io/npm/v/%40angular-devkit%2Fbuild-optimizer/latest.svg)](https://npmjs.com/package/@angular-devkit/build-optimizer) | [![README](https://img.shields.io/badge/README--green.svg)](https://github.com/angular/devkit/blob/master/packages/angular_devkit/build_optimizer/README.md) [![snapshot](https://img.shields.io/badge/snapshot--blue.svg)](https://github.com/angular/angular-devkit-build-optimizer-builds)
59+
**Build Webpack** | [`@angular-devkit/build-webpack`](https://npmjs.com/package/@angular-devkit/build-webpack) | [![latest](https://img.shields.io/npm/v/%40angular-devkit%2Fbuild-webpack/latest.svg)](https://npmjs.com/package/@angular-devkit/build-webpack) | [![README](https://img.shields.io/badge/README--green.svg)](https://github.com/angular/devkit/blob/master/packages/angular_devkit/build_webpack/README.md)
5960
**Core** | [`@angular-devkit/core`](https://npmjs.com/package/@angular-devkit/core) | [![latest](https://img.shields.io/npm/v/%40angular-devkit%2Fcore/latest.svg)](https://npmjs.com/package/@angular-devkit/core) | [![README](https://img.shields.io/badge/README--green.svg)](https://github.com/angular/devkit/blob/master/packages/angular_devkit/core/README.md) [![snapshot](https://img.shields.io/badge/snapshot--blue.svg)](https://github.com/angular/angular-devkit-core-builds)
6061
**Schematics** | [`@angular-devkit/schematics`](https://npmjs.com/package/@angular-devkit/schematics) | [![latest](https://img.shields.io/npm/v/%40angular-devkit%2Fschematics/latest.svg)](https://npmjs.com/package/@angular-devkit/schematics) | [![README](https://img.shields.io/badge/README--green.svg)](https://github.com/angular/devkit/blob/master/packages/angular_devkit/schematics/README.md) [![snapshot](https://img.shields.io/badge/snapshot--blue.svg)](https://github.com/angular/angular-devkit-schematics-builds)
6162
**Schematics CLI** | [`@angular-devkit/schematics-cli`](https://npmjs.com/package/@angular-devkit/schematics-cli) | [![latest](https://img.shields.io/npm/v/%40angular-devkit%2Fschematics-cli/latest.svg)](https://npmjs.com/package/@angular-devkit/schematics-cli) | [![snapshot](https://img.shields.io/badge/snapshot--blue.svg)](https://github.com/angular/angular-devkit-schematics-cli-builds)

package-lock.json

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
"@types/semver": "^5.3.30",
7979
"@types/source-map": "0.5.2",
8080
"@types/webpack": "^4.1.3",
81+
"@types/webpack-dev-server": "^2.9.4",
8182
"@types/webpack-sources": "^0.1.4",
8283
"ajv": "~6.4.0",
8384
"autoprefixer": "^8.4.1",
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Webpack Builder for Architect
2+
3+
WIP
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"$schema": "../architect/src/builders-schema.json",
3+
"builders": {
4+
"webpack": {
5+
"class": "./src/webpack",
6+
"schema": "./src/webpack/schema.json",
7+
"description": "Build a webpack app."
8+
},
9+
"webpack-dev-server": {
10+
"class": "./src/webpack-dev-server",
11+
"schema": "./src/webpack-dev-server/schema.json",
12+
"description": "Serve a webpack app."
13+
}
14+
}
15+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "@angular-devkit/build-webpack",
3+
"version": "0.0.0",
4+
"description": "Webpack Builder for Architect",
5+
"main": "src/index.js",
6+
"typings": "src/index.d.ts",
7+
"builders": "builders.json",
8+
"scripts": {
9+
"preinstall": "echo DO NOT INSTALL THIS PROJECT, ONLY THE ROOT PROJECT. && exit 1"
10+
},
11+
"dependencies": {
12+
"@angular-devkit/architect": "0.0.0",
13+
"@angular-devkit/core": "0.0.0",
14+
"rxjs": "^6.0.0"
15+
},
16+
"peerDependencies": {
17+
"webpack": "~4.6.0",
18+
"webpack-dev-server": "^3.1.4"
19+
}
20+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
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+
export * from './webpack';
10+
export * from './webpack-dev-server';
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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 { TestProjectHost } from '@angular-devkit/architect/testing';
10+
import { join, normalize } from '@angular-devkit/core';
11+
12+
13+
const devkitRoot = normalize((global as any)._DevKitRoot); // tslint:disable-line:no-any
14+
const basicWorkspaceRoot = join(devkitRoot, 'tests/@angular_devkit/build_webpack/basic-app/');
15+
export const basicHost = new TestProjectHost(basicWorkspaceRoot);
16+
const angularWorkspaceRoot = join(devkitRoot, 'tests/@angular_devkit/build_webpack/angular-app/');
17+
export const angularHost = new TestProjectHost(angularWorkspaceRoot);
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
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 {
10+
BuildEvent,
11+
Builder,
12+
BuilderConfiguration,
13+
BuilderContext,
14+
} from '@angular-devkit/architect';
15+
import { Path, getSystemPath, normalize, resolve } from '@angular-devkit/core';
16+
import { Observable, from } from 'rxjs';
17+
import { concatMap } from 'rxjs/operators';
18+
import * as webpack from 'webpack';
19+
import * as WebpackDevServer from 'webpack-dev-server';
20+
import { LoggingCallback, defaultLoggingCb } from '../webpack';
21+
import { WebpackDevServerBuilderSchema } from './schema';
22+
23+
24+
export class WebpackDevServerBuilder implements Builder<WebpackDevServerBuilderSchema> {
25+
26+
constructor(public context: BuilderContext) { }
27+
28+
run(builderConfig: BuilderConfiguration<WebpackDevServerBuilderSchema>): Observable<BuildEvent> {
29+
const configPath = resolve(this.context.workspace.root,
30+
normalize(builderConfig.options.webpackConfig));
31+
32+
return this.loadWebpackConfig(getSystemPath(configPath)).pipe(
33+
concatMap(config => this.runWebpackDevServer(config)),
34+
);
35+
}
36+
37+
public loadWebpackConfig(webpackConfigPath: string): Observable<webpack.Configuration> {
38+
return from(import(webpackConfigPath));
39+
}
40+
41+
public runWebpackDevServer(
42+
webpackConfig: webpack.Configuration,
43+
devServerCfg?: WebpackDevServer.Configuration,
44+
loggingCb: LoggingCallback = defaultLoggingCb,
45+
): Observable<BuildEvent> {
46+
return new Observable(obs => {
47+
const devServerConfig = devServerCfg || webpackConfig.devServer || {};
48+
devServerConfig.host = devServerConfig.host || 'localhost';
49+
devServerConfig.port = devServerConfig.port || 8080;
50+
51+
if (devServerConfig.stats) {
52+
webpackConfig.stats = devServerConfig.stats;
53+
}
54+
// Disable stats reporting by the devserver, we have our own logger.
55+
devServerConfig.stats = false;
56+
57+
const webpackCompiler = webpack(webpackConfig);
58+
const server = new WebpackDevServer(webpackCompiler, devServerConfig);
59+
60+
webpackCompiler.hooks.done.tap('build-webpack', (stats) => {
61+
// Log stats.
62+
loggingCb(stats, webpackConfig, this.context.logger);
63+
64+
obs.next({ success: !stats.hasErrors() });
65+
});
66+
67+
server.listen(
68+
devServerConfig.port,
69+
devServerConfig.host,
70+
(err) => {
71+
if (err) {
72+
obs.error(err);
73+
}
74+
},
75+
);
76+
77+
// Teardown logic. Close the server when unsubscribed from.
78+
return () => server.close();
79+
});
80+
}
81+
}
82+
83+
84+
export default WebpackDevServerBuilder;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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 { request, runTargetSpec } from '@angular-devkit/architect/testing';
10+
import { from } from 'rxjs';
11+
import { concatMap, take, tap } from 'rxjs/operators';
12+
import { basicHost } from '../test-utils';
13+
14+
15+
describe('Dev Server Builder', () => {
16+
const webpackTargetSpec = { project: 'app', target: 'serve' };
17+
18+
beforeEach(done => basicHost.initialize().toPromise().then(done, done.fail));
19+
afterEach(done => basicHost.restore().toPromise().then(done, done.fail));
20+
21+
it('works', (done) => {
22+
runTargetSpec(basicHost, webpackTargetSpec).pipe(
23+
tap((buildEvent) => expect(buildEvent.success).toBe(true)),
24+
concatMap(() => from(request('http://localhost:8080/bundle.js'))),
25+
tap(response => expect(response).toContain(`console.log('hello world')`)),
26+
take(1),
27+
).toPromise().then(done, done.fail);
28+
}, 30000);
29+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
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+
export interface WebpackDevServerBuilderSchema {
9+
/**
10+
* The path to the Webpack configuration file.
11+
*/
12+
webpackConfig: string;
13+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"title": "Webpack Dev-Server Builder",
3+
"description": "Webpack Dev-Server Builder schema for Architect.",
4+
"type": "object",
5+
"properties": {
6+
"webpackConfig": {
7+
"type": "string",
8+
"description": "The path to the Webpack configuration file."
9+
}
10+
},
11+
"additionalProperties": false,
12+
"required": [
13+
"webpackConfig"
14+
]
15+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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+
import {
9+
BuildEvent,
10+
Builder,
11+
BuilderConfiguration,
12+
BuilderContext,
13+
} from '@angular-devkit/architect';
14+
import { Path, getSystemPath, logging, normalize, resolve } from '@angular-devkit/core';
15+
import { Observable, from } from 'rxjs';
16+
import { concatMap } from 'rxjs/operators';
17+
import * as webpack from 'webpack';
18+
import { WebpackBuilderSchema } from './schema';
19+
20+
21+
export interface LoggingCallback {
22+
(stats: webpack.Stats, config: webpack.Configuration, logger: logging.Logger): void;
23+
}
24+
25+
export const defaultLoggingCb: LoggingCallback = (stats, config, logger) =>
26+
logger.info(stats.toString(config.stats));
27+
28+
export class WebpackBuilder implements Builder<WebpackBuilderSchema> {
29+
30+
constructor(public context: BuilderContext) { }
31+
32+
run(builderConfig: BuilderConfiguration<WebpackBuilderSchema>): Observable<BuildEvent> {
33+
const configPath = resolve(this.context.workspace.root,
34+
normalize(builderConfig.options.webpackConfig));
35+
36+
return this.loadWebpackConfig(getSystemPath(configPath)).pipe(
37+
concatMap(config => this.runWebpack(config)),
38+
);
39+
}
40+
41+
public loadWebpackConfig(webpackConfigPath: string): Observable<webpack.Configuration> {
42+
return from(import(webpackConfigPath));
43+
}
44+
45+
public runWebpack(
46+
config: webpack.Configuration, loggingCb = defaultLoggingCb,
47+
): Observable<BuildEvent> {
48+
return new Observable(obs => {
49+
const webpackCompiler = webpack(config);
50+
51+
const callback: webpack.compiler.CompilerCallback = (err, stats) => {
52+
if (err) {
53+
return obs.error(err);
54+
}
55+
56+
// Log stats.
57+
loggingCb(stats, config, this.context.logger);
58+
59+
obs.next({ success: !stats.hasErrors() });
60+
61+
if (!config.watch) {
62+
obs.complete();
63+
}
64+
};
65+
66+
try {
67+
if (config.watch) {
68+
const watchOptions = config.watchOptions || {};
69+
const watching = webpackCompiler.watch(watchOptions, callback);
70+
71+
// Teardown logic. Close the watcher when unsubscribed from.
72+
return () => watching.close(() => { });
73+
} else {
74+
webpackCompiler.run(callback);
75+
}
76+
} catch (err) {
77+
if (err) {
78+
this.context.logger.error(
79+
'\nAn error occured during the build:\n' + ((err && err.stack) || err));
80+
}
81+
throw err;
82+
}
83+
});
84+
}
85+
}
86+
87+
export default WebpackBuilder;

0 commit comments

Comments
 (0)