1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const fs = require("fs");
|
4 | const path = require("path");
|
5 | const chalk = require("chalk");
|
6 | const rimraf = require("rimraf");
|
7 | const webpack = require("webpack");
|
8 | const url = require("url");
|
9 | const common_tags_1 = require("common-tags");
|
10 | const utils_1 = require("../models/webpack-configs/utils");
|
11 | const webpack_config_1 = require("../models/webpack-config");
|
12 | const config_1 = require("../models/config");
|
13 | const app_utils_1 = require("../utilities/app-utils");
|
14 | const WebpackDevServer = require('webpack-dev-server');
|
15 | const Task = require('../ember-cli/lib/models/task');
|
16 | const SilentError = require('silent-error');
|
17 | const opn = require('opn');
|
18 | exports.default = Task.extend({
|
19 | run: function (serveTaskOptions, rebuildDoneCb) {
|
20 | const ui = this.ui;
|
21 | let webpackCompiler;
|
22 | const projectConfig = config_1.CliConfig.fromProject().config;
|
23 | const appConfig = app_utils_1.getAppFromConfig(projectConfig.apps, serveTaskOptions.app);
|
24 | const outputPath = serveTaskOptions.outputPath || appConfig.outDir;
|
25 | if (this.project.root === outputPath) {
|
26 | throw new SilentError('Output path MUST not be project root directory!');
|
27 | }
|
28 | if (projectConfig.project && projectConfig.project.ejected) {
|
29 | throw new SilentError('An ejected project cannot use the build command anymore.');
|
30 | }
|
31 | rimraf.sync(path.resolve(this.project.root, outputPath));
|
32 | const serveDefaults = {
|
33 |
|
34 | deployUrl: ''
|
35 | };
|
36 | serveTaskOptions = Object.assign({}, serveDefaults, serveTaskOptions);
|
37 | let webpackConfig = new webpack_config_1.NgCliWebpackConfig(serveTaskOptions, appConfig).buildConfig();
|
38 | const serverAddress = url.format({
|
39 | protocol: serveTaskOptions.ssl ? 'https' : 'http',
|
40 | hostname: serveTaskOptions.host,
|
41 | port: serveTaskOptions.port.toString()
|
42 | });
|
43 | let clientAddress = serverAddress;
|
44 | if (serveTaskOptions.liveReloadClient) {
|
45 | const clientUrl = url.parse(serveTaskOptions.liveReloadClient);
|
46 |
|
47 | if (!clientUrl.host) {
|
48 | return Promise.reject(new SilentError(`'live-reload-client' must be a full URL.`));
|
49 | }
|
50 | clientAddress = clientUrl.href;
|
51 | }
|
52 | if (serveTaskOptions.liveReload) {
|
53 |
|
54 |
|
55 | let entryPoints = [
|
56 | `webpack-dev-server/client?${clientAddress}`
|
57 | ];
|
58 | if (serveTaskOptions.hmr) {
|
59 | const webpackHmrLink = 'https://webpack.github.io/docs/hot-module-replacement.html';
|
60 | ui.writeLine(common_tags_1.oneLine `
|
61 | ${chalk.yellow('NOTICE')} Hot Module Replacement (HMR) is enabled for the dev server.
|
62 | `);
|
63 | ui.writeLine(' The project will still live reload when HMR is enabled,');
|
64 | ui.writeLine(' but to take advantage of HMR additional application code is required');
|
65 | ui.writeLine(' (not included in an Angular CLI project by default).');
|
66 | ui.writeLine(` See ${chalk.blue(webpackHmrLink)}`);
|
67 | ui.writeLine(' for information on working with HMR for Webpack.');
|
68 | entryPoints.push('webpack/hot/dev-server');
|
69 | webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
|
70 | webpackConfig.plugins.push(new webpack.NamedModulesPlugin());
|
71 | if (serveTaskOptions.extractCss) {
|
72 | ui.writeLine(common_tags_1.oneLine `
|
73 | ${chalk.yellow('NOTICE')} (HMR) does not allow for CSS hot reload when used
|
74 | together with '--extract-css'.
|
75 | `);
|
76 | }
|
77 | }
|
78 | if (!webpackConfig.entry.main) {
|
79 | webpackConfig.entry.main = [];
|
80 | }
|
81 | webpackConfig.entry.main.unshift(...entryPoints);
|
82 | }
|
83 | else if (serveTaskOptions.hmr) {
|
84 | ui.writeLine(chalk.yellow('Live reload is disabled. HMR option ignored.'));
|
85 | }
|
86 | if (!serveTaskOptions.watch) {
|
87 |
|
88 |
|
89 | webpackConfig.plugins.unshift({
|
90 | apply: (compiler) => {
|
91 | compiler.plugin('after-environment', () => {
|
92 | compiler.watchFileSystem = { watch: () => { } };
|
93 | });
|
94 | }
|
95 | });
|
96 | }
|
97 | webpackCompiler = webpack(webpackConfig);
|
98 | if (rebuildDoneCb) {
|
99 | webpackCompiler.plugin('done', rebuildDoneCb);
|
100 | }
|
101 | const statsConfig = utils_1.getWebpackStatsConfig(serveTaskOptions.verbose);
|
102 | let proxyConfig = {};
|
103 | if (serveTaskOptions.proxyConfig) {
|
104 | const proxyPath = path.resolve(this.project.root, serveTaskOptions.proxyConfig);
|
105 | if (fs.existsSync(proxyPath)) {
|
106 | proxyConfig = require(proxyPath);
|
107 | }
|
108 | else {
|
109 | const message = 'Proxy config file ' + proxyPath + ' does not exist.';
|
110 | return Promise.reject(new SilentError(message));
|
111 | }
|
112 | }
|
113 | let sslKey = null;
|
114 | let sslCert = null;
|
115 | if (serveTaskOptions.ssl) {
|
116 | const keyPath = path.resolve(this.project.root, serveTaskOptions.sslKey);
|
117 | if (fs.existsSync(keyPath)) {
|
118 | sslKey = fs.readFileSync(keyPath, 'utf-8');
|
119 | }
|
120 | const certPath = path.resolve(this.project.root, serveTaskOptions.sslCert);
|
121 | if (fs.existsSync(certPath)) {
|
122 | sslCert = fs.readFileSync(certPath, 'utf-8');
|
123 | }
|
124 | }
|
125 | const webpackDevServerConfiguration = {
|
126 | headers: { 'Access-Control-Allow-Origin': '*' },
|
127 | historyApiFallback: {
|
128 | index: `/${appConfig.index}`,
|
129 | disableDotRule: true,
|
130 | htmlAcceptHeaders: ['text/html', 'application/xhtml+xml']
|
131 | },
|
132 | stats: statsConfig,
|
133 | inline: true,
|
134 | proxy: proxyConfig,
|
135 | compress: serveTaskOptions.target === 'production',
|
136 | watchOptions: {
|
137 | poll: serveTaskOptions.poll
|
138 | },
|
139 | https: serveTaskOptions.ssl,
|
140 | overlay: serveTaskOptions.target === 'development'
|
141 | };
|
142 | if (sslKey != null && sslCert != null) {
|
143 | webpackDevServerConfiguration.key = sslKey;
|
144 | webpackDevServerConfiguration.cert = sslCert;
|
145 | }
|
146 | webpackDevServerConfiguration.hot = serveTaskOptions.hmr;
|
147 | if (serveTaskOptions.target === 'production') {
|
148 | ui.writeLine(chalk.red(common_tags_1.stripIndents `
|
149 | ****************************************************************************************
|
150 | This is a simple server for use in testing or debugging Angular applications locally.
|
151 | It hasn't been reviewed for security issues.
|
152 |
|
153 | DON'T USE IT FOR PRODUCTION USE!
|
154 | ****************************************************************************************
|
155 | `));
|
156 | }
|
157 | ui.writeLine(chalk.green(common_tags_1.oneLine `
|
158 | **
|
159 | NG Live Development Server is running on ${serverAddress}
|
160 | **
|
161 | `));
|
162 | const server = new WebpackDevServer(webpackCompiler, webpackDevServerConfiguration);
|
163 | return new Promise((_resolve, reject) => {
|
164 | server.listen(serveTaskOptions.port, serveTaskOptions.host, (err, _stats) => {
|
165 | if (err) {
|
166 | return reject(err);
|
167 | }
|
168 | if (serveTaskOptions.open) {
|
169 | opn(serverAddress);
|
170 | }
|
171 | });
|
172 | })
|
173 | .catch((err) => {
|
174 | if (err) {
|
175 | this.ui.writeError('\nAn error occured during the build:\n' + ((err && err.stack) || err));
|
176 | }
|
177 | throw err;
|
178 | });
|
179 | }
|
180 | });
|
181 |
|
\ | No newline at end of file |