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

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

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
}
}
});

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

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).

How to create a library that can have individual components imported if required à la lodash

I've written a library in es6 using import/export. I can bundle this library using Rollup into a IIFE that can be used in the browser.
I also want to be able to use this library in other projects. However, I won't usually want to include the whole library, only parts of it.
Because the library is written using es6 import/export I can include the unbundled index.js file as a dependency in another project and then import { myFunc } from 'my-lib' and this works great - I only get myFunc when my project is bundled.
However, I ran into an issue because these files haven't been processed by babel and therefore contain es6 code such as arrow functions. I have read that if you're going to publish a library it shouldn't need to be transpiled by the end-user.
How can I take my es6 library and bundle it in such a way that it is transpiled, but also able to have its individual components imported? I want a similar situation to how lodash is organised.
This isn't the easiest thing to articulate so if anything isn't clear please leave a comment and I'll edit my question to try and clarify.
I have found it is best to use webpack to create 2 modules. One for web and one for node.
In your node build, you can choose to transpile, or simply keep the code in ES6, letting the library client decide.
Your web build can tree-shake and remove unused code.
Your webpack will export an array instead of an object.
Here is the official webpack snippet (https://webpack.js.org/concepts/targets/)
const path = require('path');
const serverConfig = {
target: 'node',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'lib.node.js'
}
//…
};
const clientConfig = {
target: 'web', // <=== can be omitted as default is 'web'
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'lib.js'
}
//…
};
module.exports = [ serverConfig, clientConfig ];

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);

How to bundle a library with webpack?

I want to create a frontend library.
Therefore I want to use webpack. I especially like the css and image loader.
However I can only require non-JS files if I am using webpack.
Because I am building a library, I cannot garanty that the user of my library will too.
Is there I way to bundle everything into a UMD module to publish it?
I tried using multiple entry points, however I cannot require the module then.
You can find good guide for creating libraries in Webpack 2.0 documentation site. That's why I use ver 2 syntax in webpack.config.js for this example.
Here is a Github repo with an example library.
It builds all files from src/ (js, png and css) into one JS bundle which could be simply required as an umd module.
for that we need to specify the follow settings in webpack.config.js:
output: {
path: './dist',
filename: 'libpack.js',
library: 'libpack',
libraryTarget:'umd'
},
and package.json should have:
"main": "dist/libpack.js",
Note that you need to use appropriate loaders to pack everything in one file. e.g. base64-image-loader instead of file-loader
The comment written by #OlegPro is very helpful. I suggest every one to read this article for explanation of how these stuff work
http://krasimirtsonev.com/blog/article/javascript-library-starter-using-webpack-es6
You need the following for sure if you want to be able to import the bundle file in your project
output: {
path: path.resolve(__dirname, myLibrary),
filename: 'bundle.js',
library: "myLibrary", // Important
libraryTarget: 'umd', // Important
umdNamedDefine: true // Important
},

Categories

Resources