Webpack - How to reuse global library name when it already exists? - javascript

Good evening!
I have this monorepo with multiple packages, where each of them is bundled independently using Webpack.
Before it was a monorepo, I would have a single bundle file, and it would be possible to have it available through a global variable within the browser through output.library property. Now I have the following since I have multiple entries:
output: {
library: "SC",
// export itself to UMD format
libraryTarget: "umd",
umdNamedDefine: true,
filename: "[name]/dist/organization-[name].js",
// fix for https://github.com/webpack/webpack/issues/6525
globalObject: `(typeof self !== 'undefined' ? self : this)`
}
The problem is that if I use this same config for every package, and I import more than one to the browser using script tags, only the latest script will actually be available because it's essentially recreating the global variable each time.
Is there a way to reuse it? Or maybe a better convention I could use here.
In node, for instance, I import each of them using the bundle name, but in the browser, I feel like it should all be under the same global variable.
Thank you for any suggestions!

As mentioned in the issue I created over webpack's repository, the solution is to use the following:
library: ["MyLibrary", "[name]"]
That will make all packages available under the same global variable MyLibrary but separated by their respective entry (i.e., MyLibrary.entryOne and MyLibrary.entryTwo).

Related

How to reserve a global variable name in with vite or rollup? (window.FB)

I'm trying to setup "login with facebook" functionality in my application.
Everything works great locally or if I don't minify my bundle.
However when I minify my bundle output there is a global function that happens to be minified to FB (window.FB), which completely breaks the facebook SDK.
Facebook doesn't provide any way to use any other variable name. So I'm trying to figure out how to "reserve" or prevent my bundler viteJS / Rollup minifying this other global function name to FB.
The global function that's getting minified comes from nodejs and is likely being imported through some npm package but I am not sure which one. (even if I could find it, I'm using it for a reason and there might not be a good alternative).
This is the random nodejs function that's being saved on the global window and minified to be called "FB": https://github.com/nodejs/node/blob/4b6e4c1eb110e0be671ec5972bf280d2bf3892d8/lib/_stream_readable.js#L497
How can I prevent the window.FB global variable name to be taken up by this function? (with minify turned on of course).
I have tried setting build.outputs.globals.FB to "TEST", and defining window.FB in my index.ts file to see if it would force it to not use the same name for minifcation. But it doesn't seem to affect the bundler.
While working on a custom library that should be used on another website I've managed to achieve this disabling minification In vite.config.js file :
export default defineConfig({
plugins: [
// your plugins goes here...
],
build: {
minify: false, // <-- this is the important part
lib: {
entry: resolve(__dirname, 'src/index.tsx'),
name: 'my-lib',
fileName: 'lib-file',
formats: ['umd'] // <-- I used 'umd' here as a precaution measure, to make it possible to use the lib in every browser
}
}
});

How to make webpack not use the window object when bundling?

I'm making a React component library to abstract out some components I use in multiple projects. Some projects are made with CRA, some with Gatsby, some might be something else, etc. I used the Neutrino.js framework/toolchain as it was linked on the React docs site, but the issue I ran into is that by default the output files of the build all use the window object, which causes gatsby build to break as window doesn't exist in Node/SSR. Is there a way to make Neutrino/webpack output a bundle that doesn't use window? While searching for a solution and comparing to other libraries it seems that ESM is the best but I'm not sure how to get webpack to use it, I think it's currently not supported. Is there another tool I should be using for this?
Add globalObject configuration to your webpack configuration:
output: {
globalObject: "this",
},
The default is window
For example:
To make UMD build available on both browsers and Node.js, set output.globalObject option to 'this'.
module.exports = {
// ...
output: {
library: 'myLib',
libraryTarget: 'umd',
filename: 'myLib.js',
globalObject: 'this'
}
};
-From docs

Access webpack modules / variables from global scope

I have my react application compiled through from webpack. I'm using google recaptca's callback url like so:
<script defer src='https://www.google.com/recaptcha/api.js?render=explicit&onload=mywebpackfn'></script>
mywebpackfn is defined inside my webpack compiled js file. api.js can't find it. How can I get access to the webpack js scope from outside?
You could simply expose your function to global scope. Inside your code
if( typeof window !== 'undefined' ) { // browser env
window.mywebpackfn = yourFunction
}
Or something fancy to access global scope
// I know kung fu
(new Function('return this')()).mywebpackfn = yourFunction
Also you might want to compile your code as library https://webpack.js.org/configuration/output/#output-library
Webpack config
output: {
..
library: 'mywebpackfn',
libraryTarget: 'window'
}
If you are familiar with nodejs then you can create your node server outside of that webpack and api.js. In such a way that node server will be common to both webpack and api.js.
In that way you can export your file from webpack that will still be required by api.js by that nodejs server.
Hope this will help.
If you want to keep things controlled via webpack, rather than assign to window directly in your codebase, you can use the expose-loader
https://github.com/webpack-contrib/expose-loader
Or you can specify the output.library option of webpack which will expose the exports of your entry file as an object in global scope - you could export your callback from there

Confusion over various webpack shimming approaches

I'm a little confused on the various ways webpack allows to expose a variable that isn't available on npm or to be put in the bundle. I was able to expose the google visualizations chart script's global google var by using
resolve: {
extensions: ['.js', '.json'],
alias: {
'google': path.resolve(__dirname, 'vendor', 'google.js')
}
}
combined with
plugins: [
new webpack.ProvidePlugin({
'google': 'google'
})
]
however looking at the webpack docs there a couple other ways to shim, which look like they might do something similar. There is imports-loader and exports-loader, and script-loader. I know that I've linked to the docs, but I still find their descriptions of when these four should be used a bit unclear.
Also looking at this example, is this require not assigned to a variable? Where is it meant to go? And where is the documentation on what is going on with this syntax?
require("imports?$=jquery!./file.js")
Can someone provide me some examples of when each of these should be used?
scripts-loader
I never used this myself, but the idea is simple I guess. I think it can be used if for some reason you want to inject a script or a function or something in one of the modules/files you have no control over them.
imports-loader & exports-loader
In one of the apps I worked on we had to use tinymce which in its older versions was dependent on this being always window because it was built to work as a global script. Not as a CommonJS or ES module.
So in order to fix that, we had to use the import-loader so it can inject window to the script. Here how it looked like in webpack.config.js
{ test: require.resolve('tinymce/tinymce'), use: ['imports?this=>window', 'exports?tinymce'] }
Which says inject window in place of this & also we are using exports-loader here so we can export the global tinymce as a default export named tinymce so we can use it as a normal module in our app.
Thankfully all of this is already fixed in the latest releases.
ProvidePlugin
In my experience, this is useful when a library is depending on another library being in a global scope or something. Like jQuery plugins for example, they do use one of these $, window.$, jQuery & window.jQuery
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.$': 'jquery',
'window.jQuery': 'jquery',
}),
So what this plugin will do is to make sure when webpack sees one of these variations it will provide the jQuery object to it instead.
The difference between this & imports-loader for example that you might not know which variation is used by which script. So you let webpack handle this while the imports-loader is kind of more specific.
I hope this helped you a bit to understand the differences between all of them, also this is the new documentation page which I think better than the one you were checking https://webpack.js.org/guides/shimming/
imports and exports loaders are very simple to understand. If you use one of them, or both, your module is wrapped into another function with exports and imports.
For example, I'm using paho-mqtt module meant to be used like global <script src=""> on the page:
import Paho from 'imports-loader?this=>window!exports-loader?Paho!paho-mqtt';
//and this is transformed by webpack to something like:
(function(window){
//wow you can use `window here`, `this` in the global context === window.
// original module code here
// that exposes global var `Paho`
module.exports = Paho;
})(this);

WebPack 2: Replaced require'd module with global

Working on a set of scripts that will run in a browser context where certain modules (e.g. underscore) will be available as global modules. However, I'm depending on modules in node_modules that require / import underscore directly. Is it possible to configure WebPack to depend on the global underscore instance when compiling these files instead of duplicating that library in my compiled scripts?
What you're looking for are Externals:
externals configuration in webpack provides a way of not including a dependency in the bundle. Instead the created bundle relies on that dependency to be present in the consumers environment. This typically applies to library developers though application developers can make good use of this feature too.
This even works for modules in node_modules, as webpack walks the entire dependency tree to figure out what to include in the resulting bundle.
There's even an example that's specifically for your use case, it looks like:
externals : {
lodash : {
commonjs: "lodash",
amd: "lodash",
root: "_" // indicates global variable
}
}
This syntax is used to describe all the possible ways that an external library can be available. lodash here is available as lodash under AMD and CommonJS module systems but available as _ in a global variable form.
If you want to rely on a library already being available in the environment when your bundle is loaded, you need to make use of externals.
module.exports = {
externals: {
underscore: "_"
}
}
The key of the object (underscore) is what you use to import it, and the value (_) is the global variable it will look for.
require("underscore"); // Will return the _ variable from the global environment!

Categories

Resources