expose-loader icon indicating copy to clipboard operation
expose-loader copied to clipboard

jQuery is not being exposed

Open thany opened this issue 3 years ago • 4 comments

Bug report

Exposing jQuery doesn't work. A global $, jQuery, a window.$, window.jQuery, or even globalThis.$ or globalThis.jQuery all do not exist in the resulting bundle, and therefor in the browser, and therefor in the browser console. They all return undefined.

Actual Behavior

> window.jQuery
undefined 

Expected Behavior

> window.jQuery
function() // and more jQuery-related stringness

How Do We Reproduce?

Add this rule:

      {
        test: require.resolve('jquery'),
        loader: 'expose-loader',
        options: {
          exposes: [
            "jQuery",
            "$",
          ],
        }
      },

Build whatever project (doesn't seem to matter) and trying resolving window.jQuery in the browser console.

Please paste the results of npx webpack-cli info here, and mention other relevant information

  System:
    OS: Windows 10 10.0.19044
    CPU: (12) x64 Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz
    Memory: 14.43 GB / 31.76 GB
  Binaries:
    Node: 16.15.1 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.18 - ~\AppData\Roaming\npm\yarn.CMD
    npm: 8.13.2 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Chrome: 103.0.5060.114
    Edge: Spartan (44.19041.1266.0), Chromium (103.0.1264.49)
    Internet Explorer: 11.0.19041.1566

And as always, scripts like these are missing the one browser I'm actually using. Firefox. Also probably pretty important: Webpack 5.73.0, expose-loader 4.0.0, jQuery 1.11.2.

Here's my full webpack.config.js:

const webpack = require('webpack');
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

const opts = {
  rootDir: process.cwd(),
  devBuild: process.env.NODE_ENV !== 'production',
};

module.exports = {
  entry: {
    app: './src/js/bootstrap'
  },
  mode: opts.devBuild ? 'development' : 'production',
  devtool: opts.devBuild ? 'inline-source-map' : 'source-map',
  output: {
    path: path.join(opts.rootDir, 'dist'),
    pathinfo: opts.devBuild,
    filename: 'js/[name].js'
  },
  optimization: {
    minimizer: [
      new TerserPlugin(),
      new CssMinimizerPlugin(),
    ]
  },
  plugins: [
    // Extract css files to seperate bundle
    new MiniCssExtractPlugin({
      filename: 'css/[name].css',
      chunkFilename: 'css/[id].css'
    }),
    // Copy fonts and images to dist
    new CopyWebpackPlugin({
			patterns: [
				{ from: 'src/fonts', to: 'fonts' },
				{ from: 'src/img', to: 'img' }
			]
		}),
    new CleanWebpackPlugin(),
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery',
      'window.jQuery': 'jquery',
      'window.$': 'jquery',
      'velocity': 'velocity-animate',
      'ScrollMagic': 'scrollmagic'
    }),
  ],
  module: {
    rules: [
      // Babel-loader
      {
        test: /\.js$/,
        exclude: /(node_modules)/,
        loader: 'babel-loader',
        options: {
          cacheDirectory: true,
        }
      },
      // Css-loader & sass-loader
      {
        test: /\.scss$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'sass-loader'
        ]
      },
      // Embed or emit fonts & images
      {
        test: /\.(png|jpg|jpeg|gif|ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/,
        type: 'asset', // Inline data-URI when under the maxSize, otherwise a file is emitted.
        parser: {
          dataUrlCondition: {
            maxSize: 100000
          }
        }
      },
      // Expose jQuery globally
      {
        test: require.resolve('jquery'),
        loader: 'expose-loader',
        options: {
          exposes: [
            "jQuery",
            "$",
          ],
        }
      },
    ]
  },
  resolve: {
    extensions: ['.js', '.scss'],
    modules: [path.resolve(__dirname, '..\\node_modules'), path.resolve(__dirname, 'node_modules')],
    alias: {
      request$: 'xhr'
    }
  },
  stats: {
    warnings: false
  }
};

thany avatar Jul 08 '22 11:07 thany

Even if I change it to exposes: [ "jQueryFOOBAR", "$" ], that identifier jQueryFOOBAR is nowhere to be found in the entire bundle that is being produced. It's as if it just nothing at all happens.

Also good to know, require.resolve('jquery') resolves to the jquery package, and the file really does exist.

I've also tried moving the expose-loader to the top of the rules. No effect.

Also whether or not import "jquery"; is at the very top of my bootstrap, makes no difference.

Also good to know is that jQuery is being provided correctly by the ProvidePlugin. That part works. But ProvidePlugin doesn't provide globals - it only provides automatic imports, as we know. No good for modules that aren't modules and really do need a global variable.

thany avatar Jul 08 '22 11:07 thany

Please provide example of a usage

alexander-akait avatar Jul 08 '22 12:07 alexander-akait

What kind of usage do you mean? Just webpack --config webpack.config.js --progress and watch an app.js being built.

Turns out if i change

test: require.resolve('jquery'),

To:

test: /jquery\.js/,

It works. But why?

Again, require.resolve('jquery') absolutely does return a valid&existing path to jQuery. And I have another project that is identical to this one, when it comes to exposing jQuery, where it does work as documented. Only difference is the jQuery version, which I cannot change atm.

Further digging:

When I place a simple top-level console.log("here!") in node_modules\expose-loader\dist\index.js, it is NOT being called. It's as if the whole module is completely being ignored. That's why I started looking at the test whether that might be wrong.

So it now correctly generates all those EXPOSE_LOADER things (and, to be expected, it now also places here! in the terminal). Fluff knows why all of that is neccesary just to get window.jQuery, but whatever.

The question is still: why must I use a different test? What is wrong with using require.resolve even though it generates the correct filename??

thany avatar Jul 08 '22 12:07 thany

You can load jquery from node_modules (i.e. require.resolve('jquery') will work for your), but also you can loader jquery not from node_modules, so your test will not work, thati is why I asked about example, also I want to look how you loader jQuery in your app to undestand potential problems

alexander-akait avatar Jul 10 '22 19:07 alexander-akait

Closing due to inactivity. Please test with latest version and feel free to reopen if still regressions. Thanks!

alexander-akait avatar Jan 17 '24 14:01 alexander-akait