import * as webpack from "webpack"; import * as UglifyjsWebpackPlugin from "uglifyjs-webpack-plugin"; const compiler = webpack({ plugins: [ new UglifyjsWebpackPlugin(), ], }); const compilerOptions = webpack({ plugins: [ new UglifyjsWebpackPlugin({ cache: false, parallel: true, sourceMap: true, }), ], });
run: function(commandOptions: ServeTaskOptions) { const ui = this.ui; let webpackCompiler: any; let config = new NgCliWebpackConfig( this.project, commandOptions.target, commandOptions.environment, undefined, undefined, commandOptions.aot ).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 config.entry.main.unshift( `webpack-dev-server/client?http://${commandOptions.host}:${commandOptions.port}/` ); webpackCompiler = webpack(config); webpackCompiler.apply(new ProgressPlugin({ profile: true, colors: true })); let proxyConfig = {}; if (commandOptions.proxyConfig) { const proxyPath = path.resolve(this.project.root, commandOptions.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 (commandOptions.ssl) { const keyPath = path.resolve(this.project.root, commandOptions.sslKey); if (fs.existsSync(keyPath)) { sslKey = fs.readFileSync(keyPath, 'utf-8'); } const certPath = path.resolve(this.project.root, commandOptions.sslCert); if (fs.existsSync(certPath)) { sslCert = fs.readFileSync(certPath, 'utf-8'); } } const webpackDevServerConfiguration: IWebpackDevServerConfigurationOptions = { contentBase: path.resolve( this.project.root, `./${CliConfig.fromProject().config.apps[0].root}` ), historyApiFallback: { disableDotRule: true, htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'] }, stats: webpackDevServerOutputOptions, inline: true, proxy: proxyConfig, compress: commandOptions.target === 'production', watchOptions: { poll: CliConfig.fromProject().config.defaults.poll }, https: commandOptions.ssl }; if (sslKey != null && sslCert != null) { webpackDevServerConfiguration.key = sslKey; webpackDevServerConfiguration.cert = sslCert; } ui.writeLine(chalk.green(oneLine` ** NG Live Development Server is running on http${commandOptions.ssl ? 's' : ''}://${commandOptions.host}:${commandOptions.port}. ** `)); const server = new WebpackDevServer(webpackCompiler, webpackDevServerConfiguration); return new Promise((resolve, reject) => { server.listen(commandOptions.port, `${commandOptions.host}`, function(err: any, stats: any) { if (err) { console.error(err.stack || err); if (err.details) { console.error(err.details); } reject(err.details); } else { const { open, host, port } = commandOptions; if (open) { opn(url.format({ protocol: 'http', hostname: host, port: port.toString() })); } } }); }); }
import WebpackDevServer = require('webpack-dev-server'); import * as webpack from 'webpack'; import config from './webpack.config'; import * as path from 'path'; const compiler = webpack(config); const server = new WebpackDevServer(compiler, { hot: true, contentBase: path.resolve(__dirname, '../../public'), proxy: { '*': 'http://localhost:3000' } }); server.listen(8080, 'localhost', function () { console.log(this.address()); });
concatMap(() => new Observable(obs => { const browserBuilder = new BrowserBuilder(this.context); const webpackConfig = browserBuilder.buildWebpackConfig( root, projectRoot, host, browserOptions as NormalizedBrowserBuilderSchema); const statsConfig = getWebpackStatsConfig(browserOptions.verbose); let webpackDevServerConfig: WebpackDevServerConfigurationOptions; try { webpackDevServerConfig = this._buildServerConfig( root, projectRoot, options, browserOptions); } catch (err) { obs.error(err); return; } // Resolve public host and client address. let clientAddress = `${options.ssl ? 'https' : 'http'}://0.0.0.0:0`; if (options.publicHost) { let publicHost = options.publicHost; if (!/^\w+:\/\//.test(publicHost)) { publicHost = `${options.ssl ? 'https' : 'http'}://${publicHost}`; } const clientUrl = url.parse(publicHost); options.publicHost = clientUrl.host; clientAddress = url.format(clientUrl); } // Resolve serve address. const serverAddress = url.format({ protocol: options.ssl ? 'https' : 'http', hostname: options.host === '0.0.0.0' ? 'localhost' : options.host, port: options.port.toString(), }); // Add live reload config. if (options.liveReload) { this._addLiveReload(options, browserOptions, webpackConfig, clientAddress); } else if (options.hmr) { this.context.logger.warn('Live reload is disabled. HMR option ignored.'); } if (!options.watch) { // There's no option to turn off file watching in webpack-dev-server, but // we can override the file watcher instead. webpackConfig.plugins.unshift({ // tslint:disable-next-line:no-any apply: (compiler: any) => { compiler.hooks.afterEnvironment.tap('angular-cli', () => { compiler.watchFileSystem = { watch: () => { } }; }); }, }); } if (browserOptions.optimization) { this.context.logger.error(tags.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! **************************************************************************************** `); } this.context.logger.info(tags.oneLine` ** Angular Live Development Server is listening on ${options.host}: ${options.port}, open your browser on ${serverAddress}${webpackDevServerConfig.publicPath} ** `); const webpackCompiler = webpack(webpackConfig); const server = new WebpackDevServer(webpackCompiler, webpackDevServerConfig); let first = true; // tslint:disable-next-line:no-any (webpackCompiler as any).hooks.done.tap('angular-cli', (stats: webpack.Stats) => { if (!browserOptions.verbose) { const json = stats.toJson(statsConfig); this.context.logger.info(statsToString(json, statsConfig)); if (stats.hasWarnings()) { this.context.logger.info(statsWarningsToString(json, statsConfig)); } if (stats.hasErrors()) { this.context.logger.info(statsErrorsToString(json, statsConfig)); } } obs.next({ success: true }); if (first && options.open) { first = false; opn(serverAddress + webpackDevServerConfig.publicPath); } }); const httpServer = server.listen( options.port, options.host, (err: any) => { // tslint:disable-line:no-any if (err) { obs.error(err); } }, ); // Node 8 has a keepAliveTimeout bug which doesn't respect active connections. // Connections will end after ~5 seconds (arbitrary), often not letting the full download // of large pieces of content, such as a vendor javascript file. This results in browsers // throwing a "net::ERR_CONTENT_LENGTH_MISMATCH" error. // https://github.com/angular/angular-cli/issues/7197 // https://github.com/nodejs/node/issues/13391 // https://github.com/nodejs/node/commit/2cb6f2b281eb96a7abe16d58af6ebc9ce23d2e96 if (/^v8.\d.\d+$/.test(process.version)) { httpServer.keepAliveTimeout = 30000; // 30 seconds } // Teardown logic. Close the server when unsubscribed from. return () => server.close(); })));
run: function (serveTaskOptions: ServeTaskOptions, rebuildDoneCb: any) { const ui = this.ui; let webpackCompiler: any; const projectConfig = CliConfig.fromProject().config; const appConfig = getAppFromConfig(serveTaskOptions.app); const outputPath = serveTaskOptions.outputPath || appConfig.outDir; if (this.project.root === path.resolve(outputPath)) { throw new SilentError('Output path MUST not be project root directory!'); } if (projectConfig.project && projectConfig.project.ejected) { throw new SilentError('An ejected project cannot use the build command anymore.'); } if (appConfig.platform === 'server') { throw new SilentError('ng serve for platform server applications is coming soon!'); } if (serveTaskOptions.deleteOutputPath) { fs.removeSync(path.resolve(this.project.root, outputPath)); } const serveDefaults = { deployUrl: appConfig.deployUrl || '', baseHref: appConfig.baseHref, }; serveTaskOptions = Object.assign({}, serveDefaults, serveTaskOptions); let webpackConfig = new NgCliWebpackConfig(serveTaskOptions, appConfig).buildConfig(); const serverAddress = url.format({ protocol: serveTaskOptions.ssl ? 'https' : 'http', hostname: serveTaskOptions.host === '0.0.0.0' ? 'localhost' : serveTaskOptions.host, port: serveTaskOptions.port.toString() }); if (serveTaskOptions.disableHostCheck) { ui.writeLine(oneLine` ${yellow('WARNING')} Running a server with --disable-host-check is a security risk. See https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a for more information. `); } let clientAddress = `${serveTaskOptions.ssl ? 'https' : 'http'}://0.0.0.0:0`; if (serveTaskOptions.publicHost) { let publicHost = serveTaskOptions.publicHost; if (!/^\w+:\/\//.test(publicHost)) { publicHost = `${serveTaskOptions.ssl ? 'https' : 'http'}://${publicHost}`; } const clientUrl = url.parse(publicHost); serveTaskOptions.publicHost = clientUrl.host; clientAddress = url.format(clientUrl); } if (serveTaskOptions.liveReload) { // This allows for live reload of page when changes are made to repo. // https://webpack.js.org/configuration/dev-server/#devserver-inline let webpackDevServerPath; try { webpackDevServerPath = require.resolve('webpack-dev-server/client'); } catch { throw new SilentError('The "webpack-dev-server" package could not be found.'); } let entryPoints = [ `${webpackDevServerPath}?${clientAddress}` ]; if (serveTaskOptions.hmr) { const webpackHmrLink = 'https://webpack.js.org/guides/hot-module-replacement'; ui.writeLine(oneLine` ${yellow('NOTICE')} Hot Module Replacement (HMR) is enabled for the dev server. `); const showWarning = CliConfig.fromProject().get('warnings.hmrWarning'); if (showWarning) { 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.'); ui.writeLine(oneLine` ${yellow('To disable this warning use "ng set warnings.hmrWarning=false"')} `); } entryPoints.push('webpack/hot/dev-server'); webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin()); if (serveTaskOptions.extractCss) { ui.writeLine(oneLine` ${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(yellow('Live reload is disabled. HMR option ignored.')); } if (!serveTaskOptions.watch) { // There's no option to turn off file watching in webpack-dev-server, but // we can override the file watcher instead. webpackConfig.plugins.unshift({ apply: (compiler: any) => { compiler.plugin('after-environment', () => { compiler.watchFileSystem = { watch: () => { } }; }); } }); } webpackCompiler = webpack(webpackConfig); if (rebuildDoneCb) { webpackCompiler.plugin('done', rebuildDoneCb); } 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'); } } let servePath = serveTaskOptions.servePath; if (!servePath && servePath !== '') { const defaultServePath = findDefaultServePath(serveTaskOptions.baseHref, serveTaskOptions.deployUrl); const showWarning = CliConfig.fromProject().get('warnings.servePathDefault'); if (defaultServePath == null && showWarning) { ui.writeLine(oneLine` ${chalk.yellow('WARNING')} --deploy-url and/or --base-href contain unsupported values for ng serve. Default serve path of '/' used. Use --serve-path to override. `); } servePath = defaultServePath || ''; } if (servePath.endsWith('/')) { servePath = servePath.substr(0, servePath.length - 1); } if (!servePath.startsWith('/')) { servePath = `/${servePath}`; } const webpackDevServerConfiguration: IWebpackDevServerConfigurationOptions = { headers: { 'Access-Control-Allow-Origin': '*' }, historyApiFallback: { index: `${servePath}/${appConfig.index}`, disableDotRule: true, htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'] }, stats: serveTaskOptions.verbose ? statsConfig : 'none', inline: true, proxy: proxyConfig, compress: serveTaskOptions.target === 'production', watchOptions: { poll: serveTaskOptions.poll }, https: serveTaskOptions.ssl, overlay: { errors: serveTaskOptions.target === 'development', warnings: false }, contentBase: false, public: serveTaskOptions.publicHost, disableHostCheck: serveTaskOptions.disableHostCheck, publicPath: servePath }; 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! **************************************************************************************** `)); } ui.writeLine(chalk.green(oneLine` ** NG Live Development Server is listening on ${serveTaskOptions.host}:${serveTaskOptions.port}, open your browser on ${serverAddress}${servePath} ** `)); const server = new WebpackDevServer(webpackCompiler, webpackDevServerConfiguration); if (!serveTaskOptions.verbose) { webpackCompiler.plugin('done', (stats: any) => { const json = stats.toJson(statsConfig); this.ui.writeLine(statsToString(json, statsConfig)); if (stats.hasWarnings()) { this.ui.writeLine(statsWarningsToString(json, statsConfig)); } if (stats.hasErrors()) { this.ui.writeError(statsErrorsToString(json, statsConfig)); } }); } return new Promise((_resolve, reject) => { const httpServer = server.listen( serveTaskOptions.port, serveTaskOptions.host, (err: any, _stats: any) => { if (err) { return reject(err); } if (serveTaskOptions.open) { opn(serverAddress + servePath); } }); // Node 8 has a keepAliveTimeout bug which doesn't respect active connections. // Connections will end after ~5 seconds (arbitrary), often not letting the full download // of large pieces of content, such as a vendor javascript file. This results in browsers // throwing a "net::ERR_CONTENT_LENGTH_MISMATCH" error. // https://github.com/angular/angular-cli/issues/7197 // https://github.com/nodejs/node/issues/13391 // https://github.com/nodejs/node/commit/2cb6f2b281eb96a7abe16d58af6ebc9ce23d2e96 if (/^v8.\d.\d+$/.test(process.version)) { httpServer.keepAliveTimeout = 30000; // 30 seconds } }) .catch((err: Error) => { if (err) { this.ui.writeError('\nAn error occured during the build:\n' + ((err && err.stack) || err)); } throw err; }); }
import * as webpack from 'webpack'; import * as WebpackDevServer from 'webpack-dev-server'; var compiler = webpack({}); // basic example var server = new WebpackDevServer(compiler, { publicPath: "/assets/" }); server.listen(8080); // API example server = new WebpackDevServer(compiler, { // webpack-dev-server options contentBase: "/path/to/directory", // or: contentBase: "http://localhost/", public: 'public-host.ru', // Public host for server disableHostCheck: true, // Disable public host check, use it carefully hot: true, // Enable special support for Hot Module Replacement // Page is no longer updated, but a "webpackHotUpdate" message is send to the content // Use "webpack/hot/dev-server" as additional module in your entry point // Note: this does _not_ add the `HotModuleReplacementPlugin` like the CLI option does. // Set this as true if you want to access dev server from arbitrary url. // This is handy if you are using a html5 router. historyApiFallback: false,