diff --git a/packages/react-dev-utils/WatchTestFilesPlugin.js b/packages/react-dev-utils/WatchTestFilesPlugin.js new file mode 100644 index 00000000000..65e16f76d44 --- /dev/null +++ b/packages/react-dev-utils/WatchTestFilesPlugin.js @@ -0,0 +1,77 @@ +var glob = require('glob'); +var path = require('path'); +var os = require('os'); +var SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin'); + +function computeGlob(pattern, options) { + return new Promise((resolve, reject) => { + glob(pattern, options || {}, (err, matches) => { + if (err) { + return reject(err); + } + resolve(matches); + }); + }); +} + +function getGlobs(patterns, cwd) { + return Promise.all(patterns.map(globPattern => + computeGlob(globPattern, { + cwd: cwd, + ignore: 'node_modules/**/*', + nodir: true, + }) + )) + .then(globLists => [].concat.apply([], globLists)) +} + +function WatchTestFilesPlugin(testGlobs) { + this.testGlobs = testGlobs || []; +} + +function compileTestFile(compiler, compilation, context, testFile) { + var outputOptions = { + filename: path.join(os.tmpdir(), '__compiledTests__', testFile), + publicPath: compilation.outputOptions.publicPath, + }; + var compilerName = "WatchTestFiles compilation for " + testFile; + var childCompiler = compilation.createChildCompiler(compilerName, outputOptions); + childCompiler.context = context; + childCompiler.apply( + new SingleEntryPlugin(context, path.join(compiler.options.context, testFile)) + ); + return new Promise((resolve, reject) => { + childCompiler.runAsChild((err, entries, childCompilation) => { + if (err) { + return reject(err); + } + resolve({ + errors: childCompilation.errors, + warnings: childCompilation.warnings, + }); + }); + }); +} + +WatchTestFilesPlugin.prototype.apply = function(compiler) { + compiler.plugin('emit', (compilation, callback) => { + getGlobs(this.testGlobs, compiler.options.context) + .then(foundFiles => Promise.all( + foundFiles.map(filename => { + // Add them to the list of watched files (for auto-reloading) + compilation.fileDependencies.push(path.join(compiler.options.context, filename)); + // Create and run a sub-compiler for the file to send it through the loaders + return compileTestFile(compiler, compilation, compiler.context, filename) + }) + )) + .then((results) => { + var errors = results.reduce((list, res) => list.concat(res.errors || []), []); + var warnings = results.reduce((list, res) => list.concat(res.warnings || []), []); + compilation.errors = compilation.errors.concat(errors); + compilation.warnings = compilation.warnings.concat(warnings); + callback(); + }, callback); + }); +}; + +module.exports = WatchTestFilesPlugin; diff --git a/packages/react-dev-utils/package.json b/packages/react-dev-utils/package.json index 5d8051a8830..fce93d7f170 100644 --- a/packages/react-dev-utils/package.json +++ b/packages/react-dev-utils/package.json @@ -20,15 +20,23 @@ "openBrowser.js", "prompt.js", "WatchMissingNodeModulesPlugin.js", + "WatchTestFilesPlugin.js", "webpackHotDevClient.js" ], "dependencies": { "ansi-html": "0.0.5", "chalk": "1.1.3", "escape-string-regexp": "1.0.5", + "glob": "^7.1.1", "html-entities": "1.2.0", "opn": "4.0.2", "sockjs-client": "1.0.3", "strip-ansi": "3.0.1" + }, + "devDependencies": { + "webpack": "1.14.0" + }, + "peerDependencies": { + "webpack": "1.14.0" } } diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index 96fd632b795..2937b5b78e3 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -15,6 +15,7 @@ var HtmlWebpackPlugin = require('html-webpack-plugin'); var CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); var InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); var WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin'); +var WatchTestFilesPlugin = require('react-dev-utils/WatchTestFilesPlugin'); var getClientEnvironment = require('./env'); var paths = require('./paths'); @@ -226,7 +227,15 @@ module.exports = { // to restart the development server for Webpack to discover it. This plugin // makes the discovery automatic so you don't have to restart. // See https://github.com/facebookincubator/create-react-app/issues/186 - new WatchMissingNodeModulesPlugin(paths.appNodeModules) + new WatchMissingNodeModulesPlugin(paths.appNodeModules), + // Tests won't have any linting unless they go through webpack. + // This plugin makes webpack aware of them without emitting them. + // See https://github.com/facebookincubator/create-react-app/issues/1169 + new WatchTestFilesPlugin([ + 'src/**/__tests__/**/*', + 'src/**/*.test.*', + '__tests__/**/*', + ]), ], // Some libraries import Node modules but don't use them in the browser. // Tell Webpack to provide empty mocks for them so importing them works. diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js index 71509658eb7..b0426612df2 100644 --- a/packages/react-scripts/config/webpack.config.prod.js +++ b/packages/react-scripts/config/webpack.config.prod.js @@ -16,6 +16,7 @@ var ExtractTextPlugin = require('extract-text-webpack-plugin'); var ManifestPlugin = require('webpack-manifest-plugin'); var InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); var SubresourceIntegrityPlugin = require('webpack-subresource-integrity'); +var WatchTestFilesPlugin = require('react-dev-utils/WatchTestFilesPlugin'); var url = require('url'); var paths = require('./paths'); var getClientEnvironment = require('./env'); @@ -277,7 +278,15 @@ module.exports = { // Generate and inject subresources hashes in the final `index.html`. new SubresourceIntegrityPlugin({ hashFuncNames: ['sha256', 'sha384'] - }) + }), + // Tests won't have any linting unless they go through webpack. + // This plugin makes webpack aware of them without emitting them. + // See https://github.com/facebookincubator/create-react-app/issues/1169 + new WatchTestFilesPlugin([ + 'src/**/__tests__/**/*', + 'src/**/*.test.*', + '__tests__/**/*', + ]) ], // Some libraries import Node modules but don't use them in the browser. // Tell Webpack to provide empty mocks for them so importing them works.