diff --git a/packages/react-scripts/bin/react-scripts.js b/packages/react-scripts/bin/react-scripts.js index 58381833957..d1853fc9fe9 100755 --- a/packages/react-scripts/bin/react-scripts.js +++ b/packages/react-scripts/bin/react-scripts.js @@ -1,5 +1,6 @@ #!/usr/bin/env node var spawn = require('cross-spawn'); +var getDebugFlag = require('../utils/getDebugFlag'); var script = process.argv[2]; var args = process.argv.slice(3); @@ -8,9 +9,14 @@ case 'build': case 'eject': case 'start': case 'test': + var scriptArgs = [require.resolve('../scripts/' + script)].concat(args); + var debugFlag = getDebugFlag(args); + if (debugFlag) { + scriptArgs.unshift(debugFlag); + } var result = spawn.sync( 'node', - [require.resolve('../scripts/' + script)].concat(args), + scriptArgs, {stdio: 'inherit'} ); process.exit(result.status); diff --git a/packages/react-scripts/config/jest/babelJestDebugConfig.js b/packages/react-scripts/config/jest/babelJestDebugConfig.js new file mode 100644 index 00000000000..f905f1ff699 --- /dev/null +++ b/packages/react-scripts/config/jest/babelJestDebugConfig.js @@ -0,0 +1,3 @@ +const babelJestDefaultConfig = require('./babelJestDefaultConfig'); +const assign = require('object-assign'); +module.exports = assign(babelJestDefaultConfig, { sourceMaps: true }); diff --git a/packages/react-scripts/config/jest/babelJestDefaultConfig.js b/packages/react-scripts/config/jest/babelJestDefaultConfig.js new file mode 100644 index 00000000000..2a2e6cc76a6 --- /dev/null +++ b/packages/react-scripts/config/jest/babelJestDefaultConfig.js @@ -0,0 +1,4 @@ +module.exports = { + presets: [require.resolve('babel-preset-react-app')], + babelrc: false +} diff --git a/packages/react-scripts/config/jest/babelTransform.js b/packages/react-scripts/config/jest/babelTransform.js index 145bd86cc9a..5c407670f22 100644 --- a/packages/react-scripts/config/jest/babelTransform.js +++ b/packages/react-scripts/config/jest/babelTransform.js @@ -7,8 +7,5 @@ */ const babelJest = require('babel-jest'); - -module.exports = babelJest.createTransformer({ - presets: [require.resolve('babel-preset-react-app')], - babelrc: false -}); +const babelJestDebugConfig = require('./babelJestDebugConfig'); +module.exports = babelJest.createTransformer(babelJestDebugConfig); diff --git a/packages/react-scripts/config/jest/babelTransformDebug.js b/packages/react-scripts/config/jest/babelTransformDebug.js new file mode 100644 index 00000000000..5c407670f22 --- /dev/null +++ b/packages/react-scripts/config/jest/babelTransformDebug.js @@ -0,0 +1,11 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +const babelJest = require('babel-jest'); +const babelJestDebugConfig = require('./babelJestDebugConfig'); +module.exports = babelJest.createTransformer(babelJestDebugConfig); diff --git a/packages/react-scripts/scripts/eject.js b/packages/react-scripts/scripts/eject.js index aeed2967d06..ec655f0e8b9 100644 --- a/packages/react-scripts/scripts/eject.js +++ b/packages/react-scripts/scripts/eject.js @@ -59,7 +59,8 @@ prompt( path.join('config', 'jest', 'fileTransform.js'), path.join('scripts', 'build.js'), path.join('scripts', 'start.js'), - path.join('scripts', 'test.js') + path.join('scripts', 'test.js'), + path.join('utils', 'getDebugFlag.js') ]; // Ensure that the app folder is clean and we won't override any files @@ -122,11 +123,10 @@ prompt( console.log(cyan('Configuring package.json')); // Add Jest config console.log(' Adding ' + cyan('Jest') + ' configuration'); - appPackage.jest = createJestConfig( - filePath => path.join('', filePath), - null, - true - ); + appPackage.jest = createJestConfig({ + resolve: filePath => path.join('', filePath), + isEjecting: true + }); // Add Babel config diff --git a/packages/react-scripts/scripts/test.js b/packages/react-scripts/scripts/test.js index 9de5181d739..307b45ee702 100644 --- a/packages/react-scripts/scripts/test.js +++ b/packages/react-scripts/scripts/test.js @@ -9,6 +9,11 @@ */ // @remove-on-eject-end +/** + * Greetings! If you are here attempting to start a debugging session, please + * ensure that your debugger of choice is configured to enable source maps, + * otherwise your code may appear mangled by babel! + */ process.env.NODE_ENV = 'test'; process.env.PUBLIC_URL = ''; @@ -20,21 +25,32 @@ require('dotenv').config({silent: true}); const jest = require('jest'); const argv = process.argv.slice(2); +const debugFlag = require('../utils/getDebugFlag')(argv); +const isDebug = !!debugFlag; +const isRunInBand = argv.indexOf('--runInBand') > -1 || argv.indexOf('-i') > -1 // Watch unless on CI or in coverage mode -if (!process.env.CI && argv.indexOf('--coverage') < 0) { +if (!process.env.CI && argv.indexOf('--coverage') < 0 && !isDebug) { argv.push('--watch'); } +// Force debug into single worker +if (isDebug) { + if (!isRunInBand) { + argv.push('--runInBand') + } + argv.splice(argv.indexOf(debugFlag), 1) +} + // @remove-on-eject-begin // This is not necessary after eject because we embed config into package.json. const createJestConfig = require('../utils/createJestConfig'); const path = require('path'); const paths = require('../config/paths'); -argv.push('--config', JSON.stringify(createJestConfig( - relativePath => path.resolve(__dirname, '..', relativePath), - path.resolve(paths.appSrc, '..'), - false -))); +argv.push('--config', JSON.stringify(createJestConfig({ + resolve: relativePath => path.resolve(__dirname, '..', relativePath), + rootDir: path.resolve(paths.appSrc, '..'), + isDebug: isDebug +}))); // @remove-on-eject-end jest.run(argv); diff --git a/packages/react-scripts/template/README.md b/packages/react-scripts/template/README.md index e55e6c9f4f8..95c317c695c 100644 --- a/packages/react-scripts/template/README.md +++ b/packages/react-scripts/template/README.md @@ -48,6 +48,7 @@ You can find the most recent version of this guide [here](https://github.com/fac - [Disabling jsdom](#disabling-jsdom) - [Experimental Snapshot Testing](#experimental-snapshot-testing) - [Editor Integration](#editor-integration) + - [Debugging](#debugging) - [Developing Components in Isolation](#developing-components-in-isolation) - [Making a Progressive Web App](#making-a-progressive-web-app) - [Deployment](#deployment) @@ -480,7 +481,7 @@ Now you are ready to use the imported React Bootstrap components within your com Flow is a static type checker that helps you write code with fewer bugs. Check out this [introduction to using static types in JavaScript](https://medium.com/@preethikasireddy/why-use-static-types-in-javascript-part-1-8382da1e0adb) if you are new to this concept. -Recent versions of [Flow](http://flowtype.org/) work with Create React App projects out of the box. +Recent versions of [Flow](http://flowtype.org/) work with Create React App projects out of the box. To add Flow to a Create React App project, follow these steps: @@ -938,10 +939,18 @@ This feature is experimental and still [has major usage issues](https://github.c ### Editor Integration -If you use [Visual Studio Code](https://code.visualstudio.com), there is a [Jest extension](https://github.com/orta/vscode-jest) which works with Create React App out of the box. This provides a lot of IDE-like features while using a text editor: showing the status of a test run with potential fail messages inline, starting and stopping the watcher automatically, and offering one-click snapshot updates. +If you use [Visual Studio Code](https://code.visualstudio.com), there is a [Jest extension](https://github.com/orta/vscode-jest) which works with Create React App out of the box. This provides a lot of IDE-like features while using a text editor: showing the status of a test run with potential fail messages inline, starting and stopping the watcher automatically, and offering one-click snapshot updates. ![VS Code Jest Preview](https://cloud.githubusercontent.com/assets/49038/20795349/a032308a-b7c8-11e6-9b34-7eeac781003f.png) +### Debugging + +You can easily debug your tests by passing the usual debug flags to `npm test`. + +For example: + - Run `npm test -- --debug-brk` to attach the the debugger using your debugger of choice (vscode, webstorm, etc). + - Run `npm test -- debug` to enter the CLI debugger. + ## Developing Components in Isolation Usually, in an app, you have a lot of UI components, and each of them has many different states. @@ -1173,16 +1182,16 @@ GitHub Pages doesn't support routers that use the HTML5 `pushState` history API ### Heroku Use the [Heroku Buildpack for Create React App](https://github.com/mars/create-react-app-buildpack).
-You can find instructions in [Deploying React with Zero Configuration](https://blog.heroku.com/deploying-react-with-zero-configuration). +You can find instructions in [Deploying React with Zero Configuration](https://blog.heroku.com/deploying-react-with-zero-configuration). #### Resolving "Module not found: Error: Cannot resolve 'file' or 'directory'" Sometimes `npm run build` works locally but fails during deploy via Heroku with an error like this: -``` +``` remote: Failed to create a production build. Reason: -remote: Module not found: Error: Cannot resolve 'file' or 'directory' -MyDirectory in /tmp/build_1234/src +remote: Module not found: Error: Cannot resolve 'file' or 'directory' +MyDirectory in /tmp/build_1234/src ``` This means you need to ensure that the lettercase of the file or directory you `import` matches the one you see on your filesystem or on GitHub. diff --git a/packages/react-scripts/utils/createJestConfig.js b/packages/react-scripts/utils/createJestConfig.js index f1c67c018f1..2a846e5dbf8 100644 --- a/packages/react-scripts/utils/createJestConfig.js +++ b/packages/react-scripts/utils/createJestConfig.js @@ -12,11 +12,18 @@ const fs = require('fs'); const paths = require('../config/paths'); -module.exports = (resolve, rootDir, isEjecting) => { +module.exports = (opts) => { + const resolve = opts.resolve; + const rootDir = opts.rootDir || null; + const isEjecting = opts.isEjecting || false; + const isDebug = opts.isDebug || false; + // Use this instead of `paths.testsSetup` to avoid putting // an absolute filename into configuration after ejecting. const setupTestsFile = fs.existsSync(paths.testsSetup) ? '/src/setupTests.js' : undefined; - + const babelTransform = isEjecting + ? '/node_modules/babel-jest' + : resolve('config/jest/babelTransform' + (isDebug ? 'Debug' : '') + '.js') // TODO: I don't know if it's safe or not to just use / as path separator // in Jest configs. We need help from somebody with Windows to determine this. const config = { @@ -29,9 +36,7 @@ module.exports = (resolve, rootDir, isEjecting) => { testEnvironment: 'node', testURL: 'http://localhost', transform: { - '^.+\\.(js|jsx)$': isEjecting ? - '/node_modules/babel-jest' - : resolve('config/jest/babelTransform.js'), + '^.+\\.(js|jsx)$': babelTransform, '^.+\\.css$': resolve('config/jest/cssTransform.js'), '^(?!.*\\.(js|jsx|css|json)$)': resolve('config/jest/fileTransform.js'), }, diff --git a/packages/react-scripts/utils/getDebugFlag.js b/packages/react-scripts/utils/getDebugFlag.js new file mode 100644 index 00000000000..f9ef7d250a2 --- /dev/null +++ b/packages/react-scripts/utils/getDebugFlag.js @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +const DEBUG_FLAGS = [ 'debug', '--debug-brk', '--inspect' ]; + +module.exports = function getDebugFlag(argv) { + for (var i in argv) { + if (argv.hasOwnProperty(i)) { + for (var j in DEBUG_FLAGS) { + if (DEBUG_FLAGS.hasOwnProperty(j)) { + if (argv[i] === DEBUG_FLAGS[j]) { + return DEBUG_FLAGS[j]; + } + } + } + } + } + return null; +}