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

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'

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?

Scopes in context of Modules in Node.js

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);

JavaScript Faux Pas - Let me put global libraries on the window?

I'm using RequireJS. I absolutely hate the double variable syntax of defining a dependency and passing it in as a variable in a callback function. I am therefore attempting the implement the 'Sugar' syntax available in RequireJS.
However, I only want to 'import' global libraries like Backbone, jQuery, Underscore and Marionette once. jQuery and Backbone obviously assign themselves to the Window object but Underscore and Marionette do not.
This is my main.js file:
require.config({
paths: {
"jquery" : "vendor/jquery.min",
"underscore": "vendor/underscore-min",
"backbone" : "vendor/backbone-min",
"marionette" : "vendor/marionette",
"app" : "app/app"
}
});
define(function(require, exports, module) {
// Make these libraries available globally
var jquery = require('jquery');
window.underscore = require('underscore');
var Backbone = require('backbone');
window.marionette = require('marionette');
// Require and start our own app
var app = require('app');
app.start();
});
This obviously stops me from having to import/require each of these core libraries into every subsequent module/component for my application. Taking my code from potentially this (app.js file):
define(function (require, exports, module) {
var jquery = require('jquery'),
underscore = require('underscore'),
Backbone = require('backbone'),
Marionette = require('marionette'),
// module specific libs
mymodule = require('../js/app/module'),
logger = require('../js/app/logger');
return {
start: function () {
var testview = new mymodule();
logger.logme();
}
}
});
To this (better app.js):
define(function (require, exports, module) {
var mymodule = require('../js/app/module'),
logger = require('../js/app/logger');
return {
start: function () {
var testview = new mymodule();
logger.logme();
}
}
});
Much cleaner IMO.
So thoughts? Criticisms? Are these going to play well together? (two already do it themselves - why not for the other two if they are core to the app).
In my head I don't think it will be a problem as long as I don't start hammering every module/component/library onto the global scope but I'm interested for someone more experienced to weigh in.
If I'm doing it wrong or there is a better way let me know!
Thanks
EDIT: After reading your comments, I realized your question has two components. One component is purely syntactical - and my original answer addresses a possible solution to that component. However, to the extent that your solution also incorporates an application design component, whereby dependencies of individual modules are defined at the application level, my answer is that is "bad practice." Dependencies should be defined at the module level (or lower) so that every element of your application is decouplable. This will be an enormous benefit in writing code that is (1) reusable, (2) testable, (3) refactorable. By defining your dependencies at the application level, you force yourself into loading the entire application simply to access a single module, for example, to run a test - and you inhibit rearranging modules or reuse of the same modules in other projects. If you do this, in the long run... you're gonna have a bad time.
Now, to the extent that your question is syntactical...
ORIGINAL ANSWER: I agree that require.js syntax is ugly as hell, annoying to use, and a potential source of difficult to debug errors. My own approach was to implement the following wrapping function (pardon the coffeescript).
# Takes dependencies in the form of a hash of arguments with the format
# { path: "name"}
# Assigns each dependency to a wrapper variable (by default "deps"),
# without the need to list each dependency as an argument of the function.
PrettyRequire = (argHash, callback) ->
deps = new Array()
args = new Array()
#loops through the passed argument, sorts into two arrays.
for propt of argHash
deps.push(propt)
args.push(argHash[propt])
#calls define using the 'dependency' array
define(deps, ()->
deps = new Array()
# assigns the resulting dependencies to properties of the deps object
for arg, i in args
deps[arg] = arguments[i]
# runs callback, passing in 'deps' object
return callback(deps)
)
This code is a simple rewrite which (1) preserves scope and (2) prettifies the syntax. I simply include the function as a part of a internal library that I maintain, and include that library at the outset of any project.
Define can then be called with the following (imho) prettier syntax:
PrettyRequire({
'backbone': 'Backbone'
'vendor/marionette': 'Marionette'
'../js/app/module': 'myModule'
}, (deps)->
Backbone.Model.extend(...) # for dependencies that assign themselves to global
deps.myModule(...) # for all dependencies (including globals)
)
That's my approach, to the extent that it's responsive to your question. It's worth noting also that your apps will take a (small) performance hit as a result of the increased overhead, but as long as you're not calling too many sub modules, it shouldn't be too much of an issue, and to me, it's worth it not to have to deal with the double syntax you describe.

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.

How do you request a named module with requirejs

I think I'm just missing something simple but I've been struggling with requirejs (v2.1.14). Say I have a module defined with a name (perhaps from the r.js optimizer): i.e.
// mymodule.js
define("modname", ['dep1', 'dep2', 'dep3'], function(){ ... });
If I try to require that module elsewhere it doesn't work. I've tried using
require(['../path/to/file/mymodule'], function(mod){ // mod is undefined here. }
which results in mod being undefined, and
require(['modname'], function(mod){...}
which errors looks for a file named modname.js in the current directory. As soon as I remove the module name part of the define statement from the output then it works fantastically.
define(['dep1', 'dep2', 'dep3'], function(mod){ // mod is defined and good here }
Am I missing some fundamental piece of requiring named modules??
Require will not know where to look for named modules if they are not in the base directory (or defined in the same file as the require statement), so you need to add a paths entry. You can't use the relative path syntax.
In your config:
paths: {
'modname': 'path/to/mymodule',
},
The optimizer uses this to define multiple modules, while keeping them in a single file.

Categories

Resources