When compiling a React and Redux application with Webpack and Babel I get:
Uncaught TypeError: Cannot assign to read only property '__esModule' of #<Object>
In some older browsers (<= Chrome 1, Android 4, Safari 5).
This issue seems to stem from redux and react-redux outputting the line exports.__esModule = true; in the lib build but my application using Object.defineProperty instead (because they build loosely and I do not).
Two solutions are:
Building my application in loose mode also.
Importing react-redux/src and redux/src and building it with the same .babelrc as the application (everything is not loose).
As long as they are consistent and both:
Object.defineProperty(exports, "__esModule", {
value: true
});
and exports.__esModule = true; do not co-exist in my output, everything works.
My question is, what is the right solution? Why does this only affect older browsers? And why do they conflict?
Here is a similar question.
Object.defineProperty is broken on some Android 4 stock browser versions and probably other browsers that made use of a buggy implementation in Webkit.
Check this bug report
and and this other one reported to the chromium project.
The good news is you can apply this polyfill to fix the problem.
To make thing easy, you can simply copy and paste that polyfill on a <script> tag before your bundle.
This will fix your issues.
My guess is, you need to install babel-plugin-add-module-exports and in your .babelrc register this plugin:
"plugins": [
"babel-plugin-add-module-exports"
]
For more information visit this website.
In my case, I solved to add babel-register library in entry points.
In webpack.config.js (Webpack 1.x version of configuration)
// As is
entry: {
main: 'index.js'
},
// To be
entry: {
main: ['babel-register', 'index.js']
},
We met this problem on Android 4.0 and currently we cannot cut the support for Android 4.0.
For webpack 1.0, just set loose: true when you are using babel-preset-env.
However for Webpack 2, loose mode can't resolve this problem.
Finally, we found this trick, a little ugly.
// client_patch.js, For Android 4.0
var defineProperty = Object.defineProperty;
Object.defineProperty = function (exports, name) {
if (name === '__esModule') {
exports[name] = true;
return;
}
return defineProperty.apply(this, arguments);
};
And in your webpack config file.
// webpack.config.js
entry: {
main: [
path.resolve(__dirname, 'client_patch.js'),
'index.js'
]
}
Related
I've never published an NPM package before. All these details to generate a package seem way too complicated to my level. The only tool, that was beginner friendly, that I could find is create-react-library which recommended to switch to tsup instead.
I'm asking here to know if there's a batteries-included, most-cases-met, setup for tsup or any other tool of your recommendation for this kind of project (and I think this is a common scenario):
A React Project
Typed with Typescript
Tested with Jest
No dependencies
Exports React components
Should be public on NPM
If you want most-cases-met, batteries-included way to make a library like that then take a look at dts-cli. It will work but it will definitely be slower than tsup. I myself am in the process of switching a library with about 60 react components from dts-cli to tsup because the build time started taking too long (about a minute) on a MacBook Air M1.
Here is an example setup.
First you need to bundle each component separately. You can use a glob as an entry point in Tsup.
Keep in mind that options that works for Esbuild works for Tsup most of the time.
// tsup.config.ts
defineConfig([
{
clean: true,
sourcemap: true,
tsconfig: path.resolve(__dirname, "./tsconfig.build.json"),
entry: ["./components/core/!(index).ts?(x)"],
format: ["esm"],
outDir: "dist/",
esbuildOptions(options, context) {
// the directory structure will be the same as the source
options.outbase = "./";
},
},
Then you'll want to have a index.ts, for convenience, that expose named exports. This index is sometimes referred as a "barrel" file.
// index.ts
// the actual file is "Button.tsx" but we still want a ".js" here
export { Button } from "./components/core/Button.js";
Notice the .js extension. ESM expects explicit extensions so it's needed in the final build.
Adding the .js doesn't seem to bother TypeScript, which stills correctly recognize the type of "Button" from Button.tsx. At this point I am not sure why it works, but it does.
Transpile this index, without bundling.
// tsup.config.ts
{
clean: true,
sourcemap: true,
tsconfig: path.resolve(__dirname, "./tsconfig.build.json"),
entry: ["index.ts", "./components/core/index.ts"],
bundle: false,
format: ["esm"],
outDir: "dist",
esbuildOptions(options, context) {
options.outbase = "./";
},
},
])
Finally define your package.json as usual:
"sideEffects": false,
"type": "module",
"exports": {
".": "./dist/index.js"
},
sideEffects is a non-standard property targeting application bundlers like Webpack and Rollup.
Setting it to false tells them that the package is safe for tree-shaking.
Now import { Button } from "my-package" should work as you expect, and tree-shaking and dynamic loading at app-level become possible because "Button" is bundled as its own ES module Button.js, and the package is marked as being side-effect free.
This is confirmed by my Webpack Bundle Analyzer in a Next app:
Before (a single bundled index.js):
After (separate files means I can select more precisely my imports):
Final config available here (might be improved in the future)
I'm trying to use node-jsencrypt with NextJS (tsx):
index.tsx
import JSEncrypt from 'node-jsencrypt';
package.json
"node-jsencrypt": "^1.0.0"
Log
error - ./node_modules/node-jsencrypt/index.js:2:0
Module not found: Can't resolve 'fs'
Notes:
I didn't find the 'webpack.config.js' file, as I saw in some topics.
Ok, I played around with this issue & I think I have what cover all possible combinations. In the repo you can find working examples. There are 3 possible approaches, and the right one will depend on what's already in your project - details that were unspecified in the original question.
Solution while using webpack 5 next.config.js
module.exports = {
future: {
webpack5: true, // by default, if you customize webpack config, they switch back to version 4.
// Looks like backward compatibility approach.
},
webpack(config) {
config.resolve.fallback = {
...config.resolve.fallback, // if you miss it, all the other options in fallback, specified
// by next.js will be dropped. Doesn't make much sense, but how it is
fs: false, // the solution
};
return config;
},
};
Solution while using webpack 4 - next.config.js
module.exports = {
webpack(config) { // we depend on nextjs switching to webpack 4 by default. Probably they will
// change this behavior at some future major version.
config.node = {
fs: "empty", // webpack4 era solution
};
return config;
},
};
You could consider using other library. According to node-jsencrypt readme they are node port of jsencrypt, and here I assume you try to build for browser. The node library got stuck at version 1, while the original library is already at version 3. As I checked in the last commit on main, if you use this library, it's building just fine without any issues.
Original, nextjs unaware answer:
Since version 5, webpack doesn't include polyfiles for node libraries. In your case, you most likely need to add resolve.fallback.fs: false to your webpack config.
More about this option- https://webpack.js.org/configuration/resolve/#resolvefallback
It mentioned in v4 to v6 migration guide, if this is your case:
https://webpack.js.org/migrate/5/
In the next.config.js file add the code below
build: {
extend(config, {}) {
config.node = {
fs: 'empty'
}
}
},
I have a question or problem.
I'm using React v.16 so when I create a project I did with create-react-app that webpack is already preconfigured. And I want work with ol-cesium, and in npmjs I see that I have to:
create an alias to the goog directory. With webpack:
resolve: {
alias: {
'goog': path_to_goog,
}
}
If I dont create a webpack file show me this error:
./node_modules/olcs/AbstractSynchronizer.js
107:22-35 "export 'getUid' (imported as 'olBase') was not found in 'ol/index.js'
How can solve it??? And what is path_to_goog???
EDIT
Thanks to Shishir Anshuman for your help.
Now I add alias on webpack.config.dev.js and webpack.config.prod.js but some me a lot errors.
resolve: {
// This allows you to set a fallback for where Webpack should look for modules.
// We placed these paths second because we want `node_modules` to "win"
// if there are any conflicts. This matches Node resolution mechanism.
// https://github.com/facebookincubator/create-react-app/issues/253
modules: ['node_modules', paths.appNodeModules].concat(
// It is guaranteed to exist because we tweak it in `env.js`
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
),
// These are the reasonable defaults supported by the Node ecosystem.
// We also include JSX as a common component filename extension to support
// some tools, although we do not recommend using it, see:
// https://github.com/facebookincubator/create-react-app/issues/290
// `web` extension prefixes have been added for better support
// for React Native Web.
extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'],
alias: {
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web',
// Ol-Cesium
'goog': '../node_modules/olcs/goog',
},
plugins: [
// Prevents users from importing files from outside of src/ (or node_modules/).
// This often causes confusion because we only process files within src/ with babel.
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
// please link the files into your node_modules/ and let module-resolution kick in.
// Make sure your source files are compiled, as they will not be processed in any way.
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
],
},
In console show me this error:
./node_modules/olcs/AbstractSynchronizer.js
107:22-35 "export 'getUid' (imported as 'olBase') was not found in 'ol/index.js'
__stack_frame_overlay_proxy_console__ # index.js:2178
handleErrors # webpackHotDevClient.js:178
./node_modules/react-dev-utils/webpackHotDevClient.js.connection.onmessage # webpackHotDevClient.js:211
./node_modules/sockjs-client/lib/event/eventtarget.js.EventTarget.dispatchEvent # eventtarget.js:51
(anonymous) # main.js:274
./node_modules/sockjs-client/lib/main.js.SockJS._transportMessage # main.js:272
./node_modules/sockjs-client/lib/event/emitter.js.EventEmitter.emit # emitter.js:50
WebSocketTransport.ws.onmessage
In the Codesandbox provided by you, I was unable to find the root cause, but I noticed the following:
I noticed that you have used the ES6 import statement:import OLCesium from "olcs/OLCesium";.
But as per this issue, the module is not yet ported to ES6.
I have never used this library before, So it's hard to figure out what exactly is going on.
Did you try installing https://www.npmjs.com/package/geom ? Since the error says 4.6.4/geom/Point.js is missing.
I just started working with React last week, and I'm having trouble following a tutorial for the static-render-webpack-plugin.
I've put the code online at GitHub if you want to take a closer look.
After following the tutorial and making a couple of changes (I added babel-core, I changed the js loader to babel-loader and the entry point url needed a small correction), when I try to run webpack -p to generate the static files I get the following error:
ERROR in ./src/entry.js
Module build failed: SyntaxError: .../src/entry.js: Unexpected token (10:2)
8 |
9 | const routes = (
> 10 | <Route path="/" handler={RootPage}>
| ^
I think it might have something to do with the changes made with the latest version of react-router. I'm using the latest version, but the syntax for the tutorial looks like it might have been written prior to v.1.0. For example, I think the part of the tutorial that says to add this to the src/entry.js file:
if (typeof document != 'undefined') {
Router.run(routes, path, (Root) => {
React.render(<Root/>, document);
});
}
probably needs to be rewritten to something like this (but I'm not sure if this is quite right):
if (typeof document != 'undefined') {
ReactDOM.render(routes, document);
}
There's obviously more going on though since I get the same error message when I try that rewritten snippet then run webpack-dev-server -- which is the only time it should hit that code. (Yes, I added import ReactDOM from 'react-dom'; to the top of the page and "react-dom": "^0.14.7", to the package.json.)
I am sure this part (also on src/entry.js) needs to be rewritten to match the latest react-router too but I'm not sure how:
export default function(path, props, callback) {
Router.run(routes, path, (Root) => {
const html = React.renderToString(<Root/>);
callback('<!doctype html>' + html);
});
}
Thanks in advance for any help or hints you can give.
Your code is breaking because Webpack doesn't know how to transpile the JSX to ES5. You've specified babel-loader as your loader for JS files in your webpack config, but unfortunately Babel 6 does not do anything out of the box, you need to include "plugins" that contain the rules for compiling different syntaxes down to ES5. In this case, you'll want the es2015 preset to support all ES6 syntax, and the react preset to support JSX. You're also missing the extract-text-webpack-plugin you are trying to import into your webpack config. Snag these through NPM:
npm i -D babel-preset-2015 babel-preset-react extract-text-webpack-plugin
Then, add the presets to your webpack.config.js file in the loaders section for js/jsx files:
{
test: /\.(js|jsx)?$/,
loader: 'babel',
exclude: /node_modules/,
query: {
presets: ['es2015', 'react']
}
}
I forked your repo and made these changes and was able to get a bit further through the compilation process. It seems like there are module dependencies specific to your project you'll still need to resolve.
I'm trying to take React 0.14 for a spin before I upgrade it in my project. However, with a simple "hello world" prototype, Webpack is throwing an error:
ERROR in ./~/react/lib/ReactElement.js
Module parse failed: /home/dan/Demos/reactiflux/node_modules/babel-loader/index.js!/home/dan/Demos/reactiflux/node_modules/react/lib/ReactElement.js Line 25: Unexpected string
You may need an appropriate loader to handle this file type.
| // The Symbol used to tag the ReactElement type. If there is no native Symbol
| // nor polyfill, then a plain number is used for performance.
| var REACT_ELEMENT_TYPE = typeof _Symbol === 'function' && _Symbol.'for' && _Symbol.'for'('react.element') || 0xeac7;
|
| var RESERVED_PROPS = {
# ./~/react/lib/ReactMount.js 18:19-44
I do have babel-loader configured, and when I downgrade to React 0.13, everything works. What really stands out to me, is _Symbol.'for', in the middle of the error message.
In react/lib/ReactElement.js on line 21 (not 25), that line looks much more correct, with square brackets around the 'for' key:
var REACT_ELEMENT_TYPE = typeof Symbol === 'function' && Symbol['for'] && Symbol['for']('react.element') || 0xeac7;
I assume that the code shown in the error message is either in an intermediate state during compilation, or is the final compiled output. Does anyone know what could cause Webpack to produce something that looks so wrong? Has anyone successfully used Webpack, Babel and React ~0.14.1 together yet?
update
There is an issue for this: https://github.com/babel/babel/issues/2377
It's closed, but it looks like it came back for me. This was fixed in 5.8.25, but I have 5.8.29 and I still hit the bug.
It appears that the problem has something to do with me including babel runtime. My .babelrc was copied from an older project:
{
"optional": "runtime",
"stage": 0
}
In this little hello-world demo, there is nothing that requires bundling the runtime, so I just removed it, after noticing that https://github.com/DominicTobias/universal-react/, which also uses the same build tools, does not need it. That was the only change I needed to make to get this to build.
My webpack config is super simple:
var path = require("path");
module.exports = {
entry: "./index.js",
output: {
path: path.join(__dirname, "/dist"),
filename: "index.min.js"
},
module: {
loaders: [{
test: /\.js$/,
loader: "babel"
}]
}
};
I guess that's what I get for copying a config file from a more complex project into what was supposed to be a simplest possible demo.
I see that there is a babel-plugin-runtime as well as a babel-runtime on NPM, but when I tried out BPR for the sake of completeness, Babel complains: Module build failed: ReferenceError: The plugin "runtime" collides with another of the same name. Since I don't actually need the runtime, the linked GH repo is a 404, and since this really belongs in the issue trackers after all, this is as far as I am going to take this for now.
No, that is not valid code. That was an issue in Babel project, but it has been fixed in the 6.0 version which was released recently.
I was run into this issue too, and now I have checked this with latest version, and it is works fine. Here is my test steps:
# install Babel and plugins
npm install babel-cli babel-preset-es2015 babel-plugin-transform-runtime
# install React
npm install react
# run babel against problem react file
./node_modules/.bin/babel node_modules/react/lib/ReactElement.js --plugins transform-runtime --presets es2015
It is provides valid output, so the issue seems to be resolved.
And there is good news for you, babel-loader for webpack already supports 6 version of Babel. Check out its docs for details