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

global variable is not available in the module #181

Open
bugy opened this issue Apr 11, 2020 · 8 comments
Open

global variable is not available in the module #181

bugy opened this issue Apr 11, 2020 · 8 comments

Comments

@bugy
Copy link

bugy commented Apr 11, 2020

Hi, I'm trying to add a non-exported 3rd party class to global scope, using rewire

The class looks like this: https://github.com/Dogfalo/materialize/blob/v1-dev/js/component.js

I'm doing it this way:

const ComponentModule = require('rewire')('materialize-css/js/component');
global.Component = ComponentModule.__get__('Component');

And it almost works, but inside component.js, there are usages of Element and cash. And during runtime, they fail as "is not defined"

I managed to solve the problem by:

const ComponentModule = require('rewire')('materialize-css/js/component');
ComponentModule.__set__('Element', Element);
ComponentModule.__set__('cash', cash);

global.Component = ComponentModule.__get__('Component');

And it works.

However, it seems very strange to me, since both of these variables are available before rewiring. Is there any way to add these globals implicitly?

I think it might be the same problem: #99

@rensbaardman
Copy link
Contributor

rensbaardman commented Apr 13, 2020

What do you mean with 'both of these variables are available before rewiring'? Looking at the source code of component.js, they don't get declared or imported there, so somehow they have been declared before when running Materialize in the usual way.

Even if in the file you are currently working in, you have declared Element and cash, that doesn't mean rewire makes them automagically available to component.js (nor should it, else you would get a very confusing mess of implicitly declared globals). So I don't think there is an issue with rewire, and your fix seems the way to go.

@bugy
Copy link
Author

bugy commented Apr 13, 2020

Hi @rensbaardman, thanks for the answer.

Even if in the file you are currently working in, you have declared Element and cash, that doesn't mean rewire makes them automagically available to component.js

I didn't declare them locally. They are already available in the global declarations. The example which I gave here is the full listing of the file.

@rensbaardman
Copy link
Contributor

rensbaardman commented Apr 13, 2020

Hi @rensbaardman, thanks for the answer.

Even if in the file you are currently working in, you have declared Element and cash, that doesn't mean rewire makes them automagically available to component.js

I didn't declare them locally. They are already available in the global declarations. The example which I gave here is the full listing of the file.

I see. Element is part of a standard webbrowser environment, cash seems to be a utility which has been loading into a webpage. But are you using this with Node? Then where do these globals get declared? Because they are not standard Node globals.

The problem probably lies with the different scope of component.js. Are you sure Element and cash get defined as globals? Because then component.js should have access to them.

By the way, I have to say that modifying the global variable is probably not the way to go for exporting Component. It's usually cleaner to use the module.exports-pattern.

@bugy
Copy link
Author

bugy commented Apr 13, 2020

Hi @rensbaardman, sorry, I had to provide a little bit more context:
I was writing Jest tests, with node + jsdom

My file (which I test) depends on 3rd party materializecss library. This library is quite old-school and doesn't export much. In most cases it relies on global nature of declarations inside a browser. I.e. it just sets window.abc = abc (for example, the cash function), usually.
Or declares a class without any exports (as in this source)

And my file depends on a Select class, from this library, which depends on global Component class, which depends on global cash method.
Also, all of them depend on the global Element class

Are you sure Element and cash get defined as globals? Because then component.js should have access to them.

I had a setup.js file for Jest (it's just any file, which runs before any tests) which looked like this:

import 'materialize-css/js/cash';

const ComponentModule = require('rewire')('materialize-css/js/component');
global.Component = ComponentModule.__get__('Component');

I could assume, that this import could be available only inside setup.js
But as you can see, I don't do anything with Element here, it comes from jsdom, which I don't import here at all (it's already injected by Jest).

By the way, I have to say that modifying the global variable is probably not the way to go for exporting Component. It's usually cleaner to use the module.exports-pattern.

The problem, that I'm not using this class directly. It's used in another .js from that library (e.g. select.js:13)
Otherwise, I fully agree with you, I try to use exports/imports where it's possible

@rensbaardman
Copy link
Contributor

rensbaardman commented Apr 13, 2020

Alright, but where/how are Element and cash defined as globals? Because if Jest injects Element into your test, then that (probably) doesn't make it global. And importing cash also doesn't make it global. So then it is expected that component.js doesn't have access to it.

@bugy
Copy link
Author

bugy commented Apr 13, 2020

Maybe I'm confusing something here, I'm not that good with JS. Here is my reasoning, may be I understand JS variable scoping wrong:

I don't know how Jest (or jsdom) injects the Element in my setup and tests. But this Element is also accessible in all 3rd party imported modules. I don't think, that Jest will inject Element everywhere

Also, Jest has no clue about cash, I just import it in one place and it magically works everywhere, except that component.js rewiring :(
I believe it's defined as a global this way (first lines in cash.js):

(function (factory) {
  window.cash = factory();
})

Also, if I remember correctly, I tried to debug this rewiring. Before entering require('rewire') I added a watch on global.Element and global.cash and they were there. However, somewhere during rewire initialisation, those globals became undefined.
And then somewhere during method call chain of rewire, these watches became undefined.

PS thanks for your time and trying to understand the issue!

@rensbaardman
Copy link
Contributor

rensbaardman commented Apr 13, 2020

Hmmm... that's interesting. I wouldn't know why calling rewire() would undefine global variables. I think I now understand what your problem is 😅 And now I see why it could be related to #99. If you could make a minimal reproducible example, that might help sorting this thing out!

bugy added a commit to bugy/rewire-181 that referenced this issue Apr 13, 2020
@bugy
Copy link
Author

bugy commented Apr 13, 2020

Hi @rensbaardman could you check this one, please?
https://github.com/bugy/rewire-181

There is an entry point: case_181.test.js, where I import a file, which sets global variable
Then I rewire a class
And then I print global variable from 3 places:

  • common util
  • rewired class
  • test

Only rewired class fails to print it

npm run test

Output:

someGlobalVar in test: abc
someGlobalVar in MyClass: undefined
someGlobalVar in another_file: abc

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

No branches or pull requests

2 participants