Why does my Webpack bundle include jQuery twice? - javascript

I set up a parent module with 2 submodule dependencies. The parent module has no specified jQuery dependency, but each submodule specifies jQuery ^3.3.1 as a dependency (results in 3.4.1 for each submodule). I Webpacked the parent module and then I see in the generated bundle file that jQuery 3.4.1 is included twice. What should I be doing so that the same version of jQuery isn't included twice? I did try the splitchunks plugin and it did generate chunks but jQuery was still in there twice. I thought that Webpack is supposed to automatically analyze dependencies in the module graph and optimize the bundled code? I haven't yet tested NPM peer dependencies or the Webpack de-dupe plugin. I'm also wondering if there's something about jQuery itself to where Webpack can't/decides not to de-dupe automatically?
In both submodules' index.js files, I'm using:
import $ from "jquery"
In each submodule's package.json I specified:
"dependencies": {
"jquery" : "^3.3.1"
}
Then I did a npm install on each submodule.

module.exports = {
resolve: {
alias: {
// fix every jQuery to our direct jQuery dependency. Shariff 1.24.1 brings its own jQuery and it would be included twice without this alias.
'jquery': __dirname + '/node_modules/jquery/',
},
},
};
Note -happens because Shariff 1.24.1 defines jQuery as its own dependency instead of defining it as peer dependency in the package.json.
Refrence

I found that module resolution can automatically happen downwards (into child folders) of an entry point folder, but not upwards (in module folders outside of the entry point folder). I found that I needed to "tell" Webpack about these other module locations by adding a "resolve" object to Webpack.config:
resolve: {
modules: [
path.resolve(__dirname, "src/modules/pagetype/node_modules"),
path.resolve(__dirname, "src/modules/sitewide/node_modules"),
path.resolve(__dirname, "src/modules/template/node_modules"),
"node_modules"
]
}, // resolve

Related

Webpack Fabric external is resolving to undefined

I'm setting up a project (typescript, webpack) with a couple of js libraries, configured as externals in webpack. They should not be part of the bundle, instead provided by script tags within the html.
But when trying to use them in my class, they resolve to undefined.
Fabric configured as an external in webpack is resolving to undefined
An error occurs when trying to set up the fabric js library as an external within a (typescript + webpack ) project. Fabric should not be bundled in the output file since it will be the responsibility of the consumer to provide (eg. through a browser script tag).
Note: jQuery initially had an issue (as an external) but is now resolved, and works as expected. Fabric on the other hand does not.
fabric has been configured as an external so that it will not be included in the webpack bundle.
Here's how...
Added as an external within the webpack.config.js
...
externals: {
jquery: 'jQuery',
fabric: 'fabric',
},
...
Installed the declaration files for both libraries
npm install #types/jquery -D
npm install #types/fabric -D
Added the libraries in public folder and index.html (since they must not be part of the app bundle)
<script src="js/lib/jquery.min.js"></script>
<script src="js/lib/fabric.min.js"></script>
Created a class App.ts, imported and implemented instances of these two libraries. (see App.ts)
import { fabric } from "fabric";
import $ from 'jquery';
fabric resolves to undefined within the class App.ts with the error:
TypeError: Cannot read property 'Canvas' of undefined
Please don't recommend ProvidePlugin or installing Babel.
More about webpack "externals": https://webpack.js.org/configuration/externals/
Update #1
jQuery is now working as an external library. I was not referencing the actual jquery global "jQuery" in the externals setup. I had "JQuery" (with a capital J). That's now resolved and jquery is working. Thanks #Aluan
Fabric on the other hand seems to be a different issue altogether.
What you're looking for is called shimming. Webpack docs cover this extensively here: https://webpack.js.org/guides/shimming/
Edit to add example:
In your webpack.config.js plugins array:
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
})
]
EDIT:
I pulled down your code and got it working. Here are the steps:
ts-loader chokes on shims, so use babel's #babel/preset-typescript -- otherwise you'll need to find a way to tell the ts compiler to ignore them. This will get you started:
npm install --save-dev #babel/core #babel/cli #babel/preset-env #babel/preset-typescript core-js#3
In your root, create a file called .babelrc and add the following:
{
"presets": [
"#babel/preset-typescript",
[
"#babel/env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1"
},
"useBuiltIns": "usage",
"corejs": "3"
}
]
]
}
Add this to your webpack.config.js:
plugins: [
new ProvidePlugin({
$: "jquery",
jQuery: "jquery",
fabric: "fabric"
})
]
Also update ts-loader, changing it to babel-loader.
Now in your code, you'll need to prefix your shimmed libraries with window:
constructor(private readonly selector: string, canvasHeight: number, canvasWidth: number) {
window.$(`#${selector}`).replaceWith(`<canvas id="${selector}" height=${canvasHeight} width=${canvasWidth}> </canvas>`);
this.canvas = new window.fabric.Canvas(`${selector}`, { selection: false });
}
It turns out that the issue with fabric is from fabric itself! The reason fabric is resolving to undefined (when being configured as an external on webpack) is related to the way that fabric exposes its library for consumption. It's an issue they need to fix.
I've added an issue on the official fabric github page
But there is a quick solution for us. Just import using CommonJS like this:
const fabric = require('fabric');
Now it works!

How to make Webpack use project's "node_modules" in js scripts located outside the project folder?

I have Node.js project and the following structure of folders:
lib
awesome-formatter.js
FrontEndApp
prettify.js
node_modules
awesome-parser
BackEndApp
...
I use awesome-parser module and awesome-formatter.js library in prettify.js script like this:
require('awesome-parser')
require('../lib/awesome-formatter.js')
awesome-formatter.js, in turns, should use awesome-parser too:
require('awesome-parser')
My FrontEndApp has been configured to use Webpack, and I'm trying to run it in dev mode using npm run dev command. However, I got the following error:
ERROR Failed to compile with 1 errors
These dependencies were not found:
* awesome-parser in /home/user/dev/lib/awesome-formatter.js
I don't want to move awesome-formatter.js inside the FrontEndApp because I also use it in BackEndApp project (and probably in some other projects) and I don't want to create separate "node_modules" in "lib" for it just not to duplicate installed modules on disk.
So, my question is, how to make Webpack use project's "node_modules" in js scripts located outside the project folder?
P.S. Of course there are workarounds like symlinks or making a full-featured module (with package.json etc.) from lib/awesome-fromatter and installing it into FrontEndApp/node_modules, but is there a direct way to solve this problem?
I've found a solution: resolve.modules sould be added to Webpack configuration file.
module.exports = {
...
resolve: {
...
modules: [
'node_modules',
resolve('node_modules')
]
},
...
}
This means that Webpack is searching modules in 'node_modules' as a relative subfolder (and it's the usual behavior), and at the absolute path to the project's 'node_modules' as well: resolve('node_modules'), so that scripts in folders outside the project (like lib in my structure) can find and use it.

Why webpack is including node_modules of dependency?

I have two modules inside the same directory, managed both by lerna js. One of them is a library that others module includes. Both of them packed by webpack following webpack library authoring.
But, when I launch webpack in the app dir, the process includes all library/node_modules dependencies inside the app, for example vue.js. In library vue is "devDependency" while in the app is "dependencies". This implies two Vue context in navigator. Somebody known why?
Thanks.
You need to add an alias:
module.exports = {
...
....
},
resolve: {
modules: ["node_modules",
alias: {
'vue$': 'vue/dist/vue',
'jquery': 'jquery/dist/jquery.min.js'
}
},
...
Thanks to #evocateur
"Node resolves symlinks when requiring, which means libraries (like React and Vue) that are singletons will break. You need to add resolve.alias webpack config so it always chooses the "root" node_modules packages."
Putting in webpack following works perfectly in resolve.alias:
vue: path.resolve(__dirname, './node_modules/vue/')

Loading NPM module when using Bower Grunt Angularjs Build

I'm currently trying to integrate an npm module into an application that is built on Angularjs 1.4, Grunt, and Bower.
Require and Import do not work in the angualrjs framework which is the only way I can think of accessing the node_modules folder.
Does anyone have any idea how to use both npm and bower modules in the same application?
Here is a very trimmed down version of my app.js folder:
(function(angular) {
'use strict';
angular
.module('AppApp', [dependencies])
.constant('appconfig',{})
.config(function(...){
$statprovider.state{...}
.run(function($state){
$state.go('login);
})
})(angular);
I currently get all my dependencies through bower and access via index.html file. This does not seem to work if I write a script tag linking to the node_modules folder there.
USING MODULE INJECTION IN ANGULARJS
Accessing AngularJS modules from node_modules and bower_components is straight forward:
Let's take an example of angular-bootstrap from node_modules (Similar can be done from bower_components):
In HTML file, specify the reference of js file.
<script type="text/javascript" src="../node_modules/angular-bootstrap/ui-bootstrap-tpls.js"></script>
In your angular module, register the dependency as (You can check the module name on npm website or github while downloading or you can check it in the js files in node_modules/bower_components after downloading):
angular.module('AppApp',
[
'*ui.bootstrap*',
]);
However, you can also make Require and Import work in the AngularJS framework. This can be done using browserify or webpack that bundles your all the javascript files containing require/import into one to resolve the dependencies so that browser can understand require/import syntax, which is otherwise not understood by browsers.
USING BROWSERIFY
For using browserify when using grunt (app.js is the file containing require, you can specify other files in the array),
browserify: {
options: {
browserifyOptions: {
debug: true,
insertGlobalVars: []
},
transform: ['brfs' ]
},
app: {
files: {
['app.js']
}
}
}
node_modules dependencies required to use browserify are: brfs, grunt-browserify

webpack 1: resolve.alias not aliasing one npm module to another

I have a webpack config used to create a server bundle for an isomorphic React app. Inside of that config, I’m trying to use resolve.alias to alias one module (parse) as another (parse/node). This handy chart about resolve.alias in webpack's documentation makes it seem like this should be possible. This is what my alias object looks like:
alias: {
parse: 'parse/node',
pubnub: 'static/deps/pubnub/pubnub-3.13.0.min.js'
}
I’m already using alias for the module pubnub, and that alias works at intended. Unfortunately, whatever I do to the parse key doesn’t seem to change the resulting require in my webpack-built bundle.js:
/***/ function(module, exports) {
module.exports = require("parse");
/***/ },
I’m trying to resolve to /node_modules/parse/node.js. Using absolute/relative paths doesn’t seem to work, nor added a .js extension to the end. At this point, I can’t figure out if this is a webpack bug or something I’m overlooking. Any help would be greatly appreciated! Here’s a link to a gist with my full webpack config: https://gist.github.com/daleee/a0025f55885207c1a00a
node#5.7.0
npm#3.7.5
webpack#1.12.14
The issue was with the following line in the webpack configuration:
...,
externals: [nodeExternals({
whitelist: ['webpack/hot/poll?1000']
})],
...,
webpack-node-externals is a webpack plugin that is used to automatically blacklist modules found in node_modules. There is usually no need to bundle npm modules in with your final bundle when building for the backend, as the npm modules are installed on the system. This blacklist also prevented webpack from doing any aliasing to any npm modules. Changing the whitelist array like so solved my issue:
...,
externals: [nodeExternals({
whitelist: ['webpack/hot/poll?1000', 'parse']
})],
...,
The moral of this story: don't copy code from boilerplates without fully understanding what it does. I could have saved a few days of headache if I had simply just read the README.md for the webpack plugins I was using.

Categories

Resources