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

jQuery on Node.js can't be used as a dependency #3979

Closed
BurkovBA opened this issue Feb 15, 2018 · 7 comments
Closed

jQuery on Node.js can't be used as a dependency #3979

BurkovBA opened this issue Feb 15, 2018 · 7 comments

Comments

@BurkovBA
Copy link

BurkovBA commented Feb 15, 2018

What's the status of jquery in node.js environment?

One of the modules, my node.js project imports, is a jquery plugin and requires jquery as a dependency. When I try to initialize it, I get an error that $.fn is undefined, thus plugin can't attach to it.

Turns out, jquery can't properly initialize without a window object.

console.log(require("jquery").toString()); returns:

function ( w ) { if ( !w.document ) { throw new Error( "jQuery requires a window with a document" ); } return factory( w ); }

I can't just initialize jquery manually, passing jsdom as a window object substitute:

const jsdom = require("jsdom");
const { window } = new jsdom.JSDOM(`...`);
var $ = require("jquery")(window);

because require('jquery') is executed in a plugin code, that I can't modify.

StackOverflow question has an overwhelmingly large number of recent answers, so, I suppose many people have recently encountered a similar problem.

What do I do?

Thank you,
Boris.

@dmethvin
Copy link
Member

You're not using a plugin in your code snippets shown there. Please provide a complete test case as asked for in the issue template.

@BurkovBA
Copy link
Author

BurkovBA commented Feb 15, 2018

@dmethvin Here is a minimal, complete and isolated test case to reproduce this issue.

This is a sample jquery plugin that you try to execute in node.js. It has a UMD wrapper and tries to require jquery, but jquery initialization fails due to the absence of window object in node.js:

testJquery.js

(function (global, factory) { // this is a UMD wrapper around the plugin
  if (typeof define === "function" && define.amd) {
    define(['jquery'], factory);
  } else if (typeof exports !== "undefined") {
    factory(require('jquery'));
  } else {
    var mod = {
      exports: {}
    };
    factory(global.jquery);
  }
})(this, function (_jquery) {
  'use strict';

  var _jquery2 = _interopRequireDefault(_jquery);  // this is ES6 interop, I assume 

  function _interopRequireDefault(obj) {
    return obj && obj.__esModule ? obj : {
      default: obj
    };
  }

  var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
    return typeof obj;
  } : function (obj) {
    return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
  };

  function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
      throw new TypeError("Cannot call a class as a function");
    }
  }

  var TestJQueryPlugin = function($) { // this is the code of plugin itself
    console.log(require('jquery').toString());     // try importing jquery and printing the results

    var testJQueryPlugin = function() {};
    testJQueryPlugin.prototype.init = function () {};

    $.fn['TestJQueryPlugin'] = testJQueryPlugin; // THIS WILL FAIL, $.fn does not exist
    return testJQueryPlugin;
  }(_jquery2)
});

If you run it with node testJquery.js (after npm install jquery, naturally), you get:

function ( w ) {
				if ( !w.document ) {
					throw new Error( "jQuery requires a window with a document" );
				}
				return factory( w );
			}
/home/burkov/Documents/Projects/BurkovBA.github.io/testJquery.js:42
    $.fn['TestJQueryPlugin'] = testJQueryPlugin;
                             ^

TypeError: Cannot set property 'TestJQueryPlugin' of undefined
    at /home/burkov/Documents/Projects/BurkovBA.github.io/testJquery.js:42:30
    at /home/burkov/Documents/Projects/BurkovBA.github.io/testJquery.js:44:4
    at /home/burkov/Documents/Projects/BurkovBA.github.io/testJquery.js:5:5
    at Object.<anonymous> (/home/burkov/Documents/Projects/BurkovBA.github.io/testJquery.js:13:3)
    at Module._compile (module.js:571:32)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.runMain (module.js:605:10)

@dmethvin
Copy link
Member

I'm not sure where you got that code from, it also seems like you're not sure based on the "I assume" comment which is where the problem begins. It looks like that is assuming Babel compilation which you're not doing here.

If you run node --inspect-brk testJquery.js you'll be able to step through the code and see why it fails. The command will print out a URL that explains how to attach a debugger.

@BurkovBA
Copy link
Author

BurkovBA commented Feb 16, 2018

@dmethvin Hi, Dave. Thank you for the link to node debugger.

This code is what Babel with UMD preset generates from import {$} from jQuery with

.babelrc

{
  "presets": [
    ["env"]
  ],
  "plugins": [
    ["transform-es2015-modules-umd", {
      "globals": {
        "jquery": "jQuery"
      }
    }]
  ]
}

So, with debugger I get the error - jQuery fails to initialize because of lack of window object:

$ node --inspect-brk testJquery.js 
Debugger listening on ws://127.0.0.1:9229/fb14e6ab-f3cd-4e9f-a1cc-fd3b8fdafa61
For help see https://nodejs.org/en/docs/inspector
Debugger attached.
function ( w ) {
				if ( !w.document ) {
					throw new Error( "jQuery requires a window with a document" );
				}
				return factory( w );
			}

/home/path/to/project/testJquery.js:42
    $.fn['TestJQueryPlugin'] = testJQueryPlugin;
                             ^

TypeError: Cannot set property 'TestJQueryPlugin' of undefined

I slightly modified testJquery.js before running this to handle export.default and global jQuery variable, instead of jquery for browser environments.

(function (global, factory) {
  if (typeof define === "function" && define.amd) {
    define(['jquery'], factory);
  } else if (typeof exports !== "undefined") {
    factory(require('jquery'));
  } else {
    var mod = {
      exports: {}
    };
    factory(global.jQuery);
    global.metisMenu = mod.exports;
  }
})(this, function (_jquery) {
  'use strict';

  var _jquery2 = _interopRequireDefault(_jquery);

  function _interopRequireDefault(obj) {
    return obj && obj.__esModule ? obj : {
      default: obj
    };
  }

  var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
    return typeof obj;
  } : function (obj) {
    return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
  };

  function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
      throw new TypeError("Cannot call a class as a function");
    }
  }

  var TestJQueryPlugin = function($) {
    console.log(require('jquery').toString());

    var testJQueryPlugin = function() {};
    testJQueryPlugin.prototype.init = function () {};

    $.fn['TestJQueryPlugin'] = testJQueryPlugin;
    return testJQueryPlugin;
  }(_jquery2.default)
});

@dmethvin
Copy link
Member

I think it's safe to say that at this point you're debugging something other than an issue in jQuery. Would you agree? All the plumbing around JavaScript can be a pain to get right for so many cases (CommonJS, Native ES Modules, Babel, Webpack loading, AMD, script tags). I have sympathy, but not the time to help you remotely debug this. StackOverflow is probably a good place to ask, but once again you will have to provide to them a complete example including your build steps if they're required.

@BurkovBA
Copy link
Author

BurkovBA commented Feb 16, 2018

@dmethvin
Dave, I solved all my practical issues, I don't need jquery in node.js for myself.

But again, please, take a note that jquery itself fails to initialize in node.js:

function ( w ) {
				if ( !w.document ) {
					throw new Error( "jQuery requires a window with a document" );
				}
				return factory( w );
			}

Take note that other people run into the same problem on StackOverflow. As of 2017 jquery can't be used in node.js side as a dependency (you have to explicitly initialize jsdom and pass its window to jquery upon jquery initialization).

Thank you for your time and efforts to maintain jquery (and for sympathy)!

@dmethvin
Copy link
Member

Yep, you definitely need a window as you mentioned in your original issue. The part I missed was "because require('jquery') is executed in a plugin code, that I can't modify" which would probably be true if you are using Babel to turn ES module declarations into CJS requires. You would need to do your own require.

@lock lock bot locked as resolved and limited conversation to collaborators Aug 15, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

No branches or pull requests

2 participants