Why is webpack not exposing jQuery as a global? - javascript

My project requires a few npm modules, which I'm using yarn and webpack to load in. Namely, they are jquery, bootstrap3, moment, jquery-tablesort, jquery-ujs, bootstrap-select, and livestamp.
Some of those plugins need jQuery to be exposed as the global $ object. I've tried to make webpack do that, but so far I've failed.
My webpack config currently looks like this:
module.exports = {
entry: packPaths.reduce(
(map, entry) => {
const localMap = map;
const namespace = relative(join(entryPath), dirname(entry));
const key = join(namespace, basename(entry, extname(entry)));
localMap[key] = [resolve(entry)];
if (entry.includes('vendor')) {
localMap[key].unshift('babel-polyfill');
}
return localMap;
}, {}
),
output: {
filename: '[name].js',
path: output.path,
publicPath: output.publicPath
},
module: {
rules: [{
test: require.resolve('jquery'),
use: [{
loader: 'expose-loader',
options: '$'
}]
}, ...sync(join(loadersDir, '*.js')).map(loader => require(loader))]
},
plugins: [
new webpack.EnvironmentPlugin(JSON.parse(JSON.stringify(env))),
new ExtractTextPlugin(env.NODE_ENV === 'production' ? '[name]-[hash].css' : '[name].css'),
new ManifestPlugin({
publicPath: output.publicPath,
writeToFileEmit: true
})
],
resolve: {
extensions: settings.extensions,
modules: [
resolve(settings.source_path),
'node_modules'
]
},
resolveLoader: {
modules: ['node_modules']
}
};
On the advice in this question, I added the module section for expose-loader and removed a previous ProvidePlugin section that I had tried; neither of these options have worked.
My scripts are included like so (in application.html.erb):
<%= javascript_pack_tag 'vendor.js', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag "//code.highcharts.com/highcharts.js", "chartkick" %>
There are no other scripts included before or after these three. In vendor.js:
import * as $ from 'jquery';
window.$ = window.jQuery = $;
import 'bootstrap3'
import 'moment'
import 'jquery-tablesort';
import 'jquery-ujs';
import 'bootstrap-select';
import 'livestamp';
The manual assignment to window.$ also doesn't work. When Bootstrap loads, it gives me this error:
transition.js:59 Uncaught ReferenceError: jQuery is not defined
at Object.<anonymous> (transition.js:59)
at __webpack_require__ (bootstrap bde90632505934e68af9:19)
at Object.module.exports (npm.js:2)
at __webpack_require__ (bootstrap bde90632505934e68af9:19)
at Object.<anonymous> (vendor.js:1)
at __webpack_require__ (bootstrap bde90632505934e68af9:19)
at Object.<anonymous> (runtime.js:736)
at __webpack_require__ (bootstrap bde90632505934e68af9:19)
at bootstrap bde90632505934e68af9:65
at vendor.js:69
(Note the vendor.js in the last line of the trace there is the compiled vendor.js, i.e. not the one included above but the Webpack result.)
What can I do to fix this?

I think in this situation you should use require instead import
Try this in your vendor.js:
window.$ = window.jQuery = require('jquery');
require('bootstrap');
require('moment');
// others...

Related

Vue 3 - How to add Polyfills to ChainWebpack

Using Vue 3, how do I add path-browserify to vue.config.js?
module.exports = {
chainWebpack: config => {}
}
I am receiving the following error when compiling:
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.
If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "path": require.resolve("path-browserify") }'
- install 'path-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "path": false }
Webpack 5 removed some things that Webpack 4 included in the bundle.
To get it all back in a vue3 app you can use the polyfill plugin.
From a vanilla create-vue-app with babel:
> npm i node-polyfill-webpack-plugin
babel.config.js
module.exports = {
presets: [
'#vue/cli-plugin-babel/preset'
]
}
vue.config.js
const { defineConfig } = require("#vue/cli-service");
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
module.exports = defineConfig({
transpileDependencies: true,
configureWebpack: {
plugins: [new NodePolyfillPlugin()],
optimization: {
splitChunks: {
chunks: "all",
},
},
},
});
With #Zack's help, using chainWebpack:
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin')
module.exports = {
chainWebpack: config => {
config.plugin('polyfills').use(NodePolyfillPlugin)
},
}

Webpack: "Cannot read property 'fn' of undefined" when importing owl.carousel [duplicate]

I am new to the F2E world.
I just created a web application using create-react-app. (https://github.com/facebookincubator/create-react-app)
I wanted to import owl.carousel into my projects, so that I followed the guide of NPM (https://www.npmjs.com/package/owl.carousel) ,which of the syntax is:
import $ from 'jquery';
import 'imports?jQuery=jquery!owl.carousel';
but the debugger console indicated the error :
Unexpected '!' in 'imports?jQuery=jquery!owl.carousel'. Do not use import syntax to configure webpack loaders import/no-webpack-loader-syntax
I tried another syntax:
import owlCarousel from 'owl.carousel'
and the error would be:
Uncaught TypeError: Cannot read property 'fn' of undefined
Could somebody help me figure out what happened? thanks.
Update: my webpack loader settings:
loaders: [
// Process JS with Babel.
{
test: /\.(js|jsx)$/,
include: paths.appSrc,
loader: 'babel-loader',
query: {
cacheDirectory: findCacheDir({
name: 'react-scripts'
})
}
},
{
test: /\.css$/,
loader: 'style!css?importLoaders=1!postcss'
},
{
test: /\.json$/,
loader: 'json'
},
{
test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/,
loader: 'file',
query: {
name: 'static/media/[name].[hash:8].[ext]'
}
},
{
test: /\.(mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/,
loader: 'url',
query: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]'
}
}
]
my component code:
import React, { Component } from 'react';
import './App.css';
import './css/style.css';
import './css/bootstrap.min.css';
import './css/owl.carousel.css';
import FruitSelector from './containers/fruit_Selector';
import FruitDetail from './containers/fruit_Detail';
import $ from 'jquery';
import 'owl.carousel';
class App extends Component {
render() {
$(document).ready(function(){
$(".content-slider").owlCarousel({
slideSpeed: 350,
singleItem: true,
autoHeight: true,
navigation: true,
navigationText: ["<i class='fa fa-angle-left'></i>", "<i class='fa fa-angle-right'></i>"]
});
});
return (
<div className="App">
<div className="row">
<div className="col-sm-4 col-md-3 sidebar">
<FruitSelector/>
</div>
<div className="col col-md-8">
<FruitDetail/>
</div>
</div>
</div>
);
}
}
export default App;
my webpack.config.dev.js plugin setting:
plugins: [
new InterpolateHtmlPlugin({
PUBLIC_URL: publicUrl
}),
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
}),
new webpack.DefinePlugin(env),
new webpack.HotModuleReplacementPlugin(),
// Watcher doesn't work well if you mistype casing in a path so we use
// a plugin that prints an error when you attempt to do this.
// See https://github.com/facebookincubator/create-react-app/issues/240
new CaseSensitivePathsPlugin(),
// If you require a missing module and then `npm install` it, you still have
// to restart the development server for Webpack to discover it. This plugin
// makes the discovery automatic so you don't have to restart.
// See https://github.com/facebookincubator/create-react-app/issues/186
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
})]
the error pops out:
App.js:71 Uncaught TypeError: (0 , _jquery2.default)(...).owlCarousel is not a function(…)
Remove plugin which blocks import syntax
Problem is with import syntax which is not default webpack syntax. You have installed in Your project https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-webpack-loader-syntax.md to block it, for sure it is part of react-create-app. Please remove it to enable this syntax.
Owl.carousel needs jQuery library imported inside it because it uses $ variable, so this is problem and it is why webpack-loader-syntax must be removed.
If we try to import owl in standard way then jQuery is not defined there ( every file in webpack has own scope ), so it will throw an error:
Uncaught TypeError: Cannot read property 'fn' of undefined
( Alternative )Use shimming module
If removing plugin is problem then You can try to add jQuery to every module with usage it as shimming module - https://webpack.github.io/docs/shimming-modules.html.
In webpack config it will look like:
module.exports = {
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
})
]
//other config vars
};
And just add it by:
import 'owl.carousel'
new webpack.ProvidePlugin({
$: path.resolve(path.join(__dirname, 'node_modules/jquery')),
jQuery: path.resolve(path.join(__dirname, 'node_modules/jquery')),
'window.jQuery': path.resolve(path.join(__dirname, 'node_modules/jquery')),
}),
It helped me
because the carousel has its own modules(node_modules/owl.carousel/node_modules/jquery) and the plugin takes from there jQuery and writes the carousel in to this module
dont import jquery as (import $ from 'jquery') as owl.carousel need jquery, instead in webpack.config file :
const webpack = require("webpack");
.....
----------
module.exports={
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery'
}),
]
}
this works perfectly alright.
I had a issue with same error "Cannot read property 'fn' of undefined", but making sure the Jquery reference was first, fixed it for me.
<!-- Webjar References -->
<script src="webjars/jquery/3.3.1/jquery.min.js"></script>
<script src="webjars/bootstrap/4.3.1/js/bootstrap.min.js"></script>
<link href="webjars/bootstrap/4.3.1/css/bootstrap.min.css"
rel="stylesheet">

Webpack does not import bundles from node_modules (js only)

Good Morning,
I'm quite new to Webpack and feel a bit lost - importing modules from the source path works just fine - but importing modules from node_modules (e.g. jQuery) gives me error messages that the module is not found. I am completely lost and don't even know what to look for or how to debug this further.
The error message i am getting is:
external "jquery":1 Uncaught ReferenceError: jquery is not defined
at Object.jquery (external "jquery":1)
at __webpack_require__ (bootstrap:723)
at fn (bootstrap:100)
at Object../js/ManagementApplication.ts (ManagementApplication.ts:5)
at __webpack_require__ (bootstrap:723)
at fn (bootstrap:100)
at Object.0 (dist.js:40457)
at __webpack_require__ (bootstrap:723)
at bootstrap:790
at bootstrap:790
jquery # external "jquery":1
__webpack_require__ # bootstrap:723
fn # bootstrap:100
./js/ManagementApplication.ts # ManagementApplication.ts:5
__webpack_require__ # bootstrap:723
fn # bootstrap:100
0 # dist.js:40457
__webpack_require__ # bootstrap:723
(anonymous) # bootstrap:790
(anonymous) # bootstrap:790
and here is my webpack config:
// shared config (dev and prod)
const {resolve} = require('path');
const {CheckerPlugin} = require('awesome-typescript-loader');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require("webpack")
module.exports = {
resolve: {
extensions: ['.ts', '.js'],
},
context: resolve(__dirname, '../src/main/'),
output: {
filename: "dist.js",
path: resolve(__dirname, '../target')
},
externals: {
bootstrap: "bootstrap",
jquery: "jquery"
},
module: {
rules: [
{
test: /\.js$/,
use: ['babel-loader'],
},
{
test: /\.tsx?$/,
use: ['babel-loader', 'awesome-typescript-loader'],
},
{
test: /\.css$/,
use: ['style-loader', {loader: 'css-loader', options: {importLoaders: 1}}],
},
{
test: /\.(scss)$/,
use: [{
loader: 'style-loader', // inject CSS to page
}, {
loader: 'css-loader', // translates CSS into CommonJS modules
}, {
loader: 'postcss-loader', // Run postcss actions
options: {
plugins: function () { // postcss plugins, can be exported to postcss.config.js
return [
require('autoprefixer')
];
}
}
}, {
loader: 'sass-loader' // compiles Sass to CSS
}]
},
{
test: /\.woff2?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: 'url-loader?limit=10000',
},
{
test: /\.hbs/,
loaders: "handlebars-loader"
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
loaders: [
'file-loader?hash=sha512&digest=hex&name=img/[hash].[ext]',
'image-webpack-loader?bypassOnDebug&optipng.optimizationLevel=7&gifsicle.interlaced=false',
],
},
],
},
plugins: [
new CheckerPlugin(),
new HtmlWebpackPlugin(),
new webpack.IgnorePlugin(/\/iconv-loader$/)
],
performance: {
hints: false,
},
};
and this one:
// development config
const merge = require('webpack-merge');
const webpack = require('webpack');
const commonConfig = require('./common');
module.exports = merge(commonConfig, {
mode: 'development',
entry: [
'webpack-dev-server/client?http://localhost:4000',// bundle the client for webpack-dev-server and connect to the provided endpoint
'webpack/hot/only-dev-server', // bundle the client for hot reloading, only- means to only hot reload for successful updates
'./js/ManagementApplication.ts' // the entry point of our app
],
devServer: {
hot: true,
host: "0.0.0.0",
port: "4000"
},
devtool: 'source-map',
plugins: [
new webpack.HotModuleReplacementPlugin(), // enable HMR globally
new webpack.NamedModulesPlugin(), // prints more readable module names in the browser console on HMR updates
],
});
(both of them are loaded, the latter one overriding the first one).
I've checked a billion times that the libraries are correctly inside node_modules - just don't know why they are not loaded. This problem is not specific only to a specific library but genreally to all libraries.
Importing css resources from libraries works fine in contrast.
Does anyone have an idea how to fix this or can help me understanding what is happening?
If you intended jquery to be treated as an external, #Pandelis answer's right (note the uppercase Q: jquery: jQuery). But in case you want to import jquery as a node module, see below.
Use jQuery as a node module
If you want to use jQuery as a node module & have it bundled, you should install jquery from npm
npm install jquery
Then import it in your code
import $ from "jquery";
No need to add anything to webpack.config.js. But if you want to use jQuery as an external:
Use jQuery as an external
When you do something like this in webpack.config.js:
...
module.exports = {
externals: {
jquery: "jQuery" // or jquery: "$"
},
...
}
It tells webpack that in the line import jquery, jquery shouldn't be bundled; instead, look for the jQuery object in the global scope (which is window in our case). Both jQuery and $ will be valid. It also means you have to load jquery from external source:
#index.html
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<sript src="bundle.js"></script>
Then in your code, you can then do
import 'jquery' // or import $ from 'jquery'
$...
Just to illustrate, you can also do externals: { foo: 'jQuery' } and import 'foo' would still work.
Hope it helps!
Not actually 100% sure without looking at more of the project but give this a go.
Set your jquery external to:
"jquery": "jQuery"
and use jquery in your project as: import jQuery from 'jquery' or import $ from 'jquery'

ES6 - How to modify a variable in other modules

in module Global.js:
export let transLog = [];
in main:
import * as G from "./Global";
G.transLog = [];
I got a error:
app.js?c99e:19 Uncaught TypeError: Cannot set property q of #<Object> which has only a getter
at eval (app.js?c99e:19)
at Object.<anonymous> (bootstrap e92860b74eb6dd40b159:62)
at __webpack_require__ (bootstrap e92860b74eb6dd40b159:19)
at bootstrap e92860b74eb6dd40b159:62
at bootstrap e92860b74eb6dd40b159:62
webpack config:
const webpack = require('webpack');
module.exports = {
entry: './js/app.js',
plugins: [
new webpack.SourceMapDevToolPlugin({
filename: "[file].map"
})
],
output: {
filename: './dist/app.js'
},
devtool: 'source-map'
};
So, how to modify a variable in other modules?
You cannot assign new values to exported variables, only the module itself can do that (and when it does, it can be pretty confusing, so I'd recommend to avoid this).
You can mutate exported objects though, e.g. G.translog.push(…) or G.translog.length = 0.

typescript: resolving typeahead.js with typescript and webpack 2

I am getting the following error from webpack.
ERROR in ./wwwroot/js/admin/infrastructure/typeaheadComponent.ts
Module not found: Error: Can't resolve 'typeahead' in ...
I have the following installed
npm install typeahead.js
npm install #types/typeahead
My typescript is as follows, using node module resolution.
import { module } from "angular";
import "typeahead";
// necessary to import typeahead into JQuery, as otherwise
// typeahead below is not defined.
class TypeAheadController {
foo(e) {
$(e).typeahead(...)
}
}
this generates javascript as follows:
"use strict";
var angular_1 = require("angular");
require("typeahead");
var TypeAheadController = (function () { ...
My webpack.config.js is as follows:
module.exports = {
context: __dirname,
entry: [
"./app.ts",
"./tab.ts",
"./client/clientService.ts",
"./client/clientSearchComponent.ts",
"./infrastructure/messageComponent.ts",
"./infrastructure/typeaheadComponent.ts",
"./url.ts"],
output: {
filename: "./wwwroot/js/admin/admin.js"
},
devtool: "source-map",
module: {
rules: [
{ test: /\.ts$/, use: 'ts-loader' }
]
}
};
imported into a gulp task.
How do I specify that typeahead is located in node_modules/typeahead.js/dist/typeahead.bundle.js
The module is called typeadhead.js so you also need to import typeahead.js, not typeahead.
import "typeahead.js";
The import is always the same as the name you use to install it with npm. And it's not even special, it simple looks into node_modules and finds the directory with the given name. Then it looks into package.json and imports the file specified in the main field. See also Node.js - Folders as Modules.
You could use resolve.alias to change the name of the import, but there is not really a good reason for doing that in this case.
I resolved this by making the following changes.
You need to import Bloodhound and Typeahead seperately. To do this edit your webpack.config.js
resolve: {
extensions: ['.js', '.ts'],
alias: {
typeahead: 'corejs-typeahead/dist/typeahead.jquery.min.js',
bloodhound: 'corejs-typeahead/dist/bloodhound.min.js'
}
},
And then in your .ts file:
import "typeahead";
import * as Bloodhound from "bloodhound";
You could solve this using aliasing. Minimal example of what to change in your webpack.config.js:
module.exports = {
/* ... everything you currently have */
resolve: {
alias: {
typeahead: 'typeahead.js'
}
}
}

Categories