diff --git a/flow-typed/npm-custom/resolve.js b/flow-typed/npm-custom/resolve.js new file mode 100644 index 0000000..743ee3b --- /dev/null +++ b/flow-typed/npm-custom/resolve.js @@ -0,0 +1,3 @@ +declare module 'resolve' { + declare module.exports: any +} diff --git a/lib/utils/messages.js b/lib/utils/messages.js index 164e6ff..fd87e72 100644 --- a/lib/utils/messages.js +++ b/lib/utils/messages.js @@ -17,6 +17,8 @@ exports.devServerCompiledSuccessfullyMsg = devServerCompiledSuccessfullyMsg; exports.devServerFailedToCompileMsg = devServerFailedToCompileMsg; exports.devServerCompiledWithWarningsMsg = devServerCompiledWithWarningsMsg; exports.devServerFileDoesNotExistMsg = devServerFileDoesNotExistMsg; +exports.devServerRestartMsg = devServerRestartMsg; +exports.devServerModuleDoesntExists = devServerModuleDoesntExists; exports.builderBanner = builderBanner; exports.builderRemovingDistMsg = builderRemovingDistMsg; exports.builderRunningBuildMsg = builderRunningBuildMsg; @@ -148,6 +150,16 @@ function devServerFileDoesNotExistMsg(filename) { return print([warningBadge() + ` File "${_chalk2.default.yellow(filename)}" doesn\'t exist.`, '']); } +function devServerRestartMsg(module) { + clearConsole(true); + return print([warningBadge() + ' ' + _chalk2.default.yellow(`New npm module was added (${module}).`), '', 'Restarting webpack-dev-server is requried.', '', 'Please be patient and wait until restart completes, otherwise some changes might not be tracked.', '']); +} + +function devServerModuleDoesntExists(module, filename) { + clearConsole(true); + return print([errorBadge() + ' ' + _chalk2.default.red(`Module '${module}' doesn't exists.`), '', `Error in ${filename}`, '', `Webpack tried to resolve module ${_chalk2.default.bgYellow.black(' ' + module + ' ')} which doesn't exist.`, '', `It's likely caused by ${_chalk2.default.yellow('typo')} in the module name.`, '']); +} + /** * * Build Messages diff --git a/lib/webpack-dev-server.js b/lib/webpack-dev-server.js index 8a6ee17..3727065 100644 --- a/lib/webpack-dev-server.js +++ b/lib/webpack-dev-server.js @@ -15,6 +15,10 @@ var _connectHistoryApiFallback = require('connect-history-api-fallback'); var _connectHistoryApiFallback2 = _interopRequireDefault(_connectHistoryApiFallback); +var _resolve = require('resolve'); + +var _resolve2 = _interopRequireDefault(_resolve); + var _webpack = require('webpack'); var _webpack2 = _interopRequireDefault(_webpack); @@ -40,7 +44,8 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de /** * On done handler for webpack compiler. */ -function onDone(filename, flags, params, stats) { +function onDone(filename, flags, params, compiler, invalidate, stats) { + // eslint-disable-line const hasErrors = stats.hasErrors(); const hasWarnings = stats.hasWarnings(); const buildDuration = stats.endTime - stats.startTime; @@ -58,6 +63,12 @@ function onDone(filename, flags, params, stats) { let formattedErrors = json.errors.map(message => 'Error in ' + (0, _errorHelpers.formatMessage)(message)); if (hasErrors) { + if (formattedErrors.filter(err => err.match('Cannot resolve module')).length) { + invalidate(formattedErrors); + (0, _testUtils2.default)(); + return; + } + (0, _messages.devServerFailedToCompileMsg)(); // If there are any syntax errors, show just them. @@ -85,10 +96,13 @@ function onDone(filename, flags, params, stats) { /** * Creates webpack compiler. */ -function createWebpackCompiler(filename, flags, params, config) { + + +function createWebpackCompiler(filename, flags, params, config, invalidate) { + // eslint-disable-line const compiler = (0, _webpack2.default)(config); compiler.plugin('invalid', _messages.devServerInvalidBuildMsg); - compiler.plugin('done', onDone.bind(null, filename, flags, params)); + compiler.plugin('done', onDone.bind(null, filename, flags, params, compiler, invalidate)); return compiler; } @@ -102,9 +116,32 @@ function createWebpackDevServer(filename, flags, params) { flags.port = port; } + let server; // eslint-disable-line + const invalidate = errors => { + if (!server) return; + + const error = errors[0] || ''; + const fileWithError = (error.match(/Error in (.+)\n/) || [])[1]; + let moduleName = (error.match(/Module not found: Error: Cannot resolve module '(.+)'/) || [])[1]; + + if (!moduleName) return; + + moduleName = moduleName.replace(/'/gmi, ''); + + try { + _resolve2.default.sync(moduleName, { basedir: process.cwd() }); + (0, _messages.devServerRestartMsg)(moduleName); + server.close(); + createWebpackDevServer(filename, flags, params); + } catch (e) { + // eslint-disable-line + (0, _messages.devServerModuleDoesntExists)(moduleName, fileWithError); + } + }; const config = (0, _configBuilder2.default)(filename, flags, params); - const compiler = createWebpackCompiler(filename, flags, params, config); - const server = new _webpackDevServer2.default(compiler, { + const compiler = createWebpackCompiler(filename, flags, params, config, invalidate); + + server = new _webpackDevServer2.default(compiler, { // Enable gzip compression of generated files. compress: true, diff --git a/package.json b/package.json index 06b5442..e6b2ce2 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "postcss-partial-import": "3.1.0", "precss": "1.4.0", "react-hot-loader": "1.3.1", + "resolve": "^1.2.0", "style-loader": "0.13.1", "webpack": "1.14.0", "webpack-dev-server": "1.16.3" diff --git a/src/utils/messages.js b/src/utils/messages.js index 1ed146c..10c66cf 100644 --- a/src/utils/messages.js +++ b/src/utils/messages.js @@ -134,6 +134,32 @@ export function devServerFileDoesNotExistMsg(filename: string) { ]); } +export function devServerRestartMsg(module: string) { + clearConsole(true); + return print([ + warningBadge() + ' ' + chalk.yellow(`New npm module was added (${module}).`), + '', + 'Restarting webpack-dev-server is requried.', + '', + 'Please be patient and wait until restart completes, otherwise some changes might not be tracked.', + '' + ]); +} + +export function devServerModuleDoesntExists(module: string, filename: string) { + clearConsole(true); + return print([ + errorBadge() + ' ' + chalk.red(`Module '${module}' doesn't exists.`), + '', + `Error in ${filename}`, + '', + `Webpack tried to resolve module ${chalk.bgYellow.black(' ' + module + ' ')} which doesn't exist.`, + '', + `It's likely caused by ${chalk.yellow('typo')} in the module name.`, + '' + ]); +} + /** * * Build Messages diff --git a/src/webpack-dev-server.js b/src/webpack-dev-server.js index c04d088..1ea465f 100644 --- a/src/webpack-dev-server.js +++ b/src/webpack-dev-server.js @@ -2,6 +2,7 @@ import detectPort from 'detect-port'; import historyApiFallback from 'connect-history-api-fallback'; +import resolveModule from 'resolve'; import webpack from 'webpack'; import WebpackDevServer from 'webpack-dev-server'; import webpackConfigBuilder from './webpack/config-builder'; @@ -13,13 +14,15 @@ import { devServerInvalidBuildMsg, devServerCompiledSuccessfullyMsg, devServerFailedToCompileMsg, - devServerCompiledWithWarningsMsg + devServerCompiledWithWarningsMsg, + devServerRestartMsg, + devServerModuleDoesntExists } from './utils/messages'; /** * On done handler for webpack compiler. */ -export function onDone(filename: string, flags: CLIFlags, params: AikParams, stats: Object) { +export function onDone(filename: string, flags: CLIFlags, params: AikParams, compiler: any, invalidate: Function, stats: Object) { // eslint-disable-line const hasErrors = stats.hasErrors(); const hasWarnings = stats.hasWarnings(); const buildDuration: number = stats.endTime - stats.startTime; @@ -37,6 +40,12 @@ export function onDone(filename: string, flags: CLIFlags, params: AikParams, sta let formattedErrors = json.errors.map(message => 'Error in ' + formatMessage(message)); if (hasErrors) { + if (formattedErrors.filter(err => err.match('Cannot resolve module')).length) { + invalidate(formattedErrors); + testUtils(); + return; + } + devServerFailedToCompileMsg(); // If there are any syntax errors, show just them. @@ -65,10 +74,10 @@ export function onDone(filename: string, flags: CLIFlags, params: AikParams, sta /** * Creates webpack compiler. */ -export function createWebpackCompiler(filename: string, flags: CLIFlags, params: AikParams, config: Object) { +export function createWebpackCompiler(filename: string, flags: CLIFlags, params: AikParams, config: Object, invalidate: Function) { // eslint-disable-line const compiler = webpack(config); compiler.plugin('invalid', devServerInvalidBuildMsg); - compiler.plugin('done', onDone.bind(null, filename, flags, params)); + compiler.plugin('done', onDone.bind(null, filename, flags, params, compiler, invalidate)); return compiler; } @@ -82,9 +91,31 @@ export default function createWebpackDevServer(filename: string, flags: CLIFlags flags.port = port; } + let server: WebpackDevServer; // eslint-disable-line + const invalidate = (errors: string[]) => { + if (!server) return; + + const error = errors[0] || ''; + const fileWithError = (error.match(/Error in (.+)\n/) || [])[1]; + let moduleName = (error.match(/Module not found: Error: Cannot resolve module '(.+)'/) || [])[1]; + + if (!moduleName) return; + + moduleName = moduleName.replace(/'/gmi, ''); + + try { + resolveModule.sync(moduleName, { basedir: process.cwd() }); + devServerRestartMsg(moduleName); + server.close(); + createWebpackDevServer(filename, flags, params); + } catch (e) { // eslint-disable-line + devServerModuleDoesntExists(moduleName, fileWithError); + } + }; const config = webpackConfigBuilder(filename, flags, params); - const compiler = createWebpackCompiler(filename, flags, params, config); - const server = new WebpackDevServer(compiler, { + const compiler = createWebpackCompiler(filename, flags, params, config, invalidate); + + server = new WebpackDevServer(compiler, { // Enable gzip compression of generated files. compress: true,