12

I have a problem with jquery when i using it on webpack. My code:

const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CompressionPlugin = require("compression-webpack-plugin");

module.exports = {
    entry: {
        vendor: [
            './src/main/webapp/js/vendor/jquery-3.3.1.min.js',
            // './src/main/webapp/js/vendor/fs.js',
            './src/main/webapp/js/vendor/google-adsense.js',
            './src/main/webapp/js/vendor/jquery.menu-aim.min.js',
            './src/main/webapp/js/vendor/jquery.touchSwipe.min.js',
        ],
        app: './src/main/assets/js/desktop/app.js',
        mobile: './src/main/assets/js/mobile/app.js',
        touch: './src/main/assets/js/touch/app.js',
    },
    module: {
        rules: [{
                test: /\.js$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env']
                    }
                }
            },
            {
                test: require.resolve('jquery'),
                loader: 'expose-loader?jQuery!expose-loader?$'
            }
        ],
    },
    plugins: [
        // new CleanWebpackPlugin(['src/main/webapp/assets']),
        new webpack.optimize.CommonsChunkPlugin({
            name: 'common' // Specify the common bundle's name.
        }),
        new UglifyJsPlugin({
            test: /\.js$/,
            sourceMap: process.env.NODE_ENV === "development"
        }),
        new webpack.ProvidePlugin({
            $: "jquery",
            jQuery: "jquery"
        })
    ],
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, './src/main/webapp/js')
    }
};

When above code compiles , console throws this error

vendor.js:1 Uncaught ReferenceError: webpackJsonp is not defined at vendor.js:1

And when i try to use this

externals: {
  jquery: 'jQuery'
}

It throws

vendor.js:1 Uncaught ReferenceError: jQuery is not defined
    at Object.231 (vendor.js:1)
    at o (common.js:1)
    at Object.228 (vendor.js:1)
    at o (common.js:1)
    at window.webpackJsonp (common.js:1)
    at vendor.js:1

And i using jquery in my core js file import $ from 'jquery'. What did i do wrong ? any help ? Thank you.

batgerel.e
  • 837
  • 1
  • 10
  • 31
  • 1
    Possible duplicate of [Managing jQuery plugin dependency in webpack](https://stackoverflow.com/questions/28969861/managing-jquery-plugin-dependency-in-webpack) – Jordi Castilla Feb 02 '18 at 09:30
  • @JordiCastilla above link doesn't fix my problem. I am still on this :(. Any suggestion ? – batgerel.e Feb 02 '18 at 09:36
  • did you check all post?? IMHO seems you're missing `npm i jquery --save` or some other small part defined in the linked question – Jordi Castilla Feb 02 '18 at 09:37
  • Yes, i tried `npm install jquery --save` and `npm update` etc – batgerel.e Feb 02 '18 at 09:38
  • 1
    How are you _using_ jQuery? – Aluan Haddad Feb 02 '18 at 09:40
  • @AluanHaddad i didn't understand your question so well i am sorry. But i think i am using jquery like in my core js file `import $ from 'jquery'`. – batgerel.e Feb 02 '18 at 09:42
  • That (`import $ from 'jquery';`) is what I wanted to know thanks. Add it to your question. – Aluan Haddad Feb 02 '18 at 09:42
  • @AluanHaddad please suggest me edited version :) thank you. – batgerel.e Feb 02 '18 at 09:43
  • @KhShuren-Erdene no need, that is the proper and correct import syntax. The issue is likely in your configuration. You say that you've ran `npm i jquery`, however, you are explicitly bundling `'./src/main/webapp/js/vendor/jquery-3.3.1.min.js'`. Having a third party library in your application folder is almost certain to cause problems. Additionally, you like 3 other plugins that do something jQuery related. Just try the simple case (no special exclude, no external, no expose) first. – Aluan Haddad Feb 02 '18 at 09:46
  • @AluanHaddad i thought it was causing jquery minified version and i update `3.3.1.min.js` to `3.3.1.js` and rebuild and still don't work as you said i need to don't use exclude ? or something different ? – batgerel.e Feb 02 '18 at 09:51
  • @KhShuren-Erdene that is not the point. Someone asked you if you installed it with npm, you said yes but look at your config. You are either not bundling that installed version or you are bundling multiple versions! – Aluan Haddad Feb 02 '18 at 09:52

1 Answers1

20

So there are few themes in your webpack.config.js, some of which conflict. Just going to list them so I can better understand what I think you are trying to achieve.

Theme 1

You have an entry called vendor that is clearly referencing a minified jQuery library you have presumably downloaded and placed in the directory specified.

Theme 2

You also have an expose-loader that is essential exposing the jquery library from its node_modules probably listed in the dependencies of your package.json.

This makes the jquery in the node_modules available as $ and jQuery in the global scope of the page where your bundle is included.

Theme 3

You also have included the ProvidePlugin with configuration for jQuery.

The ProvidePlugin is supposed to inject dependencies into the scope of your module code, meaning you do not need to have import $ from 'jquery' instead $ and jQuery will already be available in all of your modules.

Conclusion

From what I have gathered I think you're trying to bundle jQuery from the static file at ./src/main/webapp/js/vendor/jquery-3.3.1.min.js in a vendor bundle.

You are then trying to expose the libraries in the vendor bundle to the global scope (jQuery).

Then also have your application code able to import jQuery from what is made available by the vendor bundle in the global scope.

Answer

So if that is what you are doing you need to do the following things.

Firstly, check in your package.json files dependencies for jquery. If its there you want to remove it, there's no need for it if you're going to use your jquery-3.3.1.min.js file instead to provide jQuery to your application.

Secondly, change your test of the expose-loader to trigger when it sees your jquery-3.3.1.min.js file in your entries, not resolve it from the jquery dependency from your node_modules.

This regex pattern should do the trick.

{
  test: /jquery.+\.js$/,
  use: [{
      loader: 'expose-loader',
      options: 'jQuery'
  },{
      loader: 'expose-loader',
      options: '$'
  }]
}

Thirdly, remove the ProvidePlugin if you're going to import the library explicitly with import $ from 'jquery' you do not need it.

Lastly, you need to tell webpack when it sees an import for jquery it can resolve this from window.jQuery in the global scope. You can do this with the externals configuration you already referenced.

externals: {
  jquery: 'jQuery'
}

With all these changes you should end up with a webpack.config.js file that looks like this.

const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CompressionPlugin = require("compression-webpack-plugin");

module.exports = {
    entry: {
        vendor: [
            './src/main/webapp/js/vendor/jquery-3.3.1.min.js',
            // './src/main/webapp/js/vendor/fs.js',
            './src/main/webapp/js/vendor/google-adsense.js',
            './src/main/webapp/js/vendor/jquery.menu-aim.min.js',
            './src/main/webapp/js/vendor/jquery.touchSwipe.min.js',
        ],
        app: './src/main/assets/js/desktop/app.js',
        mobile: './src/main/assets/js/mobile/app.js',
        touch: './src/main/assets/js/touch/app.js',
    },
    module: {
        rules: [{
                test: /\.js$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env']
                    }
                }
            },
            {
                test: /jquery.+\.js$/,
                use: [{
                    loader: 'expose-loader',
                    options: 'jQuery'
                },{
                    loader: 'expose-loader',
                    options: '$'
                }]
            }
        ],
    },
    plugins: [
        // new CleanWebpackPlugin(['src/main/webapp/assets']),
        new webpack.optimize.CommonsChunkPlugin({
            name: 'common' // Specify the common bundle's name.
        }),
        new UglifyJsPlugin({
            test: /\.js$/,
            sourceMap: process.env.NODE_ENV === "development"
        })
    ],
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, './src/main/webapp/js')
    },
    externals: {
        jquery: 'jQuery'
    }
};

I hope that does not just give you the answer but enough explanation as to where you were going wrong.

jshbrntt
  • 5,134
  • 6
  • 31
  • 60