Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some dependencies that rely on window don't work #333

Closed
mix3d opened this issue Dec 18, 2017 · 29 comments · Fixed by #6247
Closed

Some dependencies that rely on window don't work #333

mix3d opened this issue Dec 18, 2017 · 29 comments · Fixed by #6247

Comments

@mix3d
Copy link

mix3d commented Dec 18, 2017

BUG:
An older website that imports jQuery and Bootstrap from locally downloaded files don't have their dependencies properly running; Bootstrap complains that jQuery and $ are not found. It seems that either the order from the HTML is not respected, or that JS that installs to the root window are not working correctly.

🎛 Configuration (.babelrc, package.json, cli command)

        <script src="LIBS/jquery.min.js"></script>
        <script src="LIBS/bootstrap.min.js"></script>

$> parcel index.html

🤔 Expected Behavior

Imported scripts run correctly

😯 Current Behavior

Bootstrap complains about not finding jquery. Not sure if import order or window problem

🔦 Context

Can't use Parcel if this doesn't work for older / retrofit websites.

🌍 Your Environment

Software Version(s)
Parcel 1.2.0
Node v8.9.1
npm/Yarn npm v5.5.1
Operating System Windows 10
@benhutton
Copy link

We're seeing a lot of this, too, trying to get some legacy code working with Parcel

@brandon93s
Copy link
Contributor

This seems like a valid use case for a parcelignore. They're being included in the correct order, but I'm guessing the closure scope is preventing them from mounting to the window correctly.

@benhutton
Copy link

some of my problems here seem to go away by using jquery off of CDN instead of bundling in a node package. Probably more performant that way, anyways... Still investigating to see if that fixes everything.

@mix3d
Copy link
Author

mix3d commented Dec 27, 2017 via email

@wywywywy
Copy link

wywywywy commented Dec 27, 2017

Hi.

I tried using a CDN, a local link, and node modules, nothing works.

This is a very simple HTML that I used to test.

<!DOCTYPE html>
<html lang="en">
<head>
	<title>Test</title>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<!-- JQuery -->
	<script src="import/jquery-3.2.1.min.js"></script>
	<!-- Bootstrap JS and CSS -->
	<link rel="stylesheet" href="import/bootstrap/css/bootstrap.min.css">
	<script src="import/bootstrap/js/bootstrap.min.js"></script>
</head>
<body>
    <div id="test"></div>
</body>
<script>
    $('#test').text('aaa');
</script>
</html>

@benhutton
Copy link

@DeMoorJasper @brandon93s I'm not sure how .parcelignore would fix this. I don't want to ignore these files... I want parcel to manage everything and for it to work! If I include jquery separately from CDN, before my parcel bundle, it works. I would rather include it in my parcel bundle.

@DeMoorJasper
Copy link
Member

DeMoorJasper commented Jan 2, 2018

@benhutton parcelignore could possibly just copy over the files without any processing making this exactly the same as using a cdn for example and hopefully mounting to the window correctly.
Although a better fix would be a more solid way to go, possibly removing the need for parcelignore (which is currently still RFC)

@benhutton
Copy link

@DeMoorJasper so I would still somehow be able to leverage jquery from npm?

@wywywywy
Copy link

wywywywy commented Jan 2, 2018

Yes I would be pretty happy if it can just bundle our code without touching the 3rd party libraries.

@DeMoorJasper
Copy link
Member

DeMoorJasper commented Jan 2, 2018

@benhutton is that not currently possible? If u approach jQuery as a node_module it should work just fine, because it never has the need to mount to the window?

something like

const $ = require('jQuery');

$("#somethin")....

@DeMoorJasper
Copy link
Member

DeMoorJasper commented Jan 2, 2018

Also another way to fix this would be to detect how many packages are being bundled and just leave sole packages/libraries without imports alone.
All these things i'm proposing sound and kinda are hotfixes/short term solutions.

Like discussed in another issue the proper end-game for bundling would be to remove the need of the prelude entirely, so that code is just bundled in a smaller package and doesn't perform any unexpected behaviour.

@benhutton
Copy link

unfortunately, const $ = require('jQuery'); doesn't work for me. At least, I can't do that at the top level and have it work everywhere (like I could with webpack, which we're moving from). I think it works if I do that in every single file that uses jquery.

@mix3d
Copy link
Author

mix3d commented Jan 2, 2018

@benhutton Since bootstrap (at least the self downloaded one) seems to require jQuery on the window scope, and fails if not found, I'm not sure how to solve that multi-dependency.

@antony
Copy link

antony commented Jan 8, 2018

Like @benhutton I have also been stymied by the fact that const $ = require('jquery') doesn't work, so right now I don't know a way past this.

@terminalqo
Copy link

same problem, how to use jquery with parcel? need help

@devongovett
Copy link
Member

This is because jQuery disables setting itself on the window global in a CommonJS environment (which Parcel is). See https://github.com/jquery/jquery/blob/master/src/wrapper.js#L29 and https://github.com/jquery/jquery/blob/2d4f53416e5f74fa98e0c1d66b6f3c285a12f0ce/src/exports/global.js#L30-L32.

If you need it to be accessible on window, you could do something like window.$ = require('jquery'). Does that work?

@windware-ono
Copy link

windware-ono commented Jan 22, 2018

I'm using TypeScript but this did expose '$' to 'window' for jquery-ui etc to consume it.

import * as $ from 'jquery'
(<any>window).$ = (<any>window).jQuery = $

I have a question, but how can I access any of the classes being declared in the bundled JS without individually exporting them like above?

I found that they're listed in 'window.require.cache[N].exports.default' where N is some number I haven't specified but is there a way to access those by class names?

How am I supposed to access class methods in the bundled JS from within inline HTML such as through 'onclick' properties?

Currently I'm exporting every classes that need exposed the same way as jQuery above which is bit of a work since I can't iterate over a directory in the entry point script but have to manually list them all (which needs updating from time to time).

@laosb
Copy link

laosb commented Oct 19, 2018

Hitting this when using EaselJS 1.x versions.

import 'easeljs/lib/easeljs.min.js'

complains createjs wasn't there.

@devongovett
Copy link
Member

devongovett commented Nov 4, 2018

I'm not sure what we should do about this. The cause of this issue is that jQuery relies on this being the global object, but in a CommonJS environment (which Parcel emulates) this refers to the module object.

Perhaps we could make it so that if you imported a script from HTML, it would retain this in the entry module, but as soon as you require it would turn into the module object since it is in a common js context. Then you could have <script type="commonjs"> and <script type="module"> for commonjs and ES6 module environments. Obviously this is a breaking change from the current default, but with Parcel 2 on the horizon I'd like to revisit this issue and get some options that make sense. Thoughts?

@trickydisco78
Copy link

trickydisco78 commented Feb 21, 2019

I solved this by adding this code

  _jQuery = window.jQuery,
  // Map over the $ in case of overwrite
  _$ = window.$;

jQuery.noConflict = function(deep) {
  if (window.$ === jQuery) {
    window.$ = _$;
  }

  if (deep && window.jQuery === jQuery) {
    window.jQuery = _jQuery;
  }

  return jQuery;
};

// Expose jQuery and $ identifiers, even in AMD
// (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
// and CommonJS for browser emulators (#13566)
if (!noGlobal) {
  window.jQuery = window.$ = jQuery;
}```

in the top of the file (linked to above)

AlejandroAkbal added a commit to AlejandroAkbal/akbal.dev that referenced this issue May 3, 2019
@sundayz
Copy link

sundayz commented Jun 21, 2019

2 and a half years and nobody posted at least a workaround?

This worked for me in a project that uses bootstrap and jquery. In my app.js (entry point for parcel):

import jQuery from "jquery";
window.$ = window.jQuery = jQuery; // workaround for https://github.com/parcel-bundler/parcel/issues/333
import 'bootstrap/dist/js/bootstrap.bundle';

Import order matters. Couldn't get it to work with the normal bootstrap js include because it couldn't resolve the dependency for Popper, even though I'm not using tooltips or popper anywhere.

@taducquang
Copy link

So only workaround? I think gulp doesn't have this issue.

@antony
Copy link

antony commented Jul 18, 2019

@taducquang gulp isn't a bundler.

muxator added a commit to ether/etherpad-lite that referenced this issue Sep 16, 2019
The vendored jquery version was 1.9.1 from 2013-02-04. Let's replace it with the
most recent one from the 1.x branch (1.12.4 from 2016-05-20).

The modification in rjquery.js is needed because recent jQuery versions changed
their behaviour, and do not set themselves on the global window object.
See: parcel-bundler/parcel#333 (comment)

This will be the lastest jQuery 1.x version ever, because 1.x branch is
definitively EOLed (see jquery/jquery.com#162).

This is a stopgap measure to get the latest security fixes. Going forward,
another strategy will be needed.

Closes #3640
@stoically
Copy link

stoically commented Nov 4, 2019

The workaround from @sundayz

import jQuery from "jquery";
window.$ = window.jQuery = jQuery;
import "lib-that-depends-on-jQuery";

didn't work for me, since the library that depends on jQuery does so in a sync way, and because parcel ends up generating

"use strict";
var _ = _interopRequireDefault(require("jquery"));
require("lib-that-depends-on-jQuery");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
window.$ = window.jQuery = _.default;

assigning jQuery on window happens too late.

Combining the workaround with the hint from @devongovett does the trick for me:

const jQuery = require("jquery")
window.$ = window.jQuery = jQuery;
require("lib-that-depends-on-jQuery")

since parcel generates in that case

var jQuery = require("jquery");
window.$ = window.jQuery = jQuery;
require("lib-that-depends-on-jQuery");

@flip111
Copy link

flip111 commented Mar 27, 2020

var jQuery = require("jquery");
window.$ = window.jQuery = jQuery;
require("lib-that-depends-on-jQuery");

This works for me if lib-that-depends-on-jQuery only uses an instance of jQuery. I tested it with select2 and this condition to test for jQuery absence does not trigger.

However ..

select2 extends jQuery functionality, but the "upgraded" jQuery instance is not available.

That is: the jQuery instance is not mutated (to be upgraded)
And also: the return value from const foo = require('select2/dist/js/select2.js'); is not a jQuery instance but instead something with signature of function exports(root, jQuery).

Does someone have this working with a library that extends jQuery ??

@flip111
Copy link

flip111 commented Mar 27, 2020

My suspicion is that another library can make use of jQuery through 3 methods:

  1. grab it from window (i guess this would be useful when jQuery was loaded in CommonJS but dependent libraries are outside of CommonJS)
  2. the library can try to require jQuery itself through require('jquery'), however this can sometimes fail depending on how your paths are loaded (you can try require('jquery') in your own code to see if it works.
  3. the library can accept an instance of jQuery (and fallback to trying to load it itself), this is my preferred method because you have full control where jQuery and other library get loaded from and you also have control of the jQuery instances (for plugins)

As example of 3

const jQuery_tmp = require('cash-dom/dist/cash.min.js'); // using jQuery compatible library
const $ = require('select2/dist/js/select2.js')(jQuery_tmp); // Let the plugin do it's thing

@mix3d
Copy link
Author

mix3d commented Apr 2, 2020

@flip111 That's all fine and good if you are the library owner. If you are just importing something like Bootstrap, however, you as a dev don't really have control over how a dependency like jQuery is looked for.

@flip111
Copy link

flip111 commented Apr 3, 2020

My suspicion is that another library can make use of jQuery

@mix3d you shouldn't take this too literally. Replace "another library" by "your app".

You see here that if you include bootstrap, including jQuery is a separate step that is not within the code of bootstrap itself. This is the same if you are using import statements.

If you have code that is going "to look for jQuery", you should select the method of making jQuery available in such a way that it corresponds to the ways the code is looking for jQuery.

@fawazahmed0
Copy link

This worked for me:
import $ from 'jquery'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.