Why am I having Node.js local module importing inconsistencies? - node.js

I'm really suffering here due to some awful inconsistencies in importing/exporting modules in Node.js.
It's easier to see:
//game.js
const {Player} = require("./player");
{...}
console.log(Player); //outputs undefined
//player.js
class Player {
constructor(client, host = false) {
this.properties = {...client};
this.host = host;
this.hand = [];
}
{...}
}
module.exports = {Player};
This may seem fine but here's the strangest part. Inside another file, deck.js, I export in the exact same way. And it gets correctly recognized in game.js.
//game.js
const {Deck} = require("./deck");
console.log(Deck); //outputs "[Function: Deck]"
//deck.js
class Deck {
constructor() {
this.deck = [...compressed_cards];
this.shuffle();
}
{...}
}
module.exports = {Deck};
These are both local files, as you can see. I can import deck.js just fine but not player.js, despite the exact same methodologies. I've tried module.exports.Player = Player, I've directly set module.exports.Player = class Player {...}, it just won't seem to work. To whoever figures this out and makes me look like an idiot, thanks.
Oh, and to add to my confusion, I can import player.js in other files outside of the folder just fine. But not inside. Why. And of course, all of my other files can access each other in the exact same way without any issues.
File structure looks like this:

The reason could be that you have circular dependencies. That means player.js might require game.js or deck.js, such that when you draw lines between files that require each other, you will see a full circle. The suggestion is to restructure your dependencies to avoid such dependency structure.
More about circular/cyclic dependencies is discussed here: How to deal with cyclic dependencies in Node.js

According to this article you should be able to import / export a class like this:
//game.js
const Player = require("./player");
//player.js
class Player {...}
module.exports = Player;

Related

TS/Node.js: Getting the absolute path of the class instance rather than the class itself

Is there a way to get the path (__dirname) of the file where an instance of a class was made without passing that into the constructor?
For example,
// src/classes/A.ts
export class A {
private instanceDirname: string;
constructor() {
this.instanceDirname = ??
}
}
// src/index.ts
import { A } from "./classes/A"
const a = new A();
// a.instanceDirname === __dirname ✓
I tried callsite, it worked but I had to do some regex that I'm not happy with to get what I need, I also tried a module called caller-callsite, but that ended up returning the module path, not the path of the file where the instance was made.
Is there a workaround for this?
I would have callers pass in the location information. Sniffing this stuff seems like a code smell to me (pardon the pun). ;-)
But you can do it, by using regular expressions on the V8 call stack from an Error instance, but it still involves doing regular expressions (which you didn't like with callsite), though it's doing them on V8's own stacks, which aren't likely to change in a breaking way (and certainly won't except when you do upgrades of Node.js, so it's easy to test). See comments:
// A regular expression to look for lines in this file (A.ts / A.js)
const rexThisFile = /\bA\.[tj]s:/i;
// Your class
export class A {
constructor() {
// Get a stack trace, break into lines -- this is V8, we can rely on the format
const stackLines = (new Error().stack).split(/\r\n|\r|\n/);
// Find the first line that doesn't reference this file
const line = stackLines.find((line, index) => index > 0 && !rexThisFile.test(line));
if (line) {
// Found it, extract the directory from it
const instanceOfDirName = line.replace(/^\s*at\s*/, "")
.replace(/\w+\.[tj]s[:\d]+$/, "")
.replace(/^file:\/\//, "");
console.log(`instanceOfDirName = "${instanceOfDirName}"`);
}
}
}
Those three replaces can be combined:
const instanceOfDirName = line.replace(/(?:^\s*at\s*(?:file:\/\/)?)|(?:\w+\.[tj]s[:\d]+$)/g, "");
...but I left them separate for clarity; not going to make any performance difference to care about.

How to separate methods into different files and than bundle them in one file to require this

let's imagine you wanna build a file that contains different methods. For example: file bundler.js that need method a(), b(), c(). But these methods are very complex and take much lines of code, so it would be nice to separate those methods into there own files. So you have the files a.js, b.js and c.js.
File a.js would look like:
module.exports = {
a: data => console.log(data)
}
File b and c look exactly the same and now you want to use them in the bundle.js file.
My problem: they all are related to the same instance -> Bundle() presented by bundle.js, but when I require them in bundle.js they all would have the same variable name to follow a logical declaration.
let bundle = require('./a.js');
let bundle = require('./b.js');
let bundle = require('./c.js');
bundle.a();
bundle.b();
bundle.c();
But when I understand nodejs (and some other programming languages I guess) right, that wouldn't work. I would need a seperate layer that bundles this methods and exports it again. So I require this layer once and would have access to all methods. But I don't know how that layer would look like.
Can someone help me?
Many thanks in advice.
Ok a couple things to note here to start. 1) Your require statements aren't being done correctly. The variable bundle is basically being overwritten each time you require. You would have do something like:
let bundleA = require('./a.js');
let bundleB = require('./b.js');
let bundleC = require('./c.js');
This way, your bundle variable isn't being overwritten each time. Now, on to what you are actually asking about. You would want to make a file, in this case called bundle.js that would look something like this:
// your bundle.js
const a = require('./a.js')
const b = require('./b.js')
const c = require('./c.js')
exports.bundledA = function() {
return a;
}
exports.bundledB = function() {
return b;
}
exports.bundledC = function() {
return c;
}
Now you have a bundle.js you can include. So the next step would be to use it, something like this:
// Some file
const bundle = require('./bundle')
// Using method A
bundle.bundledA()
Hopefully all that makes sense. If you have any questions please feel free to say so in the comment and I'll try to explain as best I can. There's also a link here for exporting and creating your own modules you should read up on:
https://www.sitepoint.com/understanding-module-exports-exports-node-js/
Happy coding :-)

How to set global variables for exported class methods in node.js

I have multiple different files broken out like so:
index.js
utils.js
ClassA/
index.js
base.js
utils contains a number of utility functions used everywhere. ClassA/index.js contains the constructor for ClassA as well as requires base.js, then exports ClassA. Base.js exports prototype methods for ClassA. Here is basically what they look like:
//ClassA/index.js
function ClassA () {
//constructor stuff
}
ClassA.prototype.constructor = ClassA;
require('./base')(ClassA);
module.exports = ClassA;
//ClassA/base.js
module.exports = ClassA => {
ClassA.prototype.aMethod = function () {
log('hello');
}
//utils.js
module.exports = {
log : function (logText) {
//dostuff
}
}
So my problem is that I cannot access the log function from within methods in ClassA/base.js. If I set const log = require('../utils').log at the top of the file, it doesn't work. It also doesn't work if I place that same line of code within the exports but outside of the method definitions, however it does work if I place it within the method itself (as expected, but this would mean replicating every require statement in any method in which it's needed). Is there any way around using this messy and repetitive route?
It was an issue with cyclic dependencies. I changed 'log' to be a method of ClassA and this resolved the issue. Thanks to Francois P. for pointing me in the right direction.

Derby.js: split client-side only code into several files

I'm trying to split some client-side-only code into several files in a Derby.js project. It has to be client-side only since it's interacting with the TinyMCE editor. So I tried:
app.ready(function(model) {
var tiny = derby.use(require('../../lib/app/TinyMCE'))
//other client-side code
}
and put the following into lib/app/TinyMCE.js:
var derby = require('derby')
module.exports.decorate = 'derby'; //because before I got an 'decorate' is undefined error...
module.exports.TinyMCE = function() {
//code
}
But now I'm getting a object is not a function error.
Am I even on the right track? I also considered putting the code in the public directory, but the cache-expiration of one year makes this rather inconvenient.
Also, is there really no isServer or isClient method to query?
Okay, I don't know whether that's a good way, but I got it working:
module.exports = tiny
tiny.decorate = 'derby'
function tiny() {
//code
}

Can I load multiple files with one require statement?

maybe this question is a little silly, but is it possible to load multiple .js files with one require statement? like this:
var mylib = require('./lib/mylibfiles');
and use:
mylib.foo(); //return "hello from one"
mylib.bar(): //return "hello from two"
And in the folder mylibfiles will have two files:
One.js
exports.foo= function(){return "hello from one";}
Two.js
exports.bar= function(){return "hello from two";}
I was thinking to put a package.json in the folder that say to load all the files, but I don't know how. Other aproach that I was thinking is to have a index.js that exports everything again but I will be duplicating work.
Thanks!!
P.D: I'm working with nodejs v0.611 on a windows 7 machine
First of all using require does not duplicate anything. It loads the module and it caches it, so calling require again will get it from memory (thus you can modify module at fly without interacting with its source code - this is sometimes desirable, for example when you want to store db connection inside module).
Also package.json does not load anything and does not interact with your app at all. It is only used for npm.
Now you cannot require multiple modules at once. For example what will happen if both One.js and Two.js have defined function with the same name?? There are more problems.
But what you can do, is to write additional file, say modules.js with the following content
module.exports = {
one : require('./one.js'),
two : require('./two.js'),
/* some other modules you want */
}
and then you can simply use
var modules = require('./modules.js');
modules.one.foo();
modules.two.bar();
I have a snippet of code that requires more than one module, but it doesn't clump them together as your post suggests. However, that can be overcome with a trick that I found.
function requireMany () {
return Array.prototype.slice.call(arguments).map(function (value) {
try {
return require(value)
}
catch (event) {
return console.log(event)
}
})
}
And you use it as such
requireMany("fs", "socket.io", "path")
Which will return
[ fs {}, socketio {}, path {} ]
If a module is not found, an error will be sent to the console. It won't break the programme. The error will be shown in the array as undefined. The array will not be shorter because one of the modules failed to load.
Then you can bind those each of those array elements to a variable name, like so:
var [fs, socketio, path] = requireMany("fs", "socket.io", "path")
It essentially works like an object, but assigns the keys and their values to the global namespace. So, in your case, you could do:
var [foo, bar] = requireMany("./foo.js", "./bar.js")
foo() //return "hello from one"
bar() //return "hello from two"
And if you do want it to break the programme on error, just use this modified version, which is smaller
function requireMany () {
return Array.prototype.slice.call(arguments).map(require)
}
Yes, you may require a folder as a module, according to the node docs. Let's say you want to require() a folder called ./mypack/.
Inside ./mypack/, create a package.json file with the name of the folder and a main javascript file with the same name, inside a ./lib/ directory.
{
"name" : "mypack",
"main" : "./lib/mypack.js"
}
Now you can use require('./mypack') and node will load ./mypack/lib/mypack.js.
However if you do not include this package.json file, it may still work. Without the file, node will attempt to load ./mypack/index.js, or if that's not there, ./mypack/index.node.
My understanding is that this could be beneficial if you have split your program into many javascript files but do not want to concatenate them for deployment.
You can use destructuring assignment to map an array of exported modules from require statements in one line:
const requires = (...modules) => modules.map(module => require(module));
const [fs, path] = requires('fs', 'path');
I was doing something similar to what #freakish suggests in his answer with a project where I've a list of test scripts that are pulled into a Puppeteer + Jest testing setup. My test files follow the naming convention testname1.js - testnameN.js and I was able use a generator function to require N number of files from the particular directory with the approach below:
const fs = require('fs');
const path = require('path');
module.exports = class FilesInDirectory {
constructor(directory) {
this.fid = fs.readdirSync(path.resolve(directory));
this.requiredFiles = (this.fid.map((fileId) => {
let resolvedPath = path.resolve(directory, fileId);
return require(resolvedPath);
})).filter(file => !!file);
}
printRetrievedFiles() {
console.log(this.requiredFiles);
}
nextFileGenerator() {
const parent = this;
const fidLength = parent.requiredFiles.length;
function* iterate(index) {
while (index < fidLength) {
yield parent.requiredFiles[index++];
}
}
return iterate(0);
}
}
Then use like so:
//Use in test
const FilesInDirectory = require('./utilities/getfilesindirectory');
const StepsCollection = new FilesInDirectory('./test-steps');
const StepsGenerator = StepsCollection.nextFileGenerator();
//Assuming we're in an async function
await StepsGenerator.next().value.FUNCTION_REQUIRED_FROM_FILE(someArg);

Resources