Scopes in context of Modules in Node.js - javascript

I need to understand the concept of scopes in Node.js. The fact that this === global, when I try the code below
//basic1.js file
this.bar = "Bacon";
//basic2.js file
require('./basic1');
console.log(this.bar);
and run basic2.js, output is undefined instead of Bacon. Since I am assigning a property bar in global object and as global object is shared by all the node modules, why am I getting undefined as output? Can you help me understand that.

To understand how node.js interpret modules better to look at source code:
Read source code from file.
Wrap source into function call like function(exports, require, module, __dirname, __filename){ /* source code */ }
Evaluate wrapped code into v8 virtual machine (similar to eval function in browser) and get function.
Call function from previous step with overwriting this context with exports.
Simplified code:
var code = fs.readFileSync('./module.js', 'utf-8');
var wrappedCode = `function (exports, require, module, __dirname, __filename) {\n${code}\n}`;
var exports = {};
var fn = vm.runInThisContext(wrappedCode);
var otherArgs = [];
// ... put require, module, __dirname, __filename in otherArgs
fn.call(exports, exports, ...otherArgs);

Related

How do I mock non-global node variables _filename or __dirname in jest?

__filename and __dirname are the absolute file paths in which a code is entered. Per Node.js documentation (and confirmed) these 2 special global variables are injected rather than being part of the the global object so cannot be configured via globalThis/global within jest.
Example function: utils.ts
export const getFileAndDirName = () => ({
relevantFile: __filename.split('/')?.[3],
rootPath: __dirname,
})
Example test code which does not work: utils.test.ts
it('finds the file path where the function was executed', () => {
globalThis.__filename = '/Users/me/work/utils/index.ts'
expect(getFileAndDirName()).toEqual('utils')
globalThis.__filename = '/Users/me/work/tests/index.ts'
expect(getFileAndDirName()).toEqual('tests')
})
Alternatives/Work arounds:
I'm aware that I can refactor my code to still be able to test this:
export const safelyExtractThirdElement = (filePath = '') => filePath.split('/')?.[3]
export const getFileAndDirName = () => ({
relevantFile: safelyExtractThirdElement(__filename),
rootPath: __dirname,
})
This solution enables me to avoid mocking __filename... however ideally I wouldn't need to change my actual code and keep private internal utilities un-tested, and also this is a bit of a simplified arbitrary example compared to my own.
I can also hack my code for this to work, but I feel that testing should make code stronger not hackier:
__filename = globalThis.__filename || __filename // will probably work since globalThis.__filename is never defined.
With this strategy I can mock __filename and allow my code to read it but this is again hacking implementation.
Is there some ideal/not-so ideal strategy for mocking these special variables in jest?
The real question is if it is possible to mock __dirname or __filename or should I accept that these may not be mocked for node jest and circumvent that limitation?
Background: __filename, __dirname
The fact that this doesn't work does not surprise me: if we look at the Node.js documentation for __filename it indicates plainly that __filename and __dirname you may assume are globals but are NOT.
__dirname#
This variable may appear to be global but is not. See __dirname.
__filename#
This variable may appear to be global but is not. See __filename.
https://nodejs.org/api/globals.html#__dirname
(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
});
The question is if it is possible, how would one mock __filename or __dirname with jest. Jest documentation makes it pretty clear how to mock and temporarily set functions using globalThis, but what about these non-global globals?

Load a javascript file into a module pattern on Client-side

I have a simple javascript module pattern that executes client-side.
var module = (function() {
var _privateVariable = 10;
function publicMethod () {
console.log("public method; private variable: " + _privateVariable);
}
return {
publicMethod: publicMethod
};
})();
Let's say I want to load in another module (which also uses a module pattern) from a separate javascript file. How do I do that, i.e. something like:
?? Load other module here ??
var _other_module = ??
var module = (function() {
var _privateVariable = 10;
function publicMethod () {
console.log("public method; private variable: " + _privateVariable);
console.log("public method; other module: " + other_module.publicMethod());
}
return {
publicMethod: publicMethod
};
})();
You can't. To load another module form another file you need to use a Module formats.
It's a long story, i will try to shorten.
Let's talk first about the old way. earlier the developer used to load alle the JS-Files in a certain order in the HTML-Page. If we have 2 JS-Files index.js and variables.js and we want to get a variable from varible.js in index.js, we had load them like that
<script src="variables.js"></script>
<script src="index.js"></script>
But this is not a good way and has many negatives.
The right way is to use a Module formats.
There are many Module formats,
Asynchronous Module Definition (AMD)
CommonJS
Universal Module Definition (UMD)
System.register
ES6 module format
and each format has own Syntax.
For example CommonJS:
var dep1 = require('./dep1');
module.exports = function(){
// ...
}
but the Browsers dont't understand that. so we need a Module bundlers or Module loaders
to convert our code to code which browsers can understand.
Module bundlers: Bundle your inter-dependent Javascript files in the correct order
Browserify
Webpack
Module loaders: a module loader interprets and loads a module written in a certain module format.
RequireJS
SystemJS
This article will help you so much to understand exactly how modules work.
Not sure which context you are doing this.
But. In node.JS this would be typically done by
module1.js
module.exports.publicMethod = function() {}
index.js
const module1 = require('./module1.js');
module1.publicMethod();
or
const {publicMethod} = require('./module1.js');
publicMethod();

Why dirname is not a module atribute? (__ notation)

I'm studying the basics of node. According to the documentation, both __dirname and __filename belong to module scope. As expected, both:
console.log(__dirname)
console.log(__filename)
Work, printing current dirname and filename.
But when i try calling them from module, only filename works:
console.log(module.dirname)
console.log(module.filename)
The first one prints undefined.
1 - Why console.log(module.dirname) prints undefined?
2 - I'm confused about __notation. If it's not a sugar sintax for module., what it is used for?
Those variables are defined as arguments to a function that is wrapped around your module code like this:
(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
});
This does several things:
It provides a scope for your module (the function scope of the wrapper function). Any top level variables you declare in your module will actually be scoped to the module, not at the top, top scope.
It provides the values of require, module, __filename and __dirname as independent variables that are unique for each module and cannot be messed with by other modules.
The properties of the module object (collected with Object.getOwnPropertyNames(module) are:
'id',
'exports',
'parent',
'filename',
'loaded',
'children',
'paths'
Why dirname is not a module attribute?
The "why" would have to go into the head of the designer and discussion at the time the module system was designed. It's possible that a module object might be accessible by some other code and passing __dirname and __filename as arguments to the wrapper made it so that even something else who had access to your module object couldn't mess with __dirname and __filename. Or, it could have just been done for shortcuts in typing. Or, it could have just been a personal design choice.
Why console.log(module.dirname) prints undefined?
Because dirname is not a property of the module object itself (I have no idea why). Instead, __dirname is passed as the wrapper function argument.
I'm confused about __notation. If it's not a sugar syntax for module., what it is used for?
Usually, the _ or __ prefix is just used to make sure it doesn't accidentally collide with any regular variable declaration in the code. My guess is that the original designers of node.js envisioned existing code bases being used in modules and they wanted to lessen the chances of a variable naming collision.
Some of your question seems to be about why aren't all of these just properties of the module object. I can think of no particular reason why that wouldn't have been a perfectly normal and logical design.
To save typing, there are object lots of references to require so I can certainly see an argument for not making us all type
module.require('someModuleName') every time.
And, exports is already a property of module. For example, module.exports === exports. Using it as module.exports does have some use because you can define a whole new object for the exports:
module.exports = {
greeting: "hello",
talk: function() {...}
};
But, you cannot do that by just reassigning the exports. This will not work:
exports = {
greeting: "hello",
talk: function() {...}
};
So, I guess exports is also supplied as a shortcut when you just want to do:
exports.talk = function() {...}
But, module.exports is needed if you're assigning a whole new exports object.
Maybe this can help
const fs = require('fs');
dirname = fs.realpathSync('.');
Now, it's upon you what you are using 'dirname' of 'dirname'

Hoisted local variable masking global variable?

Came across strange behavior in node. I have an emscripten compiled program that I would like to use as a library. emscripten use a variable Module to control runtime behavior, and generates code like ./lib/g.js below. In the browser, Module is properly aliased from the global defined in index.js to the local var in ./lib/g.js. However, in node, this does not seem to be the case. The construct: var Module = Module || {}; wipes out my global Module.
index.js:
global.Module = { name: 'Module' };
var g = require ( './lib/g.js' );
./lib/g.js:
console.log ( 'Module: ', Module );
var Module = Module || {};
Output of node index.js:
Module: undefined
I assume in g.js, Module is hoisted and dereferenced only in the local scope, masking the global version (in global.Module). Can anyone suggest a work-around?
This problem can be fixed by editing the emscripten produced code to use var Module = global.Module || {}. While this is a possible workaround, I would rather not edit the code generated by emscripten.
You might take a look at using rewire
var rewire = require("rewire");
var g = rewire("./lib/g");
g.__set__("Module", {name: "Module"});
Can anyone suggest a work-around?
Just remove the var keyword.

Write javascript code that works with client side javascript and server side NodeJs modules

Browserify and lo-dash, do something special. Ok, but should like to be free from other vendor libraries or nodejs modules. I want to write reusable code. So, ...
I can write some javascript code. Also, I can write NodeJs code. Ok: I can write a module for NodeJs, for server-side code but at some point I need to write a NodeJs code to export, for example, a module.
var MyModule = function () {
this.attribute = 666
}
module.exports = MyModule
But, ... it's a NodeJs module. If I try to include it in client page ...
<script src="lib/myModule.js">
I'll get 'Uncaught ReferenceError: module is not defined'. Why? Maybe, because it's NodeJs code, and not Javascript. Ok but, ... Which are best practice to share same code in client and server side with NodeJs. Ok, I can share a javascript file, and use it in both sides. How can I write a module, that works with a javascript code, that I want to use also in client side?
What I am looking for, is:
Write some javascript code. my_public_file.js:
console.log('I am a javascript snippet')
Then, I want to write a module that works with same code. my_module.js
var lib_public_code = require('some/public/path/my_public_file.js')
var MyModule = function () {
this.attribute = 666
}
module.exports = MyModule
And, also, I wanto to write a public web page (index.html) that works with the same code
<script src="javascript/my_public_file.js">
Best practices? I am insane?
To make it work both client-side and server-side, just export it if module.exports exists:
var MyModule = function () {
this.attribute = 666;
};
if (typeof module !== "undefined" && module.exports) {
module.exports = MyModule;
}
This, and similar ways are used by a lot of libraries to enable packages to be used on CommonJS and the browser (e.g.: jQuery)
You install browserify
$ npm install -g browserify
you take your my_public_file.js whatever node code etc.
var lib_public_code = require('some_file.js')
var MyModule = function () {
this.attribute = 666
}
module.exports = MyModule
you run the magic
$ browserify my_public_file.js -o bundled_public.js
then it works in the browser and the required files are all there.
<script src="bundled_public.js">
Use require.js both on client and on node.js. Already done several apps which shares the code between client and server side.
To write a reusable javascript module. I will use the code below, which will work with browser, nodejs and requirejs.
(function(root, factory) {
if (typeof module !== 'undefined' && typeof exports === 'object') {
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
define([], factory);
} else {
root.myModule = factory();
}
}(this, function() {
var myModule = {};
myModule.publicMethod = function() {};
return myModule;
}));
The factory function is being invoked and we can assign it to the relevant
environment inside. In nodeJs, export.modules = factory(); . In requireJs,
define([], factory);. In the browser, root.myModule = factory();, because root === window, we can use myModule as a global variable.

Categories

Resources