diff --git a/fixtures/browser/issue-5234-direct-mjs-package/__snapshots__/index.test.js.snap b/fixtures/browser/issue-5234-direct-mjs-package/__snapshots__/index.test.js.snap new file mode 100644 index 0000000000..d9822e4559 --- /dev/null +++ b/fixtures/browser/issue-5234-direct-mjs-package/__snapshots__/index.test.js.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`issue #5234 (mjs files are imported as static files) correctly bundles files in development 1`] = ` +Object { + "graphql": "undefined", + "parse": "function", + "test": "/static/media/Test.4478d87c.mjs", +} +`; diff --git a/fixtures/browser/issue-5234-direct-mjs-package/index.test.js b/fixtures/browser/issue-5234-direct-mjs-package/index.test.js new file mode 100644 index 0000000000..99bf4e95ea --- /dev/null +++ b/fixtures/browser/issue-5234-direct-mjs-package/index.test.js @@ -0,0 +1,29 @@ +const { bootstrap, startDevelopmentServer } = require('../../utils'); +const puppeteer = require('puppeteer'); + +beforeEach(async () => { + await bootstrap({ directory: global.testDirectory, template: __dirname }); + global.appPort = await startDevelopmentServer({ + directory: global.testDirectory, + }); + await new Promise(resolve => setTimeout(resolve, 3000)); +}); + +// https://github.com/facebook/create-react-app/issues/5234 +describe('issue #5234 (mjs files are imported as static files)', () => { + it('correctly bundles files in development', async () => { + const browser = await puppeteer.launch({ headless: true }); + try { + const page = await browser.newPage(); + console.log(`http://localhost:${global.appPort}/`); + await page.goto(`http://localhost:${global.appPort}/`); + await page.waitForSelector('.App-Ready'); + const output = await page.evaluate(() => { + return document.testData; + }); + expect(output).toMatchSnapshot(); + } finally { + browser.close(); + } + }); +}); diff --git a/fixtures/browser/issue-5234-direct-mjs-package/package.json b/fixtures/browser/issue-5234-direct-mjs-package/package.json new file mode 100644 index 0000000000..94aa62aa03 --- /dev/null +++ b/fixtures/browser/issue-5234-direct-mjs-package/package.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "react": "^16.5.2", + "react-dom": "^16.5.2", + "graphql": "14.0.2" + } +} diff --git a/fixtures/browser/issue-5234-direct-mjs-package/public/index.html b/fixtures/browser/issue-5234-direct-mjs-package/public/index.html new file mode 100644 index 0000000000..86010b2406 --- /dev/null +++ b/fixtures/browser/issue-5234-direct-mjs-package/public/index.html @@ -0,0 +1,9 @@ + + + + React App + + +
+ + diff --git a/fixtures/browser/issue-5234-direct-mjs-package/src/Test.mjs b/fixtures/browser/issue-5234-direct-mjs-package/src/Test.mjs new file mode 100644 index 0000000000..be3d611be2 --- /dev/null +++ b/fixtures/browser/issue-5234-direct-mjs-package/src/Test.mjs @@ -0,0 +1,6 @@ +export function foo() { + console.log('fooey'); +} +export default function bar() { + console.log('barrio'); +} diff --git a/fixtures/browser/issue-5234-direct-mjs-package/src/index.html b/fixtures/browser/issue-5234-direct-mjs-package/src/index.html new file mode 100644 index 0000000000..86010b2406 --- /dev/null +++ b/fixtures/browser/issue-5234-direct-mjs-package/src/index.html @@ -0,0 +1,9 @@ + + + + React App + + +
+ + diff --git a/fixtures/browser/issue-5234-direct-mjs-package/src/index.js b/fixtures/browser/issue-5234-direct-mjs-package/src/index.js new file mode 100644 index 0000000000..1ae2016f67 --- /dev/null +++ b/fixtures/browser/issue-5234-direct-mjs-package/src/index.js @@ -0,0 +1,17 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import graphql, { parse } from 'graphql'; +import test from './Test.mjs'; + +class App extends React.Component { + state = { ready: false }; + async componentDidMount() { + document.testData = { graphql: typeof graphql, parse: typeof parse, test }; + this.setState({ ready: true }); + } + render() { + return this.state.ready ?
: null; + } +} + +ReactDOM.render(, document.getElementById('root')); diff --git a/fixtures/browser/jest.config.js b/fixtures/browser/jest.config.js new file mode 100644 index 0000000000..b2f8182ebd --- /dev/null +++ b/fixtures/browser/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + testEnvironment: 'node', + testMatch: ['**/*.test.js'], + testPathIgnorePatterns: ['/src/', 'node_modules'], + setupTestFrameworkScriptFile: './setupSmokeTests.js', +}; diff --git a/fixtures/browser/setupSmokeTests.js b/fixtures/browser/setupSmokeTests.js new file mode 100644 index 0000000000..1d4038de41 --- /dev/null +++ b/fixtures/browser/setupSmokeTests.js @@ -0,0 +1,10 @@ +const fs = require('fs-extra'); +const tempy = require('tempy'); + +beforeEach(() => { + global.testDirectory = tempy.directory(); + jest.setTimeout(1000 * 60 * 5); +}); +afterEach(() => { + fs.removeSync(global.testDirectory); +}); diff --git a/fixtures/utils.js b/fixtures/utils.js index bde092f6e3..08a42535c3 100644 --- a/fixtures/utils.js +++ b/fixtures/utils.js @@ -111,6 +111,24 @@ async function getOutputDevelopment({ directory, env = {} }) { } } +async function startDevelopmentServer({ directory, env = {} }) { + const port = await getPort(); + execa('./node_modules/.bin/react-scripts', ['start'], { + cwd: directory, + env: Object.assign( + {}, + { + BROWSER: 'none', + PORT: port, + CI: 'false', + FORCE_COLOR: '0', + }, + env + ), + }); + return port; +} + async function getOutputProduction({ directory, env = {} }) { try { const { stdout, stderr } = await execa( @@ -141,5 +159,6 @@ module.exports = { isSuccessfulProduction, isSuccessfulTest, getOutputDevelopment, + startDevelopmentServer, getOutputProduction, }; diff --git a/package.json b/package.json index 96d63b4f22..c077728b31 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "meow": "^5.0.0", "multimatch": "^2.1.0", "prettier": "1.14.3", + "puppeteer": "^1.8.0", "strip-ansi": "^4.0.0", "svg-term-cli": "^2.1.1", "tempy": "^0.2.1" diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index fe628e912a..672a21e338 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -206,7 +206,6 @@ module.exports = { // the use of this extension, so we need to tell webpack to fall back // to auto mode (ES Module interop, allows ESM to import CommonJS). test: /\.mjs$/, - include: /node_modules/, type: 'javascript/auto', }, { @@ -271,10 +270,23 @@ module.exports = { cacheCompression: false, }, }, + // Make sure `mjs` files get processed as files, not JS. We don't + // support `mjs`, so we deliver a file for now. + { + test: /\.mjs$/, + include: paths.appSrc, + loader: require.resolve('file-loader'), + options: { + name: 'static/media/[name].[hash:8].[ext]', + }, + }, // Process any JS outside of the app with Babel. // Unlike the application JS, we only compile the standard ES features. + // `mjs` needs to be included here because some libraries forcibly + // import files with the `mjs` extension, or have set their `browser` + // or `module` field to a `mjs` file. { - test: /\.js$/, + test: /\.m?js$/, exclude: /@babel(?:\/|\\{1,2})runtime/, loader: require.resolve('babel-loader'), options: { diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js index ef4bc311d5..9600d57119 100644 --- a/packages/react-scripts/config/webpack.config.prod.js +++ b/packages/react-scripts/config/webpack.config.prod.js @@ -274,7 +274,6 @@ module.exports = { // the use of this extension, so we need to tell webpack to fall back // to auto mode (ES Module interop, allows ESM to import CommonJS). test: /\.mjs$/, - include: /node_modules/, type: 'javascript/auto', }, { @@ -284,6 +283,7 @@ module.exports = { oneOf: [ // "url" loader works just like "file" loader but it also embeds // assets smaller than specified size as data URLs to avoid requests. + // A missing `test` is equivalent to a match. { test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/], loader: require.resolve('url-loader'), @@ -337,10 +337,23 @@ module.exports = { compact: true, }, }, + // Make sure `mjs` files get processed as files, not JS. We don't + // support `mjs`, so we deliver a file for now. + { + test: /\.mjs$/, + include: paths.appSrc, + loader: require.resolve('file-loader'), + options: { + name: 'static/media/[name].[hash:8].[ext]', + }, + }, // Process any JS outside of the app with Babel. // Unlike the application JS, we only compile the standard ES features. + // `mjs` needs to be included here because some libraries forcibly + // import files with the `mjs` extension, or have set their `browser` + // or `module` field to a `mjs` file. { - test: /\.js$/, + test: /\.m?js$/, exclude: /@babel(?:\/|\\{1,2})runtime/, loader: require.resolve('babel-loader'), options: { diff --git a/tasks/e2e-behavior.sh b/tasks/e2e-behavior.sh index 4a48b423b3..f4ed77659a 100755 --- a/tasks/e2e-behavior.sh +++ b/tasks/e2e-behavior.sh @@ -95,6 +95,9 @@ git clean -df # Now that we have published them, run all tests as if they were released. # ****************************************************************************** +# Browser tests +CI=true ./node_modules/.bin/jest --config fixtures/browser/jest.config.js + # Smoke tests CI=true ./node_modules/.bin/jest --config fixtures/smoke/jest.config.js