How do I prevent webpack from bundling react?
Currently I am writing a library that causes a You've loaded two copies of React on the page. error after distribution. I suspect that webpack starts bundling all dependencies, including devDependencies.
Is there any way around that?
In my case it should be possible for the library to get React out of node_modules.
So what I basicly want is, instead of webpack resolving the require('React), it should just leave require('React) untouched.
You can use webpack externals.
externals: {
// Use external version of React
"react": "React"
}
UPD Detailed docs on the resulting code generated for externals.
To make webpack "leave require('React) untouched" you need the following config
{
output: { libraryTarget: 'commonjs' },
externals: { react: true }
...
}
Moelalez, just like Yury Tarabanko stated, externals option allows you import an existing API into applications. For context, say you want to use React from a CDN via a separate tag and still declare it as a dependency via require("react") in your application, you would use externals option to specify that.
Related
I am trying to do this as simple as possible, I studied Yarn Workspaces for a while, but that's a solution that's currently doesn't work with Electron, there were simply too many issues.
I have am Electron project here: ./electron/
I have a directory with components here: ./common/
The components are developed in React/JSX, there is nothing really fancy about them. That said, I am using hooks (useXXX).
I tried many ways to include those components (ideally, I wanted to use Yarn Workspaces, but it only multiplied the number of issues), but they all failed. Which is why I would like to avoid using yarn link or workspaces or making the common a library, etc. I just want my Electron project to behave as if the files were under ./electron. That's it.
The closest I came to a solution is by using electron-webpack, and overriding it with this config:
module.exports = function(config) {
config = merge.smart(config, {
module: {
rules: [
{
test: /\.jsx?$/,
//include: /node_modules/,
include: Path.resolve(__dirname, '../common'),
loaders: ['react-hot-loader/webpack', 'babel-loader?presets[]=#babel/preset-react']
},
]
},
resolve: {
alias: {
'#common': Path.resolve(__dirname, '../common')
}
}
})
return config
}
I can import modules, and they work... except if I use hooks. And I am getting the "Invalid Hook Call Warning": https://reactjs.org/warnings/invalid-hook-call-warning.html.
I feel like that /common folder is not being compiled properly by babel, but the reality is that I have no idea where to look or what to try. I guess there is a solution for this, through that webpack config.
Thanks in advance for your help :)
I found the solution. That happens because the instance of React is different between /common and /electron.
The idea is to add an alias, like this:
'react': Path.resolve('./node_modules/react')
Of course, the same can be done for other modules which need to be exactly on the same instance. Don't hesitate to comment this if this answer it not perfectly right.
I wrestled more than a day with a similar problem. My project has a dependency on a module A that is itself bundled by Webpack (one that I authored myself). I externalised React from A (declaring it to be a commonjs2 module). This will exclude the React files from the library bundle.
My main program, running in the Electron Renderer process, uses React as well. I had Webpack include React into the bundle (no special configuration).
However, this produced the 'hooks' problem because of two instances of React in the runtime environment.
This is caused by these facts:
module A 'requires' React and this is resolved by the module system of Electron. So Electron takes React from node_modules;
the main program relies on the Webpack runtime to 'load' React from the bundle itself.
both Electron and the Webpack runtime have their own module cache...
My solution was to externalise React from the main program as well. This way, both the main program and module A get their React from Electron - a single instance in memory.
I tried any number of aliases, but that does not solve the problem as an alias only gives direction to the question of where to find the module code. It does nothing with respect to the problem of multiple module caches!
If you run into this problem with a module that you cannot control, find out if and how React is externalised. If it is not externalised, I think you cannot solve this problem in the context of Electron. If it is externalised as a global, put React into your .html file and make your main program depend on that as well.
So I'm trying to install and use react-wavesurfer in Meteor which is a react component wrapper for an existing js library (wavesurfer.js). It requires that the wavesurfer.js file (which has to be installed separately) is made available as a global variable.
The suggestion is to use webpack for this as below :
// provide WaveSurfer as a globally accessible variable
plugins: [
new webpack.ProvidePlugin({
WaveSurfer: 'wavesurfer.js'
})
],
// Alias `wavesurfer` to the correct wavesurfer package.
// (wavesurfer.js has some non-standard naming convention)
resolve: {
alias: {
wavesurfer: require.resolve('wavesurfer.js')
}
},
I've never used Webpack (not entirely sure what it does) and I'm a Meteor / React newbie.
So I installed :
meteor add webpack:webpack
And now I'm getting the following error in the console :
While determining active plugins:
error: conflict: two packages included in the app (webpack:webpack and ecmascript) are both trying to handle *.js
error: conflict: two packages included in the app (webpack:webpack and ecmascript) are both trying to handle *.jsx
From the error I assume that ecmascript (again something I know nothing about) is doing a similar job to webpack already which is causing the clash?
So, my question... How do I set this up using ecmascript instead? I literally have no idea!
Try to use new package that I still maintain
https://github.com/ardatan/meteor-webpack
I have created standalone bundle which I include in React App. The App itself does not anything know about Extension Bundle (next XB) it load, but the XB does know about App and libraries it use inside. How to make App share library packages it compiled with to XB? I mean if the App compiled with React and React-dom libraries to make XB exclude from compile with such libraries and use them from the App instead?
I put into webpack config to exclude React from compiling:
var config = {
//....
output: {
library: 'videoEditor',
libraryTarget: 'umd',
path: BUILD_PATH+"./../bundles/",
filename: '[name].bundle.js'
},
// ...
}
// .... later just extending config if compiling as standalone
config.externals = Object.assign(config.externals,{'react':'React'});
Yes, that gives me on exit the Bundle without React library (so it size reduces from 190KB to 10KB). But if I try to load it I get an error:
index.js?7afc:1 Uncaught ReferenceError: require is not defined
at eval (index.js?7afc:1)
at Object.<anonymous> (video-editor.bundle.js:80)
at __webpack_require__ (video-editor.bundle.js:30)
at video-editor.bundle.js:73
at video-editor.bundle.js:76
at webpackUniversalModuleDefinition (video-editor.bundle.js:9)
at video-editor.bundle.js:10
Without externals all is working ok.
index.js?7afc:1 is
import React from 'react';
import AppLayout from 'layouts/app';
There inside import React from 'react'; is coming as module.exports = require('react');
I do not understand how to make require work. Seems it is invisible for global space.
update: I have tried to change libraryTarget on any available option: 'umd', 'commonjs', 'this'. The result is the same.
I know about commonChunkPlugin from webpack but I have several separated projects from different sources they compile by different people.
Along with React, you also need to add ReactDOM in your externals from React v0.14.x onwards
externals: {
// Use external version of React
"react": "React",
"react-dom": "ReactDOM"
},
I am using react-treelist component and inside that i am using office-ui-fabric callout(its like a tooltip) component to have a tooltip on elements in tree structure when i am running this program it runs correctly but when i am bundling and using the bundle file in my project as a module it gives error:-
Exception in Layer.componentDidUpdate(): Invariant Violation: addComponentAsRefTo(...): Only a ReactOwner can have refs. You might be adding a ref to a component that was not created inside a component's render method, or you have multiple copies of React loaded
I have tried many solutions from diff forum but none of them worked.I am sure problem is of multiple copies of react.
Details of what i did
I cloned react-treelist from github then i installed office-ui-fabric module then i changed some code to make callout component work with react-treelist, i did npm start it worked fine the i did npm run dist and copied the bundle file to my project directory inside a folder named react-treelist and then imported that file as module in my code and ran the code it gave me the above error.
Can anybody suggest me how to accomplish the above task without having multiple copies of react in my application
If you use webpack to build your application, you can use the resolve.alias configuration option to make sure that all modules refer to the same react library:
resolve: {
alias: {
'react': fs.realpathSync('node_modules/react'),
'react-dom': fs.realpathSync('node_modules/react-dom')
}
}
If you use web pack then you can fix it by adding the following to Webpack config
externals: {
'react': 'React',
'react-dom': 'ReactDOM'
}
I have spent a lot of time looking into this, but to no avail. I am aware of how code splitting and dynamic bundling works in Webpack using the import promise API.
Howevr, my use case is that I have two completely separate bundles, generated separately using different webpack builds. To give you perspective, I am building React components and there is a requirement to dynamically load a react component into the page that has been compiled in a different process. Is this possible in react? I do have control over both webpack builds, so I can exclude dependencies, etc.
Update: I just looked at Vue.js, and how it allows developers to register Vue.js components and then reference them later in the code. I could potentially load my Vue.js component scripts before my page script. I'm trying to see if I can do something similar in React.
Did I understand you correctly: you have essentially got
a library of custom React components (built by Webpack build #1)
a React app that needs to use some (all) of these components (built by Webpack build #2, totally separate from #1)
?
If yes, then read on.
The "Is this possible in react?" question should instead be "Is this possible in Webpack?", and the answer is "Yes". The following is tested with Webpack 2, but should also work with v.1.
Let's call your projects Lib (your React component library) and App (the library consumer).
In the Lib project:
Create an entry point file, say index.js, that exports all the custom React components like this:
import {Button} from './button';
import {DatePicker} from './DatePicker';
import {TextBox} from './textBox';
export const MyComponentLib = {
Button,
DatePicker,
TextBox
};
Update webpack.config.js to make the project's bundle a UMD library (could also be 'var'), and set the entry point to the above index.js file. Doing so will make your library available via a global variable named MyComponentLib (the name comes from the export above) in the consuming app later on:
...
output: {
path: './dist',
filename: 'mylib.bundle.js',
libraryTarget: 'umd'
},
...
entry: './index.js',
...
On to the App project:
In the index.html file you will have two <script> tags: one for mylib.bundle.js (the output of the Lib project), and another for the bundle of the App project itself. You might have more bundles (app, vendor etc.), I'm just simplifying things here.
Update webpack.config.js to mark the component library as external dependency. Here, MyComponentLib is, again, the name of the global variable the library is available at, and myComponents is the name to use in import statements:
...
externals: {
myComponents: 'MyComponentLib'
},
...
Now, in App you can import a component like this:
import {DatePicker} from 'myComponents';
This will dynamically load DatePicker from the component library at run time via the global variable.
Bonus: if you use eslint, you don't want it to complain about missing modules that you know are external; add this to your .eslintrc:
...
"settings": {
"import/core-modules": ["myComponents"]
},
...