Skip to content

Commit 9a0eaf9

Browse files
filipesilvaZhicheng Wang
authored and
Zhicheng Wang
committed
fix(serve): allow relevant live-reload options to function (angular#4744)
This provides implementations for the following serve command options: live-reload [boolean; default: true] -- flag to control the browser live reload capability live-reload-client [URL; default: ssl/host/port command options] -- specify the URL that the live reload browser client will use Closes angular#3361
1 parent c67607f commit 9a0eaf9

File tree

4 files changed

+215
-133
lines changed

4 files changed

+215
-133
lines changed

docs/documentation/serve.md

+1-7
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,7 @@
1616

1717
`--live-reload` (`-lr`) flag to turn off live reloading
1818

19-
`--live-reload-host` (`-lrh`) specify the host for live reloading
20-
21-
`--live-reload-base-url` (`-lrbu`) specify the base URL for live reloading
22-
23-
`--live-reload-port` (`-lrp`) port for live reloading
24-
25-
`--live-reload-live-css` flag to live reload CSS
19+
`--live-reload-client` specify the URL that the live reload browser client will use
2620

2721
`--ssl` flag to turn on SSL
2822

packages/@angular/cli/commands/serve.ts

+42-91
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,19 @@ import { overrideOptions } from '../utilities/override-options';
1010
const SilentError = require('silent-error');
1111
const PortFinder = require('portfinder');
1212
const Command = require('../ember-cli/lib/models/command');
13-
const getPort = <any>denodeify(PortFinder.getPort);
14-
15-
PortFinder.basePort = 49152;
13+
const getPort = denodeify<{ host: string, port: number }, number>(PortFinder.getPort);
1614

1715
const config = CliConfig.fromProject() || CliConfig.fromGlobal();
1816
const defaultPort = process.env.PORT || config.get('defaults.serve.port');
1917
const defaultHost = config.get('defaults.serve.host');
18+
PortFinder.basePort = defaultPort;
2019

2120
export interface ServeTaskOptions extends BuildOptions {
2221
port?: number;
2322
host?: string;
2423
proxyConfig?: string;
2524
liveReload?: boolean;
26-
liveReloadHost?: string;
27-
liveReloadPort?: number;
28-
liveReloadBaseUrl?: string;
29-
liveReloadLiveCss?: boolean;
25+
liveReloadClient?: string;
3026
ssl?: boolean;
3127
sslKey?: string;
3228
sslCert?: string;
@@ -35,80 +31,57 @@ export interface ServeTaskOptions extends BuildOptions {
3531
}
3632

3733
// Expose options unrelated to live-reload to other commands that need to run serve
38-
export const baseServeCommandOptions: any = baseBuildCommandOptions.concat([
39-
{ name: 'port', type: Number, default: defaultPort, aliases: ['p'] },
40-
{
41-
name: 'host',
42-
type: String,
43-
default: defaultHost,
44-
aliases: ['H'],
45-
description: `Listens only on ${defaultHost} by default`
46-
},
47-
{ name: 'proxy-config', type: 'Path', aliases: ['pc'] },
48-
{ name: 'ssl', type: Boolean, default: false },
49-
{ name: 'ssl-key', type: String, default: 'ssl/server.key' },
50-
{ name: 'ssl-cert', type: String, default: 'ssl/server.crt' },
51-
{
52-
name: 'open',
53-
type: Boolean,
54-
default: false,
55-
aliases: ['o'],
56-
description: 'Opens the url in default browser',
57-
}
58-
]);
34+
export const baseServeCommandOptions: any = overrideOptions(
35+
baseBuildCommandOptions.concat([
36+
{ name: 'port', type: Number, default: defaultPort, aliases: ['p'] },
37+
{
38+
name: 'host',
39+
type: String,
40+
default: defaultHost,
41+
aliases: ['H'],
42+
description: `Listens only on ${defaultHost} by default`
43+
},
44+
{ name: 'proxy-config', type: 'Path', aliases: ['pc'] },
45+
{ name: 'ssl', type: Boolean, default: false },
46+
{ name: 'ssl-key', type: String, default: 'ssl/server.key' },
47+
{ name: 'ssl-cert', type: String, default: 'ssl/server.crt' },
48+
{
49+
name: 'open',
50+
type: Boolean,
51+
default: false,
52+
aliases: ['o'],
53+
description: 'Opens the url in default browser',
54+
},
55+
{ name: 'live-reload', type: Boolean, default: true, aliases: ['lr'] },
56+
{
57+
name: 'live-reload-client',
58+
type: String,
59+
description: 'specify the URL that the live reload browser client will use'
60+
},
61+
{
62+
name: 'hmr',
63+
type: Boolean,
64+
default: false,
65+
description: 'Enable hot module replacement',
66+
}
67+
]), [
68+
{ name: 'watch', default: true },
69+
]
70+
);
5971

6072
const ServeCommand = Command.extend({
6173
name: 'serve',
6274
description: 'Builds and serves your app, rebuilding on file changes.',
6375
aliases: ['server', 's'],
6476

65-
availableOptions: overrideOptions(
66-
baseServeCommandOptions.concat([
67-
{ name: 'live-reload', type: Boolean, default: true, aliases: ['lr'] },
68-
{
69-
name: 'live-reload-host',
70-
type: String,
71-
aliases: ['lrh'],
72-
description: 'Defaults to host'
73-
},
74-
{
75-
name: 'live-reload-base-url',
76-
type: String,
77-
aliases: ['lrbu'],
78-
description: 'Defaults to baseURL'
79-
},
80-
{
81-
name: 'live-reload-port',
82-
type: Number,
83-
aliases: ['lrp'],
84-
description: '(Defaults to port number within [49152...65535])'
85-
},
86-
{
87-
name: 'live-reload-live-css',
88-
type: Boolean,
89-
default: true,
90-
description: 'Whether to live reload CSS (default true)'
91-
},
92-
{
93-
name: 'hmr',
94-
type: Boolean,
95-
default: false,
96-
description: 'Enable hot module replacement',
97-
}
98-
]), [
99-
{ name: 'watch', default: true },
100-
]
101-
),
77+
availableOptions: baseServeCommandOptions,
10278

10379
run: function (commandOptions: ServeTaskOptions) {
10480
const ServeTask = require('../tasks/serve').default;
10581

10682
Version.assertAngularVersionIs2_3_1OrHigher(this.project.root);
107-
commandOptions.liveReloadHost = commandOptions.liveReloadHost || commandOptions.host;
10883

109-
return checkPort(commandOptions.port, commandOptions.host)
110-
.then((port: number) => commandOptions.port = port)
111-
.then(() => autoFindLiveReloadPort(commandOptions))
84+
return checkExpressPort(commandOptions)
11285
.then((opts: ServeTaskOptions) => {
11386
const serve = new ServeTask({
11487
ui: this.ui,
@@ -137,26 +110,4 @@ function checkExpressPort(commandOptions: ServeTaskOptions) {
137110
});
138111
}
139112

140-
function autoFindLiveReloadPort(commandOptions: ServeTaskOptions) {
141-
return getPort({ port: commandOptions.liveReloadPort, host: commandOptions.liveReloadHost })
142-
.then((foundPort: number) => {
143-
144-
// if live reload port matches express port, try one higher
145-
if (foundPort === commandOptions.port) {
146-
commandOptions.liveReloadPort = foundPort + 1;
147-
return autoFindLiveReloadPort(commandOptions);
148-
}
149-
150-
// port was already open
151-
if (foundPort === commandOptions.liveReloadPort) {
152-
return commandOptions;
153-
}
154-
155-
// use found port as live reload port
156-
commandOptions.liveReloadPort = foundPort;
157-
return commandOptions;
158-
159-
});
160-
}
161-
162113
export default ServeCommand;

packages/@angular/cli/tasks/serve.ts

+54-35
Original file line numberDiff line numberDiff line change
@@ -41,33 +41,52 @@ export default Task.extend({
4141

4242
let webpackConfig = new NgCliWebpackConfig(serveTaskOptions).config;
4343

44-
// This allows for live reload of page when changes are made to repo.
45-
// https://webpack.github.io/docs/webpack-dev-server.html#inline-mode
46-
let entryPoints = [
47-
`webpack-dev-server/client?http://${serveTaskOptions.host}:${serveTaskOptions.port}/`
48-
];
49-
if (serveTaskOptions.hmr) {
50-
const webpackHmrLink = 'https://webpack.github.io/docs/hot-module-replacement.html';
51-
ui.writeLine(oneLine`
52-
${chalk.yellow('NOTICE')} Hot Module Replacement (HMR) is enabled for the dev server.
53-
`);
54-
ui.writeLine(' The project will still live reload when HMR is enabled,');
55-
ui.writeLine(' but to take advantage of HMR additional application code is required');
56-
ui.writeLine(' (not included in an Angular CLI project by default).');
57-
ui.writeLine(` See ${chalk.blue(webpackHmrLink)}`);
58-
ui.writeLine(' for information on working with HMR for Webpack.');
59-
entryPoints.push('webpack/hot/dev-server');
60-
webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
61-
webpackConfig.plugins.push(new webpack.NamedModulesPlugin());
62-
if (serveTaskOptions.extractCss) {
44+
const serverAddress = url.format({
45+
protocol: serveTaskOptions.ssl ? 'https' : 'http',
46+
hostname: serveTaskOptions.host,
47+
port: serveTaskOptions.port.toString()
48+
});
49+
let clientAddress = serverAddress;
50+
if (serveTaskOptions.liveReloadClient) {
51+
const clientUrl = url.parse(serveTaskOptions.liveReloadClient);
52+
// very basic sanity check
53+
if (!clientUrl.host) {
54+
return Promise.reject(new SilentError(`'live-reload-client' must be a full URL.`));
55+
}
56+
clientAddress = clientUrl.href;
57+
}
58+
59+
if (serveTaskOptions.liveReload) {
60+
// This allows for live reload of page when changes are made to repo.
61+
// https://webpack.github.io/docs/webpack-dev-server.html#inline-mode
62+
let entryPoints = [
63+
`webpack-dev-server/client?${clientAddress}`
64+
];
65+
if (serveTaskOptions.hmr) {
66+
const webpackHmrLink = 'https://webpack.github.io/docs/hot-module-replacement.html';
6367
ui.writeLine(oneLine`
64-
${chalk.yellow('NOTICE')} (HMR) does not allow for CSS hot reload when used
65-
together with '--extract-css'.
68+
${chalk.yellow('NOTICE')} Hot Module Replacement (HMR) is enabled for the dev server.
6669
`);
70+
ui.writeLine(' The project will still live reload when HMR is enabled,');
71+
ui.writeLine(' but to take advantage of HMR additional application code is required');
72+
ui.writeLine(' (not included in an Angular CLI project by default).');
73+
ui.writeLine(` See ${chalk.blue(webpackHmrLink)}`);
74+
ui.writeLine(' for information on working with HMR for Webpack.');
75+
entryPoints.push('webpack/hot/dev-server');
76+
webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
77+
webpackConfig.plugins.push(new webpack.NamedModulesPlugin());
78+
if (serveTaskOptions.extractCss) {
79+
ui.writeLine(oneLine`
80+
${chalk.yellow('NOTICE')} (HMR) does not allow for CSS hot reload when used
81+
together with '--extract-css'.
82+
`);
83+
}
6784
}
85+
if (!webpackConfig.entry.main) { webpackConfig.entry.main = []; }
86+
webpackConfig.entry.main.unshift(...entryPoints);
87+
} else if (serveTaskOptions.hmr) {
88+
ui.writeLine(chalk.yellow('Live reload is disabled. HMR option ignored.'));
6889
}
69-
if (!webpackConfig.entry.main) { webpackConfig.entry.main = []; }
70-
webpackConfig.entry.main.unshift(...entryPoints);
7190

7291
if (!serveTaskOptions.watch) {
7392
// There's no option to turn off file watching in webpack-dev-server, but
@@ -151,26 +170,26 @@ export default Task.extend({
151170

152171
ui.writeLine(chalk.green(oneLine`
153172
**
154-
NG Live Development Server is running on
155-
http${serveTaskOptions.ssl ? 's' : ''}://${serveTaskOptions.host}:${serveTaskOptions.port}.
173+
NG Live Development Server is running on ${serverAddress}
156174
**
157175
`));
158176

159177
const server = new WebpackDevServer(webpackCompiler, webpackDevServerConfiguration);
160178
return new Promise((resolve, reject) => {
161-
server.listen(serveTaskOptions.port, `${serveTaskOptions.host}`, (err: any, stats: any) => {
179+
server.listen(serveTaskOptions.port, serveTaskOptions.host, (err: any, stats: any) => {
162180
if (err) {
163-
console.error(err.stack || err);
164-
if (err.details) { console.error(err.details); }
165-
reject(err.details);
166-
} else {
167-
const { open, ssl, host, port } = serveTaskOptions;
168-
if (open) {
169-
let protocol = ssl ? 'https' : 'http';
170-
opn(url.format({ protocol: protocol, hostname: host, port: port.toString() }));
171-
}
181+
return reject(err);
182+
}
183+
if (serveTaskOptions.open) {
184+
opn(serverAddress);
172185
}
173186
});
187+
})
188+
.catch((err: Error) => {
189+
if (err) {
190+
this.ui.writeError('\nAn error occured during the build:\n' + ((err && err.stack) || err));
191+
}
192+
throw err;
174193
});
175194
}
176195
});

0 commit comments

Comments
 (0)