0

Recently, I have been thinking of moving our build process from Grunt to webpack-2. Our code is entirely written in AMD format using requirejs. For the purpose of the demo, I'll post the minimalistic version of our index.js file we are using.

Here's the link of jQuery-1.6.4 being used by committing the file and loaded via npm. Please excuse this for the time being. jQuery-1.6.4

index.js

define(['jQuery', 'arrayUtils'], function ($, arrayUtils) {
  'use strict';
  console.log($); 
  console.log(arrayUtils);

});

Corresponding require-config.js file:

(function () {
    'use strict';

    require.config({
        baseUrl: '../../src',
        waitSeconds: 0,
        paths: {
            jQuery: '/path to/jquery-1.6.4',
            urlUtils: '/path to/urlUtils',
            // ...
        },
        shim: {
            jQuery: {
                exports: '$'
            },
            urlUtils: {
                exports: 'urlUtils'
            },
            // ...
        }
    });
})();

I tried to use our AMD approach but want to bundle everything using webpack-2. I went through the docs and various blogs and came across the config which makes everything worked like a charm except the old-gold jQuery.

Here is the webpack.config.js

var webpack = require('webpack');
var UglifyJsPlugin = webpack.optimize.UglifyJsPlugin;

// Import the plugin:
var path = require('path');
var env = require('yargs').argv.mode;
var outputFile;

var options = {
  target: 'this'
};
var libOptions = {
  name: 'heatmap-inject',
  version: JSON.stringify(require("./package.json").version)

};

var plugins = [];


if (env === 'build') {
  plugins.push(new UglifyJsPlugin({
    sourceMap: true,
    minimize: true
  }));
}

// At the end of the file:
module.exports = {
  entry: __dirname + '/src/library.js',
  devtool: 'source-map',
  output: {
    path: __dirname + '/dist/webpack',
    library: 'library.js',
    filename: libOptions.name + '.js', // libOptions.name + (options.target ? '.' + options.target : '') + (env === 'build' ? '.min.js' : '.js')
    libraryTarget: options.target || 'umd',
    umdNamedDefine: true
  },
  module: {
    rules: [{
      test: /(\.js)$/,
      exclude: /(node_modules|bower_components)/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['es2015'],
          plugins: []
        }
      }
    }, {
      enforce: 'pre',
      test: /(\.js)$/,
      exclude: /(node_modules|bower_components)/,
      use: {
        loader: 'eslint-loader',
        options: {}
      }
    }, { test: /jQuery/, loader: 'exports-loader?$'
    }, { test: /urlUtils/, loader: 'exports-loader?urlUtils'
    }]
  },
  resolve: {
    alias: {
     'jQuery': 'heatmaps/vendor/jquery-1.6.4',
     'urlUtils': 'lib/heatmap/UrlUtils'
    },
    modules: [
      'src/**/*.js',
      'src/bower_components',
      path.resolve('./src')
    ],
    extensions: ['.js']
  },
  plugins: plugins
};

The file which is generated by webpack-2 throws an error that $.fn is not defined i.e. instead of $ being a jQuery function it's a plain empty Object. Though window.$ is defined and returns the right jQuery function but why not I'm getting the same result for $. In the case of AMD, I'm getting the correct result since $ is in a closure and returns the 1.6.4 jQuery. Using webpack, it is behaving weirdly.

!(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(1), __webpack_require__(13) ], __WEBPACK_AMD_DEFINE_RESULT__ = function ($, urlUtils) { ... }

What other things I have tried:

  1. { test: /jQuery/, loader: 'expose-loader?$'
  2. { test: /jQuery/, loader: 'exports-loader?jQuery=jQuery,$=jQuery,this=>window'
  3. { test: /jQuery/, loader: 'exports-loader?this=>window'
  4. { test: /jQuery/, loader: 'expose-loader?$=jQuery'

Can't use externals property as it pollutes the thw window.jQuery and as this library is injected/loaded on user's website, this will break things on the target source.

Please help me! I'm frustrated now with this weird jQuery thing.

softvar
  • 17,917
  • 12
  • 55
  • 76

2 Answers2

1

This potentially is because the version of jQuery you are using is before a jQuery AMD fix. With jQuery 2.2.1 and 3.0, I ported a number of large AMD codebases over to ES Modules. The main stack overflow post I used to get through the jankiness of jQuery in a build env was this very thorough answer by jhhns himself.

The crux of the solution, especially if you're using jQuery UI on top of jQuery, is to use the provide plugin and then have a "plugins" module that imports the particular modules you need. Especially if they have a particular order they need loaded in around the rest of the app in question.

With ES Modules (AMD works too):

// jquery-plugins.js
// import $ from 'jquery' is implicit because of the provide plugin
import 'jquery-ui'
import 'malihu-custom-scrollbar'
import 'datatables'

Importing this module first (or close to first) for all your dependencies will give you the jQuery namespaces you need later on.

wegry
  • 7,046
  • 6
  • 39
  • 59
  • Thanks! In my case I was using jQuery 1.9.1 (which is the last one with that amd bug) and jQuery UI. Adding `amd: { jQuery: true }` to webpack solved my issue. – Samir Aguiar Jan 26 '18 at 14:56
0

I was using jQuery 1.9.1 in my legacy code and going to bundle it with Webpack 4. I faced the similar issues.

After a full day struggling, by following @SamirAguiar's solution and https://webpack.js.org/configuration/other-options/#amd, I figure out a workable way in my webpack config:

{
  resolve: {
    alias: {
      'jquery': 'jquery/jquery', // -> node_modules/jquery/jquery.js
      // ...
    },
  },
  amd: {
    jQuery: true
  },
  plugins: [
    new webpack.ProvidePlugin({
      $: 'jquery', // legacy code tend to use $ without requiring it
      jQuery: 'jquery'
    }),
    // ...
  ]
}

This works well with jquery-ui 1.10.3 and some other jQuery based plugins.

Evi Song
  • 862
  • 11
  • 14