2

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?

ArtOfCode
  • 5,702
  • 5
  • 37
  • 56

1 Answers1

-1

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...
szegheo
  • 4,175
  • 4
  • 31
  • 35
  • That's weird. I'm using it this way without any `expose-loader` or `ProvidePlugin`. I suggest you to try it with a clean, minimal webpack config to see if it works. Sounds stupid I know, but also be sure it's not some kind of old cached javascript problem.. Always hit F5 to reload or turn on `Disable cache ` while testing in Chrome's DevTools (Network tab) – szegheo Sep 24 '18 at 12:40