157 lines
6.0 KiB
TypeScript

import * as fs from 'fs';
import * as path from 'path';
import * as chalk from 'chalk';
import * as rimraf from 'rimraf';
import * as webpack from 'webpack';
import * as url from 'url';
import { oneLine, stripIndents } from 'common-tags';
import { getWebpackStatsConfig } from '../models/webpack-configs/utils';
import { NgCliWebpackConfig } from '../models/webpack-config';
import { ServeTaskOptions } from '../commands/serve';
import { CliConfig } from '../models/config';
const WebpackDevServer = require('webpack-dev-server');
const Task = require('../ember-cli/lib/models/task');
const SilentError = require('silent-error');
const opn = require('opn');
export default Task.extend({
run: function (serveTaskOptions: ServeTaskOptions) {
const ui = this.ui;
let webpackCompiler: any;
const projectConfig = CliConfig.fromProject().config;
const appConfig = projectConfig.apps[0];
const outputPath = serveTaskOptions.outputPath || appConfig.outDir;
if (this.project.root === outputPath) {
throw new SilentError ('Output path MUST not be project root directory!');
}
rimraf.sync(path.resolve(this.project.root, outputPath));
const serveDefaults = {
// default deployUrl to '' on serve to prevent the default from angular-cli.json
deployUrl: ''
};
serveTaskOptions = Object.assign({}, serveDefaults, serveTaskOptions);
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) {
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);
webpackCompiler = webpack(webpackConfig);
const statsConfig = getWebpackStatsConfig(serveTaskOptions.verbose);
let proxyConfig = {};
if (serveTaskOptions.proxyConfig) {
const proxyPath = path.resolve(this.project.root, serveTaskOptions.proxyConfig);
if (fs.existsSync(proxyPath)) {
proxyConfig = require(proxyPath);
} else {
const message = 'Proxy config file ' + proxyPath + ' does not exist.';
return Promise.reject(new SilentError(message));
}
}
let sslKey: string = null;
let sslCert: string = null;
if (serveTaskOptions.ssl) {
const keyPath = path.resolve(this.project.root, serveTaskOptions.sslKey);
if (fs.existsSync(keyPath)) {
sslKey = fs.readFileSync(keyPath, 'utf-8');
}
const certPath = path.resolve(this.project.root, serveTaskOptions.sslCert);
if (fs.existsSync(certPath)) {
sslCert = fs.readFileSync(certPath, 'utf-8');
}
}
const webpackDevServerConfiguration: IWebpackDevServerConfigurationOptions = {
contentBase: path.join(this.project.root, `./${appConfig.root}`),
headers: { 'Access-Control-Allow-Origin': '*' },
historyApiFallback: {
index: `/${appConfig.index}`,
disableDotRule: true,
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml']
},
stats: statsConfig,
inline: true,
proxy: proxyConfig,
compress: serveTaskOptions.target === 'production',
watchOptions: {
poll: projectConfig.defaults && projectConfig.defaults.poll
},
https: serveTaskOptions.ssl
};
if (sslKey != null && sslCert != null) {
webpackDevServerConfiguration.key = sslKey;
webpackDevServerConfiguration.cert = sslCert;
}
webpackDevServerConfiguration.hot = serveTaskOptions.hmr;
if (serveTaskOptions.target === 'production') {
ui.writeLine(chalk.red(stripIndents`
****************************************************************************************
This is a simple server for use in testing or debugging Angular applications locally.
It hasn't been reviewed for security issues.
DON'T USE IT FOR PRODUCTION USE!
****************************************************************************************
`));
}
ui.writeLine(chalk.green(oneLine`
**
NG Live Development Server is running on
http${serveTaskOptions.ssl ? 's' : ''}://${serveTaskOptions.host}:${serveTaskOptions.port}.
**
`));
const server = new WebpackDevServer(webpackCompiler, webpackDevServerConfiguration);
return new Promise((resolve, reject) => {
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() }));
}
}
});
});
}
});