Global variable not working in NodeJS - javascript

I am trying to get global variables working in node.js, but it seems like I don't really understand the concept, even though my understanding matches the documentation.
Minimal example
My main.js file, which is compiled using rollup is:
global.a = 1;
require('./core/test.js');
My core/test.js file is simply:
console.log(a);
This causes the error:
Uncaught ReferenceError: a is not defined
The fully compiled output is:
(function (exports) {
'use strict';
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
console.log(a);
commonjsGlobal.a = 1;
}((this.LaravelElixirBundle = this.LaravelElixirBundle || {})));
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjpudWxsLCJzb3VyY2VzIjpbIkM6L3dhbXAvd3d3L3V1YmMvcmVzb3VyY2VzL2Fzc2V0cy9qcy9jb3JlL3Rlc3QuanMiLCJDOi93YW1wL3d3dy91dWJjL3Jlc291cmNlcy9hc3NldHMvanMvYXBwLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImNvbnNvbGUubG9nKGEpO1xyXG4iLCJnbG9iYWwuYSA9IDE7XHJcbnJlcXVpcmUoJy4vY29yZS90ZXN0LmpzJyk7XHJcbiJdLCJuYW1lcyI6WyJnbG9iYWwiXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQzs7QUNBZkEsY0FBTSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUM7OyJ9
It seems that a gets defined after console.log(a);. I'm not 100% sure if this matters in JS but it could be causing the error.
So my question is: what causes this, and what am I doing wrong here?
Possibly relevant information: I'm using Laravel Elixir to compile everything.
Note: Please don't post discussion on whether or not to use global variables in node.js or in general. I know it's usually a bad idea, but I have a very good reason to do it in this case.
Edit: Additional context
What I'm really trying to do is getting Zurb Foundation to work with Laravel Elixir though node.js. I installed foundation-sites through NPM. foundation-sites relies on jquery, which it pulls in itself. However, Foundation doesn't seem to follow the usual conventions of just using something like var jquery = require('jquery'); in its own js file. It actually relies upon a global jQuery variable being available. Therefore I have to ensure that somehow.
My actual file looks like this:
global.jQuery = global.$ = require('jquery');
require('foundation-sites');
So if there are any Foundation/Laravel-specific anwers I would be very happy to hear them as well. Is there something I'm not getting, or did Foundation just not create their package the "right" way?

It looks to me like an issue with your bundler not respecting the order you're doing those two lines in. Your source is clearly:
global.a = 1;
require('./core/test.js');
...but the bundled result is clearly the other way around:
console.log(a);
commonjsGlobal.a = 1;
It seems like it's hoisting the require calls. In general, having require calls that need to be done at a particular point in the execution control flow is not best practice (in fact, when defining modules for ES2015, import is explicitly defined as not being executed in the module's step-by-step control flow).
So if you need to do this, you'll probably want to look at another bundler. But check that it supports order other than dependency resolution order.

You are right that it should work. Here's a working example:
// file1.js
global.a = 'hello';
require('./file2');
// file2.js
console.log(a);
Run node file1.js, and you'll see 'hello' printed to the console.
It looks like you've got some other tool being used (Laravel / Elixir). Use node directly and see what happens.

Alright I solved this in a pretty simple way, by using browserify instead of rollup. Thanks for the other answers pointing me in the right direction. It's not a perfect solution, and I still don't fully understand what the differences are between the two, but it'll do for this project.
The relevant part of my gulpfile:
mix.browserify('app.js','public/js/app.js');
To use browserify with Laravel Elixir, I used
npm install laravel-elixir-browserify-official --save-dev

I solved this for rollup by providing an output.intro that sets up the required variables. In your case this would be:
// rollup.config.js
export default {
// ...
output: {
// ...
intro: `let a = 1;`
}
};
Admittedly this qualifies as a hack, but solves the bundling problem in my case (where switching away from rollup is not an option).

Related

Node vs React: functions in generic javascript modules

I have a Node server with a utils file that looks like this:
update = () => {
//
};
module.exports = { update };
This works, but when I use the same file in a React app, I get the error
"'update' is not defined no-undef"
To fix this I must add const keyword. This makes no sense to me. Why is declaring the function needed only in React?
This is because the React you're using has come bundled with a linter which warns you of potential code quality problems. create-react-app is one of the React bundles which comes integrated with ESLint.
If you don't want to see the warning and don't want to use ESLint, you can build your project manually from scratch, rather than using a boilerplate setup like create-react-app.
If you installed ESLint for your Node project, you'd see the no-undef warning for Node code as well.
Note that what you're seeing is a linter warning, not a runtime error. If you ignore the warning and bundle and serve the project anyway, it will be still be runnable if it doesn't run in strict mode. (In strict mode, the use of an undefined variable without declaring it with const / let / var first will throw an error)
The linting rule being violated in the code is described at length here:
https://eslint.org/docs/rules/no-undef
For further reference on what's going on when you don't declare a variable (and why it probably isn't a good idea), see
What is the purpose of the var keyword and when should I use it (or omit it)?
If you're using modules, you should pretty much always be using explicit imports and exports only - don't implicitly create global variables (like you're currently doing with update = () => {), since that defeats the point of using modules.

Using Jest, getting internal function

I am just re-looking at Jest as it's been getting a lot of good reports. However struggling to find a good way the access internal functions to test them.
So if I have:
const Add2 = (n)=> n+2;
export default (list)=>{
return list.map(Add2());
}
Then if I was using Jasmine or Mocha I'd use rewire or babel-plugin-rewire to get the internal Add2 function like this:
var rewire = require('rewire');
var Add2 = rewire('./Adder').__get__('Add2');
it('Should add 2 to number', ()=>{
let val = Add2(1);
expect(val).toEqual(3);
});
However neither of them seem to work with jest and while there looks like an excellent mocking syntax I can't see any way to get internal function.
Is there a good way to do this, something I'm missing on the jest api or set up?
You can actually achieve this if you are willing to use babel to transform your files before each test. Here's what you need to do (I'll assume you know how to get babel itself up and running, if not there are multiple tutorials available for that):
First, we need to install the babel-jest plugin for jest, and babel-plugin-rewire for babel:
npm install --save-dev babel-jest babel-plugin-rewire
Then you need to add a .babelrc file to your root directory. It should look something like this:
{
"plugin": ["rewire"]
}
And that should be it (assuming you have babel set up correctly). babel-jest will automatically pick up the .babelrc, so no additional config needed there unless you have other transforms in place already.
Babel will transform all the files before jest runs them, and speedskater's rewire plugin will take care of exposing the internals of your modules via the rewire API.
I was struggling with this problem for some time, and I don't think the problem is specific to Jest.
I know it's not ideal, but in many situations, I actually just decided to export the internal function, just for testing purposes:
export const Add2 = x => x + 2;
Previously, I would hate the idea of changing my code, just to make testing possible/easier. This was until I learned that this an important practice in hardware design; they add certain connection points to their piece of hardware they're designing, just so they can test whether it works properly. They are changing their design to facilitate testing.
Yes, you could totally do this with something like rewire. In my opinion, the additional complexity (and with it, mental overhead) that you introduce with such tools is not worth the payoff of having "more correct" code.
It's a trade-off, I value testing and simplicity, so for me, exporting private functions for testing purposes is fine.
This is not possible with jest. Also you should not test the internals of a module, but only the public API, cause this is what other module consume. They don't care how Add2 is implemented as long as yourModule([1,2,3]) returns [3,4,5].

Access javascript objects from dev console after bundling

I'm working on a React app and learning the ropes. I have a SPA where my dev environment is all running in Docker, and is using Gulp + Watchify, Browserify, and Babelify to bundle up all my JS(X) code and get it to the browser as a "bundle.js".
Everything works well, except I cannot access objects like React and ReactDOM from my Chrome developer tools. I'm sure this is because they are being "hidden" due to how they are being bundled up, but I'm not sure how to fix that or gain access to them. I can always "window.ReactDOM = ReactDOM" in my code, but that seems like the dumb way to go, and I foresee wanting to play with more code in the console.
I noted that I can add "debugger" to my code, and these objects are available when the code is paused, but once it runs I can't access the objects.
How are these objects being hidden away, and is there a sane way to get access to them besides breakpoints?
How they are hidden:
It is often times useful to not leak variables to the window/global scope. This is important because different files and different libraries could have the same variable names and conflict with each other in. A very simple trick usually used is doing something like:
// Runs an inline anonymous function
(function(){
/// Make your variables and run your code here ...
var a = 1; // Only accessible in this function
})();
console.log(a) // Undefined
^ The above is a common 'wrapper' to see around a regular JS file.
If you examine the output of webpack or another bundler, they take this a step further and make a closure for each file. So the bundle will look something like:
(function(){
var files = [
function(module, require){ var ReactDOM = require('react-dom')... /* file one code */}
function(module, require){ ... module.exports = localReactDomVar ... }
]
// Webpack magic runs each file and recursively gets dependencies on calls to require...
})();
TLDR: Each module is intentionally hidden and is anonymous anyway until the point in your code at which you do var React = require('react'). If you need access to React and ReactDOM in your console, there is no better way than window.React = React etc.

Using browserify with typescript to include a node module

I'm attempting to use a node module in a typescript application and I've included it in my TypeScript code via require().
I build my typescript to JS and all works well if I run it through node which is great.
I'm looking for a way to use the same code and run it through the browser. Browserify seemed like a sensible solution.
I popped a post build action on my VS project and I'm building a 206KB file via browserify (so it's certainly doing something!). Unfortunately my tiny bit of Typescript doesn't seem to be accessible when it's been browserified.
I'm not that familiar with what browserify should be generating so not quite sure whether what it's wrapped my .js code in is correct (can post snippets if it helps).
So my question is twofold really, I'm looking for the answer to either:
Is there a recommended way to write TypeScript post 0.9 to allow it to be run after it's been browserified?
Is there a way to simple tell TypeScript to pull in the 'require' dependency on its own?
Any thoughts or info in this area would be greatly appreciated.
EDIT:
Just to clarify, I'm generating my .js from the .ts during save/build, and in a post build action, pointing browserify to the output. An abridged js output looks like this:
var TestModule;
(function (TestModule) {
function launchDrone() {
var exports = require('ar-drone');
var client = exports.createClient();
}
})(TestModule || (TestModule = {}));
When I generate the browserified file from that, I can't access TestModule or launchDrone in any context (or certainly not from window. ) is there some trick to accessing the browserified code?
It looks like you potentially are not exporting TestModule? Your TestModule file should look like this:
module TestModule {
export function launchDrone() {
var exports = require('ar-drone');
var client = exports.createClient();
}
}
export = TestModule;
This way you should be able to launch TestModule and TestModule.launchDrone from the window.

evaluate module in another module's context

I know that this is very un-Node/CommonJS-y—forgive me. (I'm writing a library of sorts and I'd like my library's require method to work exactly the same on the browser and on NodeJS.)
What I'd like is to be able to do is evaluate the script in the context of the current module—that is, if I say exports.a = "100"; in the module, I'd like for exports.a to be equal to "100" in all of the code in the requireing module after the require.
If this isn't clear, I'd be happy to elaborate.
This won't be a complete answer, but hopefully will help you get in the right direction.
I've been messing with Node's system of creating modules for the last couple of days. Basically I wanted to create some modules that were invoked in an entirely fresh context and variable scope, for which I would define a limited subset and extension of Node's capabilities.
I ended up studying their source here, and giving particular attention to the NativeModule constructor and its methods.
You'll notice that the source of the module is read from a file, wrapped in a string representing a function and eval'd into actual code.
The wrapper:
NativeModule.wrapper = [
'(function (exports, require, module, __filename, __dirname, define) { ',
'\n});'
];
The function is invoked, which invokes the contained module code.
The function requires half a dozen arguments as you can see from the wrapper, the first of which is the exports object (which starts off empty). It's also is passed the require function, which is why you can access require as a variable even though require isn't global.
The module code populates the exports object and then the exports is cached so all that work doesn't need to be done in the future. So when require( 'someModule' ) is invoked, it just looks up the cached exports object and returns it.
I'm sure you could do something like this in your code as long as you can get the source for the module that you want to require.
Perhaps SomeModule.toString() will be sufficient for you. Not sure how consistent browser support is though.
There's also a private API that's used to set up the environment for the modules.
process.binding('evals').Script
/*
{ [Function: Script]
createContext: [Function],
runInContext: [Function],
runInThisContext: [Function],
runInNewContext: [Function] }
*/
I ended up needing to use createContext and runInContext to get things working, but I'd guess you probably won't need anything like this.
(I'm writing a library of sorts and I'd like my library's require
method to work exactly the same on the browser and on NodeJS
If I understand you correctly(please forgive me if not ;)) you are looking for something like node-browserify.
Browserify Browser-side require() for your node modules and npm
packages
Just point a javascript file or two at browserify and it will walk the
AST to read all your require()s recursively. The resulting bundle has
everything you need, including pulling in libraries you might have
installed using npm!

Categories