UNPKG

ng6-cli

Version:

Tooling to build applications with Angular 1.5, ES6, and Webpack.

289 lines (224 loc) 10 kB
var fs = require('fs'); var path = require('path'); var chalk = require('chalk'); var detect = require('detect-port'); var Ora = require('ora'); var _ = require('lodash'); var Command = require('../../lib/command'); var clearConsole = require('react-dev-utils/clearConsole'); var formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); var openBrowser = require('react-dev-utils/openBrowser'); process.env.NODE_ENV = 'development'; var DEFAULT_PORT = process.env.PORT || 3000; module.exports = Command.extend({ init: function() { this._super.apply(this, arguments); this.description = 'Watch, build, & serve the application in a local environment.'; this.options = ''; this.category = 'build'; this.order = 1; }, checkProject: function() { //resolve project root var projectRoot = this.cli.reflect.projectRoot(); if( !projectRoot || projectRoot != process.cwd() ) { console.log(); console.log(chalk.white('You must be in the project root in order to execute serve!')); console.log(); process.exit(1); } var nodeModulesExists = fs.existsSync(path.join(projectRoot, 'node_modules')); if( !nodeModulesExists ) { console.log(); console.log(chalk.white('All dependencies seem to be missing. Have you run ' + chalk.cyan('npm install') + ' or ' + chalk.cyan('yarn') + '?')); console.log(); process.exit(1); } var nodeModules = fs.readdirSync(path.join(projectRoot, 'node_modules')); var pkg = require(path.join(projectRoot, 'package.json')); if( nodeModules.length < (pkg.dependencies.length + pkg.devDependencies.length) ) { console.log(); console.log(chalk.white('Some dependencies seem to be missing. Have you run ' + chalk.cyan('npm install') + ' or ' + chalk.cyan('yarn') + '?')); console.log(); process.exit(1); } var webpackExists = fs.existsSync(path.join(projectRoot, 'node_modules', 'webpack')); var webpackDevServerExists = fs.existsSync(path.join(projectRoot, 'node_modules', 'webpack-dev-server')); if( !webpackExists || !webpackDevServerExists ) { console.log(); console.log(chalk.white('webpack or webpack-dev-server seem to be missing. Have you run ' + chalk.cyan('npm install') + ' or ' + chalk.cyan('yarn') + '?')); console.log(); process.exit(1); } }, start: function(webpackConfig, port) { var host = process.env.HOST || 'localhost'; this.setupCompiler(webpackConfig, host, port); this.runDevServer(webpackConfig, host, port); }, setupCompiler: function(webpackConfig, host, port) { var url = 'http://' + host + ':' + port + '/'; var self = this; var projectRoot = this.cli.reflect.projectRoot(); var webpack = require(path.resolve(projectRoot, 'node_modules', 'webpack')); // 'Compiler' is a low-level interface to Webpack. // It lets us listen to some events and provide our own custom messages. this.compiler = webpack(webpackConfig); // 'invalid' event fires when you have changed a file, and Webpack is // recompiling a bundle. WebpackDevServer takes care to pause serving the // bundle, so if you refresh, it'll wait instead of serving the old one. // 'invalid' is short for 'bundle invalidated', it doesn't imply any errors. this.compiler.plugin('invalid', function() { self.spinner.stop(); clearConsole(); self.spinner.start(); self.spinner.text = chalk.cyan('Compiling...'); }); var firstRun = true; // 'done' event fires when Webpack has finished recompiling the bundle. // Whether or not you have warnings or errors, you will get this event. this.compiler.plugin('done', function(stats) { self.spinner.stop(); clearConsole(); // We have switched off the default Webpack output in WebpackDevServer // options so we are going to 'massage' the warnings and errors and present // them in a readable focused way. var messages = formatWebpackMessages(stats.toJson({}, true)); if (!messages.errors.length && !messages.warnings.length) { console.log(chalk.green('Compiled successfully!')); console.log(); console.log('Serving application at ' + chalk.green(url)); console.log(); if (firstRun) { firstRun = false; console.log(chalk.white('Using webpack config found at ' + chalk.green(self.webpackConfigPath))); console.log(); console.log('Note that the development build is not optimized.'); console.log('To create a production build, use ' + chalk.cyan('ng6 build') + ' or ' + chalk.cyan('npm run build') + '.'); console.log(); if(!openBrowser(url)) { console.log('Failed to open the browser tab. Please open the browser to' + chalk.yellow(url)); } } } // If errors exist, only show errors. if (messages.errors.length) { console.log(chalk.red('Failed to compile.')); console.log(); messages.errors.forEach(function(message) { console.log(message); }); return; } // Show warnings if no errors were found. if (messages.warnings.length) { console.log(chalk.yellow('Compiled with warnings.')); console.log(); messages.warnings.forEach(function(message) { console.log(message); }); // Teach some ESLint tricks. console.log('You may use special comments to disable some warnings.'); console.log('Use ' + chalk.yellow('// eslint-disable-next-line') + ' to ignore the next line.'); console.log('Use ' + chalk.yellow('/* eslint-disable */') + ' to ignore all warnings in a file.'); } }); }, runDevServer: function(webpackConfig, host, port) { var self = this; var WebpackDevServer = require(path.resolve(this.cli.reflect.projectRoot(), 'node_modules', 'webpack-dev-server')); var devServer = new WebpackDevServer(this.compiler, { // Enable gzip compression of generated files. compress: true, // Silence WebpackDevServer's own logs since they're generally not useful. // It will still show compile warnings and errors with this setting. clientLogLevel: 'none', // It is important to tell WebpackDevServer to use the same 'root' path // as we specified in the config. In development, we always serve from /. publicPath: webpackConfig.output.publicPath, // WebpackDevServer is noisy by default so we emit custom message instead // by listening to the compiler events with `compiler.plugin` calls above. quiet: true, // Reportedly, this avoids CPU overload on some systems. // https://github.com/facebookincubator/create-react-app/issues/293 watchOptions: { ignored: /node_modules/ }, host: host, historyApiFallback: true, }); // Launch WebpackDevServer. devServer.listen(port, function(err) { if (err) { return console.log(err); } clearConsole(); console.log(); self.spinner.start(); self.spinner.text = chalk.cyan('Starting the development server...'); }); }, getWebpackConfig: function(projectRoot) { var webpackConfigPath = ''; var webpackDev = path.resolve(projectRoot + '/webpack.dev.config.js'); var webpackDevBabel = path.resolve(projectRoot + '/webpack.dev.config.babel.js'); var webpackRoot = path.resolve(projectRoot + '/webpack.config.js'); var webpackRootBabel = path.resolve(projectRoot + '/webpack.config.babel.js'); if( fs.existsSync(webpackDev) ) { webpackConfigPath = webpackDev; } else if( fs.existsSync(webpackDevBabel) ) { webpackConfigPath = webpackDevBabel; } else if( fs.existsSync(webpackRoot) ) { webpackConfigPath = webpackRoot; } else if( fs.existsSync(webpackRootBabel) ) { webpackConfigPath = webpackRootBabel; } if( this.cli.isEnabled('config') ) { webpackConfigPath = path.resolve(projectRoot, this.cli.request.getOption('config')); } if(!webpackConfigPath) { console.log(); if( this.cli.isEnabled('config') ) { console.log(chalk.white('You specified a webpack config file at ' + chalk.yellow(webpackConfigPath) + ' but I was unable to find one ther.')); } else { console.log(chalk.white('Cannot find webpack.config looked at ' + '\n\t' + chalk.yellow(webpackDev) + '\n\t' + chalk.yellow(webpackDevBabel) + '\n\t' + chalk.yellow(webpackRoot) + '\n\t' + chalk.yellow(webpackRoot) )); } console.log(); process.exit(1); } this.webpackConfigPath = webpackConfigPath; var webpackConfig = require(webpackConfigPath); if( _.isFunction(webpackConfig) ) { return webpackConfig({ dev: true }); // --env.dev } return webpackConfig; }, run: function() { var self = this; this.spinner = Ora().start(); // This initializes the spinner and attaches it to this so that other functions can use it. this.spinner.stop(); // It is immediately stopped because it will be started in an async callback. this.checkProject(); if( this.cli.isEnabled('p') ) { DEFAULT_PORT = this.cli.request.getOption('p'); } // --port is prioritized higher than -p if( this.cli.isEnabled('port') ) { DEFAULT_PORT = this.cli.request.getOption('port'); } // We attempt to use the default port but if it is busy, we offer the user to // run on a different port. `detect()` Promise resolves to the next free port. detect(DEFAULT_PORT).then(function(port) { process.env.PORT = port; // need to require the webpack file after setting process.env.PORT var webpackConfig = self.getWebpackConfig(self.cli.reflect.projectRoot()); self.start(webpackConfig, port); }).catch(function(err) { console.log(err); }); } });