Skip to content

fix(serve): allow relevant live-reload options to function #3952

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions docs/documentation/serve.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,7 @@

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

`--live-reload-host` (`-lrh`) specify the host for live reloading

`--live-reload-base-url` (`-lrbu`) specify the base URL for live reloading

`--live-reload-port` (`-lrp`) port for live reloading

`--live-reload-live-css` flag to live reload CSS
`--live-reload-client` specify the URL that the live reload browser client will use

`--ssl` flag to turn on SSL

Expand Down
83 changes: 8 additions & 75 deletions packages/@angular/cli/commands/serve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,7 @@ import { ServeTaskOptions } from './serve';
import { checkPort } from '../utilities/check-port';
import { overrideOptions } from '../utilities/override-options';

const SilentError = require('silent-error');
const PortFinder = require('portfinder');
const Command = require('../ember-cli/lib/models/command');
const getPort = <any>denodeify(PortFinder.getPort);

PortFinder.basePort = 49152;

const config = CliConfig.fromProject() || CliConfig.fromGlobal();
const defaultPort = process.env.PORT || config.get('defaults.serve.port');
Expand All @@ -23,10 +18,7 @@ export interface ServeTaskOptions extends BuildOptions {
host?: string;
proxyConfig?: string;
liveReload?: boolean;
liveReloadHost?: string;
liveReloadPort?: number;
liveReloadBaseUrl?: string;
liveReloadLiveCss?: boolean;
liveReloadClient?: string;
ssl?: boolean;
sslKey?: string;
sslCert?: string;
Expand Down Expand Up @@ -66,28 +58,9 @@ const ServeCommand = Command.extend({
baseServeCommandOptions.concat([
{ name: 'live-reload', type: Boolean, default: true, aliases: ['lr'] },
{
name: 'live-reload-host',
type: String,
aliases: ['lrh'],
description: 'Defaults to host'
},
{
name: 'live-reload-base-url',
name: 'live-reload-client',
type: String,
aliases: ['lrbu'],
description: 'Defaults to baseURL'
},
{
name: 'live-reload-port',
type: Number,
aliases: ['lrp'],
description: '(Defaults to port number within [49152...65535])'
},
{
name: 'live-reload-live-css',
type: Boolean,
default: true,
description: 'Whether to live reload CSS (default true)'
description: 'specify the URL that the live reload browser client will use'
},
{
name: 'hmr',
Expand All @@ -104,59 +77,19 @@ const ServeCommand = Command.extend({
const ServeTask = require('../tasks/serve').default;

Version.assertAngularVersionIs2_3_1OrHigher(this.project.root);
commandOptions.liveReloadHost = commandOptions.liveReloadHost || commandOptions.host;

return checkPort(commandOptions.port, commandOptions.host)
.then((port: number) => commandOptions.port = port)
.then(() => autoFindLiveReloadPort(commandOptions))
.then((opts: ServeTaskOptions) => {
return checkPort(commandOptions.port, commandOptions.host, defaultPort)
.then(port => {
commandOptions.port = port;

const serve = new ServeTask({
ui: this.ui,
project: this.project,
});

return serve.run(opts);
return serve.run(commandOptions);
});
}
});

function checkExpressPort(commandOptions: ServeTaskOptions) {
return getPort({ port: commandOptions.port, host: commandOptions.host })
.then((foundPort: number) => {

if (commandOptions.port !== foundPort && commandOptions.port !== 0) {
throw new SilentError(
`Port ${commandOptions.port} is already in use. Use '--port' to specify a different port.`
);
}

// otherwise, our found port is good
commandOptions.port = foundPort;
return commandOptions;

});
}

function autoFindLiveReloadPort(commandOptions: ServeTaskOptions) {
return getPort({ port: commandOptions.liveReloadPort, host: commandOptions.liveReloadHost })
.then((foundPort: number) => {

// if live reload port matches express port, try one higher
if (foundPort === commandOptions.port) {
commandOptions.liveReloadPort = foundPort + 1;
return autoFindLiveReloadPort(commandOptions);
}

// port was already open
if (foundPort === commandOptions.liveReloadPort) {
return commandOptions;
}

// use found port as live reload port
commandOptions.liveReloadPort = foundPort;
return commandOptions;

});
}

export default ServeCommand;
89 changes: 54 additions & 35 deletions packages/@angular/cli/tasks/serve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,33 +41,52 @@ export default Task.extend({

let webpackConfig = new NgCliWebpackConfig(serveTaskOptions).config;

// This allows for live reload of page when changes are made to repo.
// https://webpack.github.io/docs/webpack-dev-server.html#inline-mode
let entryPoints = [
`webpack-dev-server/client?http://${serveTaskOptions.host}:${serveTaskOptions.port}/`
];
if (serveTaskOptions.hmr) {
const webpackHmrLink = 'https://webpack.github.io/docs/hot-module-replacement.html';
ui.writeLine(oneLine`
${chalk.yellow('NOTICE')} Hot Module Replacement (HMR) is enabled for the dev server.
`);
ui.writeLine(' The project will still live reload when HMR is enabled,');
ui.writeLine(' but to take advantage of HMR additional application code is required');
ui.writeLine(' (not included in an Angular CLI project by default).');
ui.writeLine(` See ${chalk.blue(webpackHmrLink)}`);
ui.writeLine(' for information on working with HMR for Webpack.');
entryPoints.push('webpack/hot/dev-server');
webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
webpackConfig.plugins.push(new webpack.NamedModulesPlugin());
if (serveTaskOptions.extractCss) {
const serverAddress = url.format({
protocol: serveTaskOptions.ssl ? 'https' : 'http',
hostname: serveTaskOptions.host,
port: serveTaskOptions.port.toString()
});
let clientAddress = serverAddress;
if (serveTaskOptions.liveReloadClient) {
const clientUrl = url.parse(serveTaskOptions.liveReloadClient);
// very basic sanity check
if (!clientUrl.host) {
return Promise.reject(new SilentError(`'live-reload-client' must be a full URL.`));
}
clientAddress = clientUrl.href;
}

if (serveTaskOptions.liveReload) {
// This allows for live reload of page when changes are made to repo.
// https://webpack.github.io/docs/webpack-dev-server.html#inline-mode
let entryPoints = [
`webpack-dev-server/client?${clientAddress}`
];
if (serveTaskOptions.hmr) {
const webpackHmrLink = 'https://webpack.github.io/docs/hot-module-replacement.html';
ui.writeLine(oneLine`
${chalk.yellow('NOTICE')} (HMR) does not allow for CSS hot reload when used
together with '--extract-css'.
${chalk.yellow('NOTICE')} Hot Module Replacement (HMR) is enabled for the dev server.
`);
ui.writeLine(' The project will still live reload when HMR is enabled,');
ui.writeLine(' but to take advantage of HMR additional application code is required');
ui.writeLine(' (not included in an Angular CLI project by default).');
ui.writeLine(` See ${chalk.blue(webpackHmrLink)}`);
ui.writeLine(' for information on working with HMR for Webpack.');
entryPoints.push('webpack/hot/dev-server');
webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
webpackConfig.plugins.push(new webpack.NamedModulesPlugin());
if (serveTaskOptions.extractCss) {
ui.writeLine(oneLine`
${chalk.yellow('NOTICE')} (HMR) does not allow for CSS hot reload when used
together with '--extract-css'.
`);
}
}
if (!webpackConfig.entry.main) { webpackConfig.entry.main = []; }
webpackConfig.entry.main.unshift(...entryPoints);
} else if (serveTaskOptions.hmr) {
ui.writeLine(chalk.yellow('Live reload is disabled. HMR option ignored.'));
}
if (!webpackConfig.entry.main) { webpackConfig.entry.main = []; }
webpackConfig.entry.main.unshift(...entryPoints);

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

ui.writeLine(chalk.green(oneLine`
**
NG Live Development Server is running on
http${serveTaskOptions.ssl ? 's' : ''}://${serveTaskOptions.host}:${serveTaskOptions.port}.
NG Live Development Server is running on ${serverAddress}
**
`));

const server = new WebpackDevServer(webpackCompiler, webpackDevServerConfiguration);
return new Promise((resolve, reject) => {
server.listen(serveTaskOptions.port, `${serveTaskOptions.host}`, (err: any, stats: any) => {
server.listen(serveTaskOptions.port, serveTaskOptions.host, (err: any, stats: any) => {
if (err) {
console.error(err.stack || err);
if (err.details) { console.error(err.details); }
reject(err.details);
} else {
const { open, ssl, host, port } = serveTaskOptions;
if (open) {
let protocol = ssl ? 'https' : 'http';
opn(url.format({ protocol: protocol, hostname: host, port: port.toString() }));
}
return reject(err);
}
if (serveTaskOptions.open) {
opn(serverAddress);
}
});
})
.catch((err: Error) => {
if (err) {
this.ui.writeError('\nAn error occured during the build:\n' + ((err && err.stack) || err));
}
throw err;
});
}
});
10 changes: 4 additions & 6 deletions packages/@angular/cli/utilities/check-port.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ import * as denodeify from 'denodeify';

const SilentError = require('silent-error');
const PortFinder = require('portfinder');
const getPort = <any>denodeify(PortFinder.getPort);
const getPort = denodeify<{host: string, port: number}, number>(PortFinder.getPort);

PortFinder.basePort = 49152;


export function checkPort(port: number, host: string) {
export function checkPort(port: number, host: string, basePort = 49152): Promise<number> {
PortFinder.basePort = basePort;
return getPort({ port, host })
.then((foundPort: number) => {
.then(foundPort => {

// If the port isn't available and we weren't looking for any port, throw error.
if (port !== foundPort && port !== 0) {
Expand Down