Overview
I am trying to use Babel and Webpack to build a React app. I know I could use create-react-app but I'd like to learn how these technologies can work together for myself.
When I run yarn run start or yarn run build (see package.json below), Webpack reports to have built the bundle fine. When I run the application in the browser, I get the Uncaught ReferenceError: React is not defined error.
There are a number of questions on SO regarding this same error, but none of the solutions have solved my problem yet.
Question
What piece am I missing to get React, Babel, and Webpack to play together nicely?
Code
package.json
{
"private": true,
"scripts": {
"build": "webpack",
"start": "webpack-dev-server"
},
"dependencies": {
"react": "^15.4.1",
"react-dom": "^15.4.1",
"react-redux": "^5.0.1",
"redux": "^3.6.0"
},
"devDependencies": {
"babel-core": "^6.21.0",
"babel-loader": "^6.2.10",
"babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0",
"redux-devtools": "^3.3.1",
"webpack": "^1.14.0",
"webpack-dev-server": "^1.16.2"
}
}
.babelrc
{
"presets": ["react", "es2015"]
}
webpack.config.js
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: './dist'
},
devtool: 'source-map',
debug: true,
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
]
}
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test</title>
</head>
<body>
<div id="root"></div>
<script src="dist/bundle.js"></script>
</body>
</html>
src/index.js
import ReactDom from 'react-dom';
import React from 'react';
import App from './App';
ReactDom.render(
<App />,
document.getElementById('root'),
);
src/App.js
import React from 'react';
import { Component } from 'react';
export default class App extends Component {
render() {
return (
<h1>hello</h1>
);
}
}
Observations
It seems that Webpack/Babel is expecting React to be available on the global scope, or is ignoring my import React from 'react'; statements.
Also, I have yet to find the proper incantation of devtools and debug properties in my Webpack config to actually get source maps working. I don't yet see them in the compiled output.
EDIT
The broken bundle.js is too large for SO (21,000+ lines), so here is a link: http://chopapp.com/#1a8udqpj — it takes several seconds to load and display.
Since you're trying to learn the in's and out's of webpack et al... Your original problem was that you needed to specify the output.publicPath in your webpack.config.js:
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: './dist',
publicPath: '/dist' // <- was missing
},
devtool: 'source-map',
debug: true,
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
]
}
}
"path" is the physical path to the folder that contains your bundle.js. "publicPath" is the virtual path (URL) to that folder. So, you don't even have to use "dist". For example, if you used...
output: {
filename: 'bundle.js',
path: './dist',
publicPath: '/assets'
},
Your HTML would then point to:
<script src="assets/bundle.js"></script>
I wish to make the following observations. First to your question regarding devTools and debug. The debug flag for webpack (according to their official documentation) switches the loaders to debug mode. Then, from what I understand, webpack, when running in a dev environment, does not actually compile the code to hard files, but keeps it in memory. Thus the source maps are also kept in memory and linked to the browser. You see their effect when you open the browser dev tools and view source.
Now assuming you have the dev server configured correctly, I also assume your index.html is located in your source directory. If this is the case, then your script reference should simply point to '/bundle.js' and not 'dist/bundle.js' since there may not be a physical "dist" folder.
I would also suggest dropping the ".js" from your entry point in the webpack.config.js
You will need to add a query to your module within the webpack.config.js file:
module: {
loaders: [{
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['react', 'es2015']
}
}]
},
resolve: {
extensions: ['', '.js', '.jsx']
};
Related
I am trying to load a JSX Component from a module declared as a devDependency in my project. However, upon running webpack, I get the below error:
Module parse failed: Unexpected token (70:20)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.
|
| render(
> <Provider store={store}>
| <MyApp />
| </Provider>,
# ./src/App.js 48:43-96
# ./src/index.js
I am using webpack 4 and babel 7.
webpack dependencies:
"devDependencies": {
"#babel/core": "^7.9.6",
"#babel/preset-env": "^7.9.6",
"#babel/preset-react": "^7.9.4",
"dummy-pkg": "file:../dummy-pkg",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^8.2.3",
"babel-loader": "^7.1.2",
"sass-loader": "^8.0.0",
"style-loader": "^0.16.1",
"uglifyjs-webpack-plugin": "^2.2.0",
"webpack": "^4.40.2",
"webpack-cli": "^3.3.10"
}
webpack config:
module: {
rules: [
{
test: /\.jsx?$/,
include: [path.resolve(__dirname, './src'), /node_modules\/dummy-pkg/],
use: 'babel-loader'
},
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader']
}
]
}
babelrc
{
"presets": ["#babel/preset-react", "#babel/preset-env"]
}
When I try to import the Component from dummy-pkg, it fails with the error message that I have mentioned above.
If I remove the import, all the other JSX components that have been declared within my current project are transpiled correctly. Introducing this causes all the problems.
I have been looking at a lot of other questions on Stack Overflow as well as some GitHub issues. The solutions suggested there do not seem to work out for me.
Any help would be really appreciated.
TIA!
UPDATE
After some more debugging, I found that you need to define the babel config in babel.config.json
More info available here. Other File types are listed here
Surprisingly what I have noticed is that when you are providing the presets in .babelrc it's not working if there's anything that you want exclude from node_modules but when I placed the presets in my webpack.config it worked like gem. So I would suggest to try keeping the rule for js or jsx like below and test
{
test: /\.(js|jsx)$/,
exclude: [/node_modules\/dummy-pkg/],
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env", "#babel/preset-react"],
plugins: [
"#babel/plugin-proposal-class-properties"
]
}
}
}
Hope this helps.
I am using webpack with HtmlWebpackPlugin, html-loader and file-loader. I have a simple project structure in which I use no frameworks, but only typescript. Thus, I write my HTML code directly to index.html. I also use this HTML file as my template in HtmlWebpackPlugin.
As all websites do I need to put an image which refers to a PNG in my assets folder. file-loader should load the file correctly put the new filename inside the src tag but that is not what is happening. Instead, as the value of src tag, I have [object Module]. I assume the file-loader emits some object and it is represented like this when its .toString() method is run. However, I can see that file-loader has processed the file successfully and emitted with new name to the output path. I get no errors. Here is my webpack configuration and index.html.
const projectRoot = path.resolve(__dirname, '..');
{
entry: path.resolve(projectRoot, 'src', 'app.ts'),
mode: 'production',
output: {
path: path.resolve(projectRoot, 'dist'),
filename: 'app.bundle.js'
},
resolve: {
extensions: ['.ts', '.js']
},
module: {
rules: [
{
test: /\.html$/i,
use: 'html-loader'
},
{
test: /\.(eot|ttf|woff|woff2|svg|png)$/i,
use: 'file-loader'
},
{
test: /\.scss$/i,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: false
}
},
{
loader: 'css-loader',
options: {
sourceMap: false
}
},
{
loader: 'sass-loader',
options: {
sourceMap: false
}
}
]
},
{
exclude: /node_modules/,
test: /\.ts$/,
use: 'ts-loader'
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: path.resolve(projectRoot, 'src', 'index.html')
}),
new MiniCssExtractPlugin({
filename: '[name].[hash].css',
chunkFilename: '[id].[hash].css',
ignoreOrder: false
})
]
};
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
</head>
<body class="dark">
<header>
<nav class="navigation">
<div class="left">
<img src="assets/logo.png" class="logo"> <!-- This logo is output as [object Module] -->
</div>
<div class="right">
</div>
</nav>
</header>
</body>
</html>
Project structure:
config/
webpack.config.js
dist/
src/
styles/
assets/
logo.png
index.html
app.ts
Edit
My package.json dependencies:
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^3.2.0",
"file-loader": "^5.0.2",
"html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.8.0",
"node-sass": "^4.13.0",
"sass-loader": "^8.0.0",
"style-loader": "^1.0.0",
"ts-loader": "^6.2.1",
"typescript": "^3.7.2",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.9.0"
Per the file-loader docs:
By default, file-loader generates JS modules that use the ES modules syntax. There are some cases in which using ES modules is beneficial, like in the case of module concatenation and tree shaking.
It seems that webpack resolves ES module require() calls to an object that looks like this: {default: module}, instead of to the flattened module itself. This behavior is somewhat controversial and is discussed in this issue.
Therefore, to get your src attribute to resolve correctly, you need to be able to access the default property of the exported module. If you're using a framework, you should be able to do something like this:
<img src={require('assets/logo.png').default}/> <!-- React -->
<!-- OR -->
<img src="require('assets/logo.png').default"/> <!-- Vue -->
Alternatively, you can enable file-loader's CommonJS module syntax, which webpack will resolve directly to the module itself. Set esModule:false in your webpack config.
webpack.config.js:
{
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
esModule: false,
},
},
],
},
#stellr42's suggested fix of esModule: false in your file-loader configuration is the best workaround at the current time.
However, this is actually a bug in html-loader which is being tracked here: https://github.com/webpack-contrib/html-loader/issues/203
It looks like ES Module support was added to file-loader, css-loader, and other friends, but html-loader was missed.
Once this bug is fixed, it will be better to remove esModule: false and simply upgrade html-loader, as ES Modules offer some minor benefits (as mentioned in the docs)
Alternatively, if (like me), you found this issue because you were having trouble loading an image from CSS (instead of from HTML), then the fix is just to upgrade css-loader, no need to disable ES Modules.
Just updated my file-loader to ^5.0.2 minutes ago.
I know esModule: false was the suggested fix but that did not work for me.
My fix was <img src={require('assets/logo.png').default}/> which was weird. First time using .default but it worked.
This happens on file-loader version 5.0.2 , earlier version works fine without calling default property
Instead of this: <img src="require('assets/logo.png').default"/>
Use it like this: <img src={require('assets/logo.png').default}/>
Use "default" followed by require to display a dynamic image in react js
src={require('../images/'+image_name+'.png').default}
For Next.JS:
// require(...).default.src
<img src={require("../public/images/avatar.png").default.src} width={256} height={256} />
I have had the same problem recently after upgrading Laravel Mix v4 to v6.
This works fine with me in my Vue component.
<img :src="require('./assets/profile.png').default"/>
I had the same problem in vuejs and esModule:false did not work for me.
Instead, I used #kerubim solution and it fixed it, but only in production mode and in development mode I get some error.
So I wrote this function in util.js that solved my problem.
maybeDefault: (module) => {
if (typeof module === "object") {
module = module.default;
}
return module;
},
use example:
let logo = 'logo.svg';
util.maybeDefault(require(`img/svg/logos/${logo}`));
This is a weird issue still unsure how mine got fixed.
So I deleted my node_module and package-lock.json and ran npm install --force
and it worked fine after
I have an issue with webpack. im using webpack-dev-server but when i try to run It im having this error SyntaxError: Unexpected token { trying to figure it out since yesterday but no luck.
I already tried to export default on other files still got the same issue when importing.
./sort.js
this is my export
export {descend, ascend};
./index.js
this is my import
import {descend, ascend} from './js/insertion_sort';
./babelrc
this is babel
{
"presets": [["#babel/preset-env"]]
}
webpack.config.js
this my webpack config
const path = require('path');
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/'
},
module: {
rules: [
{test: /\.scss$/, use: 'scss-loader'},
{test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader'}
]
},
devServer: {
contentBase: './'
}
};
im not using the bundle btw. im just using index file as my main.
UPDATE
so guys this is so weird. what i did. on my html.index is this i change the script tag to <script src="./src/index.js"></script> to <script src="./bundle.js"></script> then its working. but on my package.json i havent built it yet. i was just running it. "start": "webpack-dev-server --mode development --hot" how would webpack now where to look for bundle.js when its not ceated yet. crazy
Good afternoon,
This is the same issue I reported at webpack's github, but I suspect I might be the one doing something wrong, thus opening a question here.
I'm trying to configure webpack 2 with Babel, and one of the requirements is to transpile built-ins such as Symbol.
Despite that now working fine, when I try to use webpack and babel's transform-runtime, I'm unable to use exports *.
Input file (src/index.js):
export * from './secondFile'
secondFile.js:
export let TESTSYMBOL = Symbol('test');
export let TESTSYMBOL2 = Symbol('test2');
webpack.config.js (only copied the relevant part):
module: {
rules: [
{
test: /\.jsx?$/,
// Skip any files outside of `src` directory
include:path.resolve(__dirname, 'src'),
use: {
loader: 'babel-loader',
options: {
presets: ["es2015", "stage-3"],
plugins: ['transform-runtime']
}
}
}
]
}
script:
"webpack -d --config config/webpack.config.js"
Output file: gist
Exception:
Uncaught ReferenceError: exports is not defined - at Object.defineProperty(exports, "__esModule", {
Dev Dependencies:
"webpack": "2.6.1",
"webpack-dev-server": "2.4.5",
"webpack-notifier": "1.5.0"
"babel-cli": "6.24.1",
"babel-loader": "7.0.0",
"babel-plugin-transform-runtime": "6.23.0",
"babel-preset-es2015": "6.24.1",
"babel-preset-stage-3": "6.24.1"
Dependencies:
- "babel-runtime": "6.23.0"
Thanks for any help!
It seems that the problem is with the include. For some reason, I was unable to use path.resolve or path.join. The webpack documentation has such example.
If the webconfig is as follows, it works just fine:
module: {
rules: [
{
test: /\.js$/,
include: [
/src/
],
// or exclude: [/node_modules/],
use:
{
loader: 'babel-loader',
options: {
plugins: ['transform-runtime'],
presets: ['es2015', 'stage-3']
}
}
}
]
}
Either way, now there's a problem with exports not defined, which can be solved by setting modules to false in es2015 preset (thanks to Vanuan at Github for that suggestion):
presets: [['es2015', { modules: false }], 'stage-3'],
For IE or older browsers, I need to use es-shims - libraries which ports the ECMAScript specs to legacy JS engines.
These libs below may resolve your problem if added as the first imports on your index.html (or equivalent). Here's one for example:
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.7/es5-shim.min.js"></script>
See this link for every lib you may need:
ES-Shims
I'm trying to setup a webpack (1.13.3) config for a React (15.3.2) app, and I want the production version of React in my production build. I'm on Windows.
I'm using this (which is everywhere online when you search):
new webpack.ProvidePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production')
}
})
However, as soon as I add this and run webpack (CLI) I get slapped round the face with many many errors.
I get a whole bunch of these warnings:
WARNING in ./~/fbjs/lib/partitionObject.js.flow
Module parse failed: C:\node\sandbox\react-webpack\node_modules\fbjs\lib[ SOME FILE NAME HERE].flow Unexpected token (18:24)
You may need an appropriate loader to handle this file type.
...and a bunch of these errors:
ERROR in ./~/react/lib/NativeMethodsMixin.js
Module not found: Error: Cannot resolve module 'react-native/lib/TextInputState' in C:\node\sandbox\react-webpack\node_modules\react\lib
# ./~/react/lib/NativeMethodsMixin.js 17:21-63
I'm not using React Native, I wouldn't know how.
I'm also using webpack.optimize.UglifyJsPlugin in my webpack.config.production.js file.
When I remove the webpack.ProvidePlugin bit the build works, but includes the development version of React and I get warnings in the console:
Warning: It looks like you're using a minified copy of the development build of React. When deploying React apps to production, make sure to use the production build which skips development warnings and is faster.
What are these webpack errors all about..? How do I get a production build of React which is also minified..?
UPDATE
Full webpack config requested. I've setup a test app for this, with minimum config and the warnings and errors are still showing:
webpack.config.js
module.exports = process.env.NODE_ENV === 'production' ? require('./webpack.config.production.js') : require('./webpack.config.development.js')
webpack.config.base.js
module.exports = {
entry: {
'bundle': './client/index.js'
},
output: {
path: './public',
filename: 'js/[name].js'
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
query: { presets: ['es2015', 'react'] }
}
]
}
}
webpack.config.development.js
var config = require('./webpack.config.base')
module.exports = config
webpack.config.production.js
var webpack = require('webpack')
var config = require('./webpack.config.base')
config.plugins = [
new webpack.ProvidePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production')
}
}),
new webpack.optimize.UglifyJsPlugin({
include: /\.js$/,
compress: { warnings: false }
})
]
module.exports = config
The test app I'm using only has the following installed:
"dependencies": {
"express": "^4.14.0",
"react": "^15.3.2",
"react-dom": "^15.3.2"
},
"devDependencies": {
"babel": "^6.5.2",
"babel-core": "^6.18.2",
"babel-loader": "^6.2.7",
"babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0",
"webpack": "^1.13.3"
}
/client/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import Hello from '../components/Hello'
ReactDOM.render(<Hello />, document.getElementById('page'))
/components/Hello.js
import React from 'react'
const Hello = props => <p>Hello world...</p>
export default Hello
Asked again in a separate question, but with the full error list, and from a React Native view, because the errors contain references to React Native, even though I'm not trying to use it:
Webpack in production: Why React Native errors?
Answered here: Webpack in production: Why React Native errors? by Aruna Herath.
I should be using DefinePlugin not ProvidePlugin.