Having trouble with WebPack. I have a vendor library (saying ChildVendor), which implements requireJS and commonJS compatibility, so, I need to require it in my WebPack project like var lib = require('./childVendor');. This ChildVendor library has a dependency (saying SuperVendor), and both of them are requirejs- and commonjs-adapted, so, the heading of childVendor.js looks like:
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
define(["superVendor"], factory);
} else if (typeof exports === 'object') {
module.exports = factory(require('superVendor'));
} else {
root.Shepherd = factory(root.SuperVendor);
}
}(this, function(SuperVendor) { /*...*/ }));
The main problem is that I need to include that SuperVendor library globally on html-file manually (so, it is just initialized as window.SuperVendor), because it should be used by other third-party libraries.
To solve this, I have tried webpack.ProvidePlugin, like
plugins: [
new webpack.ProvidePlugin({
'superVendor': 'SuperVendor'
})
],
but an error is still the same (Module not found: Error: Can't resolve 'superVendor' in '...').
ProvidePlugin is not the solution for what you want to do. The configuration you've set with it tells Webpack:
Whenever superVendor is encountered as a free variable in a module, load the module SuperVendor and set the variable superVendor to what exported by SuperVendor.
In other words if you have a module that contains this:
superVendor.someMethod();
Webpack interprets it as if it were:
var superVendor = require("SuperVendor");
superVendor.someMethod();
What you should use it the externals configuration option:
externals: {
'superVendor': 'SuperVendor'
}
This tells Webpack that if the module superVendor is required it should be sought from the external environment with the name SuperVendor.
Related
I'm trying to import the bootstrap JS in my esbuild build:
// index.js
import 'jquery-import'
import 'bootstrap'
// jquery-import.js
import jquery from 'jquery'
window.jQuery = window.$ = jquery;
The build target is esm. The problem is that the following bootstrap code is run:
factory(exports, require('jquery'), require('popper.js'));
This does not use my global window.jQuery but rather requires its own, which means that my global window.$.fn doesn't include the Bootstrap extensions. Also, I think that this makes jQuery load twice.
Is there a way of fixing this without adapting the bootstrap source code?
A little more background on this:
The bootstrap source code begins with the following statements (lightly adapted for better readability):
(function (global, factory) {
if (typeof exports === 'object' && typeof module !== 'undefined') {
// Option 1: The route taken by esbuild
factory(exports, require('jquery'), require('popper.js'));
} else if (typeof define === 'function' && define.amd) {
// Option 2
define(['exports', 'jquery', 'popper.js'], factory);
} else {
// Option 2
(global = global || self, factory(global.bootstrap = {}, global.jQuery, global.Popper));
}
}(this, function (exports, $, Popper) { 'use strict';
I have tried commenting out individual options, and neither 1, 2 or 3 work by just taking the already globally defined window.jQuery.
Many thanks for your help!
The reason it's not working, is for some reason your esbuild is trying to import a UMD file (typically used for old Node and browser hybrid code).
Having looked at bootstrap's package.json, it seems that "module": "dist/js/bootstrap.js", which exports bootstrap as modules, which is probably what esbuild should be referencing.
So to fix this without fixing the esbuild config, I'd conjecture you could try:
import 'bootstrap/dist/js/bootstrap'
...or to fix it with esbuild config, add this:
const buildOpts = {
mainFields: ['module', 'main'],
}
This setting tells the bundler to prioritises module, then main entrypoints.
Normally this is module before main, but it may be the other way round because you set platform to "node".
I recently upgraded to Webpack 5 and I'm having some trouble importing UMD modules.
In particular, I'm trying to import Leaflet. Leaflet seems to export a UMD module created by rollup.js, which looks like this:
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.L = {})));
}(this, (function (exports) { 'use strict';
[...]
})));
As you can see, it first tries to use CommonJS (if exports and module is defined), then it tries to use AMD (if define is defined), and otherwise it tries to put itself to the global scope by referencing this from the module context.
Webpack (without any loaders) compiles it to this:
(function (global, factory) {
typeof exports === 'object' && "object" !== 'undefined' ? factory(exports) :
typeof define === 'function' && __webpack_require__.amdO ? define(['exports'], factory) :
(factory((global.L = {})));
}(undefined, (function (exports) { 'use strict';
[...]
})));
Concretely, what it did was:
Replace typeof module with "object"
Replace define.amd with __webpack_require__.amd0
Replace this with undefined
During the build, webpack gives me a warning: export 'default' (imported as 'L') was not found in 'leaflet' (possible exports: ). When I open the compiled bundle in the browser, I get the error Uncaught TypeError: Cannot set property 'L' of undefined.
What happens is that CommonJS fails (because exports is not defined), AMD fails (because define is not defined) and finally setting the global also fails because this is replaced with undefined.
According to the Webpack docs, Webpack should universally support importing CommonJS and AMD modules, but it seems in this case neither of the two work.
What can I do to fix this?
I narrowed the problem down and discovered that the problem was caused by the fact that I was using an imports-loader in a certain way.
Before the update, I used an imports-loader to add any needed transitive dependencies that my dependencies were not importing, in particular CSS files. The configuration looked something like this:
{ module: { rules: [ {
test: /\/node_modules\/leaflet\/.*\.js$/,
loader: "imports-loader?asdf=leaflet/dist/leaflet.css"
} ] } }
After the upgrade, webpack didn't accept the string syntax for loaders anymore, and imports-loader also changed its syntax, so I changed it to:
{ module: { rules: [ {
test: /\/node_modules\/leaflet\/.*\.js$/,
loader: "imports-loader",
options: {
imports: "default leaflet/dist/leaflet.css asdf"
}
} ] } }
This seemed to do the job and properly included the necessary CSS file. However, when I discovered that this part of the config is the cause of the problem, I dug deeper into the documentation of imports-loader. It turns out the configuration I was using was now generating the following line of code:
import asdf from "leaflet/dist/leaflet.css";
(and I could have used "side-effects leaflet/dist/leaflet.css" to get the same result without having to use asdf as a bogus import name). While this properly imported the CSS file, it probably caused webpack to think that this file is a proper ES module, disabling support for CommonJS/AMD/UMD modules. I changed the configuration of imports-loader to the following:
{ module: { rules: [ {
test: /\/node_modules\/leaflet\/.*\.js$/,
loader: "imports-loader",
options: {
imports: "pure leaflet/dist/leaflet.css",
type: "commonjs"
}
} ] } }
According to the docs, this creates the following code line:
require("leaflet/dist/leaflet.css");
After making this change, it seems to work without problems.
Interestingly, it seems that webpack actually detects the whole UMD block rather than just providing the exports and define variables, so it compiles the Leaflet code into this now:
(function (global, factory) {
true ? factory(exports) :
0;
}(this, (function (exports) { 'use strict';
[...]
})));
I'm using CodeMirror as a simple syntax highlighter (≠ code editor) with the runmode addon. In order to save some bandwidth to users, i want to switch to runmode-standlone.js version, which is also included in the package but doesn't bring the whole CodeMirror power and weight attached.
The problem, as referenced by the library author is that CodeMirror library is listed as dependency of each mode (the lang packages):
you have to somehow convince your bundler to load the runmode-standalone shim instead of the regular core library for that dependency. — Author
Anyone managed to do this?
What i've already tried
Note, i've setup an alias so when i reference codemirror, it imports the standalone version (just FYI, because i've also tried without it)
// webpack.config.js
resolve: {
alias: {
codemirror$: path.resolve(__dirname, "node_modules/codemirror/addon/runmode/runmode-standalone.js")
}
}
All combinations of script-loader, imports-loader to try to execute mode modules as normal scripts
import "codemirror";
// same as import "codemirror/addon/runmode/runmode-standalone.js";
// nope
import "script-loader!codemirror/mode/javascript/javascript";
// nope
import "imports-loader?define=>false!codemirror/mode/javascript/javascript";
// nope
import "imports-loader?define=>false,module=>false,require=>false!codemirror/mode/javascript/javascript";
A similar strategy to what's suggested for ignoring moment.js locales (repo, SO thread, using webpackIgnorePlugin
// codemirror/mode/javascript/javascript.js import looks like
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
...
So i've tried to make it ignore
new webpack.IgnorePlugin({
resourceRegExp: /^\.\.\/lib\/codemirror$/,
contextRegExp: /^codemirror\/mode\/(.*)/
});
Setup details
Using codemirror#5.47.0 and webpack#4.29.6
Configure webpack to use externals.
The externals configuration option provides a way of excluding dependencies from the output bundles. Instead, the created bundle relies on that dependency to be present in the consumer's (any end-user application) environment.
externals: {
'../lib/codemirror': 'CodeMirror', // for mode/meta.js
'../../lib/codemirror': 'CodeMirror' // for mode/[mode]/[mode].js
}
Lets say I'm writing a module in JavaScript which can be used on both the browser and the server (with Node). Lets call it Module. And lets say that that Module would benefit from methods in another module called Dependancy. Both of these modules have been written to be used by both the browser and the server, à la CommonJS style:
module.js
if (typeof module !== 'undefined' && module.exports)
module.exports = Module; /* server */
else
this.Module = Module; /* browser */
dependancy.js
if (typeof module !== 'undefined' && module.exports)
module.exports = Dependancy; /* server */
else
this.Dependancy = Dependancy; /* browser */
Obviously, Dependancy can be used straight-out-of-the-box in a browser. But if Module contains a var dependancy = require('dependency'); directive in it, it becomes more of a hassle to 'maintain' the module.
I know that I could perform a global check for Dependancy within Module, like this:
var dependancy = this.Dependancy || require('dependancy');
But that means my Module has two added requirements for browser installation:
the user must include the dependency.js file as a <script> in their document
and the user must make sure this script is loaded before module.js
Adding those two requirements throws the idea of an easy-going modular framework like CommonJS.
The other option for me is that I include a second, compiled script in my Module package with the dependency.js bundled using browserify. I then instruct users who are using the script in the browser to include this script, while server-side users use the un-bundled entry script outlined in the package.json. This is preferable to the first way, but it requires a pre-compilation process which I would have to run every time I changed the library (for example, before uploading to GitHub).
Is there any other way of doing this that I haven't thought of?
The two answers currently given are both very useful, and have helped me to arrive at my current solution. But, as per my comments, they don't quite satisfy my particular requirements of both portability vs ease-of-use (both for the client and the module maintainer).
What I found, in the end, was a particular flag in the browserify command line interface that can bundle the modules and expose them as global variables AND be used within RequireJS (if needed). Browserify (and others) call this Universal Module Definition (UMD). Some more about that here.
By passing the --standalone flag in a browserify command, I can set my module up for UMD easily.
So...
Here's the package.js for Module:
{
"name": "module",
"version": "0.0.1",
"description": "My module that requires another module (dependancy)",
"main": "index.js",
"scripts": {
"bundle": "browserify --standalone module index.js > module.js"
},
"author": "shennan",
"devDependencies": {
"dependancy": "*",
"browserify": "*"
}
}
So, when at the root of my module, I can run this in the command line:
$ npm run-script bundle
Which bundles up the dependancies into one file, and exposes them as per the UMD methodology. This means I can bootstrap the module in three different ways:
NodeJS
var Module = require('module');
/* use Module */
Browser Vanilla
<script src="module.js"></script>
<script>
var Module = module;
/* use Module */
</script>
Browser with RequireJS
<script src="require.js"></script>
<script>
requirejs(['module.js'], function (Module) {
/* use Module */
});
</script>
Thanks again for everybody's input. All of the answers are valid and I encourage everyone to try them all as different use-cases will require different solutions.
Of course you could use the same module with dependency on both sides. You just need to specify it better. This is the way I use:
(function (name, definition){
if (typeof define === 'function'){ // AMD
define(definition);
} else if (typeof module !== 'undefined' && module.exports) { // Node.js
module.exports = definition();
} else { // Browser
var theModule = definition(), global = this, old = global[name];
theModule.noConflict = function () {
global[name] = old;
return theModule;
};
global[name] = theModule;
}
})('Dependency', function () {
// return the module's API
return {
'key': 'value'
};
});
This is just a very basic sample - you can return function, instantiate function or do whatever you like. In my case I'm returning an object.
Now let's say this is the Dependency class. Your Module class should look pretty much the same, but it should have a dependency to Dependency like:
function (require, exports, module) {
var dependency = require('Dependency');
}
In RequireJS this is called Simplified CommonJS Wrapper: http://requirejs.org/docs/api.html#cjsmodule
Because there is a require statement at the beginning of your code, it will be matched as a dependency and therefore it will either be lazy loaded or if you optimize it - marked as a dependency early on (it will convert define(definition) to define(['Dependency'], definition) automatically).
The only problem here is to keep the same path to the files. Keep in mind that nested requires (if-else) won't work in Require (read the docs), so I had to do something like:
var dependency;
try {
dependency = require('./Dependency'); // node module in the same folder
} catch(err) { // it's not node
try {
dependency = require('Dependency'); // requirejs
} catch(err) { }
}
This worked perfectly for me. It's a bit tricky with all those paths, but at the end of the day, you get your two separate modules in different files which can be used on both ends without any kind of checks or hacks - they have all their dependencies are work like a charm :)
Take a look at webpack bundler.
You can write module and export it via module exports. Then You can in server use that where you have module.export and for browser build with webpack. Configuration file usage would be the best option
module.exports = {
entry: "./myModule",
output: {
path: "dist",
filename: "myModule.js",
library: "myModule",
libraryTarget: "var"
}
};
This will take myModule and export it to myModule.js file. Inside module will be assigned to var (libraryTarget flag) named myModule (library flag).
It can be exported as commonJS module, war, this, function
Since bundling is node script, this flag values can be grammatically set.
Take a look at externals flag. It is used if you want to have special behavior for some dependencies. For example you are creating react component and in your module you want to require it but not when you are bundling for web because it already is there.
Hope it is what you are looking for.
I'm struggling to add a plugin using the following AMD compatability setup to my application:
The snippet from foo.js in question:
(function (dependencies, module) {
"use strict";
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
if (typeof exports === 'object') {
return module(exports);
}
window.foo = {};
module(window.foo, {hex_sha256: hex_sha256});
}(['exports', 'sha256'], function (exports, sha256) {
// foo
}));
I'm setting foo as a dependency in another module called bar like so:
define(["jquery", "foo", "sha256", "c", "d"], function() {
// stuff
});
And inside the r.js optimizer I define bar to be:
{
name: "bar"
, include: ["foo", "sha256", "c", "d"]
, exclude: ["jquery"]
},
This generates the bar.js file allright including the abovementioned files.
However when I load my built application, there are still two requests being triggered to foo.js and sha256.js, which both 404 (optimizer cleans up built files) and who are inside my bar.js build layer.
Question:
I'm a little lost with the module amd check and am suspecting it being responsible for the unwanted call. Can anyone shed some light on what I should modify in the AMD check to make foo callable form inside a build layer?
Thanks!
EDIT:
I tried shimming like so:
shim: {
'foo': { deps: ['sha256'] }
},
which takes care of the 404, but returns an:
Mismatched anonymous define() module: function (exports, sha256) {....
error, so I'm still stuck assuming the hardcoded dependency to sha256 in my AMD check to be the culprit. Maybe this helps.
EDIT:
I'm pretty sure my problems stem from the dependency declaration inside the AMD compatability check.
The two solutions below require modifications to the source of foo. Since you can control it, they are both viable:
(Quick and dirty) Hardcode the module name in the define call:
// foo.js
if (typeof define === 'function' && define.amd) {
return define("foo", dependencies, module);
}
(Cleaner) See the code of Knockout.js at the beginning. I am using it in a project and it seems to compile good with r.js. I tried a simple project setup like yours, and it works; so you have to replace the AMD compatibility code as follows:
(function(factory) {
// Support three module loading scenarios
if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') {
// [1] CommonJS/Node.js
var target = module['exports'] || exports; // module.exports is for Node.js
var hex_sha256 = require("sha256");
factory(target, hex_sha256);
} else if (typeof define === 'function' && define['amd']) {
// [2] AMD anonymous module
define(['exports','sha256'], factory);
} else {
// [3] No module loader (plain <script> tag) - put directly in global namespace
factory(window['foo'] = {}, hex_sha256);
}
}(function(exports, sha256){
// same foo
}));
I do not know what magic happens inside r.js and this works...