I am developing a Nodejs module, which is a function and returns an object with a function in a property. If I call the module without any parameter returns error (Test is not a constructor). If I call the module passing a parameter, even if it is empty, it works properly.
Example #1
(App)
//var test = require('./index.js')().Test; // Works properly
var test = require('./index.js').Test;
var sample = new test('text', function(newText) {
console.log(newText);
});
(Module)
module.exports = function(options) {
var bold = 0;
var anotheModule = new anotherModule(options); //If the parameters are not defined, take the default.
console.log('Check 1');
return {
Test: Test
}
function Test(text) {
.....
}
}
Example #2
(Module)
module.exports = function textChange(options) {
var bold = 0;
var anotheModule = new anotherModule(options); //If the parameters are not defined, take the default.
console.log('Check 1');
}
textChange.prototype.Test = function(text) {
.....
}
Example #3
(Module)
function textChange(options) {
var bold = 0;
var anotheModule = new anotherModule(options); //If the parameters are not defined, take the default.
console.log('Check 1');
}
textChange.prototype.Test = function(text) {
.....
}
module.exports = textChange;
Example #4
(Module)
function textChange(options) {
var bold = 0;
var anotheModule = new anotherModule(options); //If the parameters are not defined, take the default.
console.log('Check 1');
}
Test = function(text) {
.....
}
module.exports = textChange;
export.Test = Test;
I can't get the solution to work without passing parameters. I can't get the solution to work without passing parameters. If the application calls the module with parameters, it works correctly.
The error Test is not a constructor that you're getting it is because you're using the new keyword on undefined. Since Test is undefined.
const test = undefined;
try {
new test();
} catch(e) {
console.log(e.message);
}
In your example #1 you're exporting a function that when executed returns an object containing Test, that's why you need to use () on the exported module.
In your example #2 & #3, you're exporting a function (constructor), so it is the instance of textChange which has the Test method:
const textChange = require('./index.js');
new textChange().Test();
Note: Your example #2 will throw a ReferenceError, textChange is not defined since you're assigning it directly to module.exports, so it should be:
module.exports = function textChange(options) {
var bold = 0;
console.log('Check 1');
}
module.exports.prototype.Test = function(text) {
/* ... */
}
In your last example, the last line: export should be exports but it won't do what you're expecting.
What's being exported it's module.exports, which is a function, textChange to be precise, so Test will never be available outside of that module.
var test = require('./module.js');
console.log(test.Test); // undefined
Check this question: module.exports vs exports in Node.js
I don't know exactly what you're trying to achieve, but if you want
var test = require('./index.js').Test;
You should export an object containing Test property.
Module
module.exports = {
Test(text) {
/* ... */
}
}
or
function Test(text) {
console.log('Text: ', text);
}
module.exports.Test = Test;
I have a module with some initialization code inside. The init should be performed when the module is loaded. At the moment I'm doing it like this:
// in the module
exports.init = function(config) { do it }
// in main
var mod = require('myModule');
mod.init(myConfig)
That works, but I'd like to be more concise:
var mod = require('myModule').init('myConfig')
What should init return in order to keep mod reference working?
You can return this, which is a reference to exports in this case.
exports.init = function(init) {
console.log(init);
return this;
};
exports.myMethod = function() {
console.log('Has access to this');
}
var mod = require('./module.js').init('test'); //Prints 'test'
mod.myMethod(); //Will print 'Has access to this.'
Or you could use a constructor:
module.exports = function(config) {
this.config = config;
this.myMethod = function() {
console.log('Has access to this');
};
return this;
};
var myModule = require('./module.js')(config);
myModule.myMethod(); //Prints 'Has access to this'
How can i initialize async.queue "for" the global module scope?
The example bellow shows the main problem, that qq is undefined, not yet known or its only defined locally in a function scope.
The target is to access a "module-global" q in different module member functions.
So creating a module pattern version of the example in https://github.com/caolan/async#queue
I know why the // not working-code isn't valid, it is only too show which declaration ideas i tried.
Additional i m aware of how to solve the problem by using a different pattern, but that wouldn't answer the question.
var mymodule = (function() {
'use strict';
var async = require('async');
// var q = async.queue(mymodule.qq); // not working
// var q ; // not working
var mymodule = {
// q = async.queue(this.qq); // not working
init: function() {
// var q = async.queue(this.qq); // local not global
// q = async.queue(this.qq); // not working
q.drain = function() {
console.log('all items have been processed');
}
},
add: function(task) {
this.q.push(task);
},
qq: function(task, callback) {
console.log(task);
callback();
},
};
return mymodule;
}());
'use strict';
var async = require('async');
var mymodule = function(){
//This will be you constructor
//You can do something like this
this.queue = async.queue(function(task, callback){
console.dir(task);
}, 4);
};
//Now start adding your methods
mymodule.prototype.add = function(task){
this.queue.push(task, function(){});
};
mymodule.prototype.qq = function(task, callback){
// ..
callback()
};
//export it
module.exports = mymodule;
How would I require() a file if I had the file's contents as a string in memory, without writing it out to disk? Here's an example:
// Load the file as a string
var strFileContents = fs.readFileSync( "./myUnalteredModule.js", 'utf8' );
// Do some stuff to the files contents
strFileContents[532] = '6';
// Load it as a node module (how would I do this?)
var loadedModule = require( doMagic(strFileContents) );
function requireFromString(src, filename) {
var Module = module.constructor;
var m = new Module();
m._compile(src, filename);
return m.exports;
}
console.log(requireFromString('module.exports = { test: 1}', ''));
look at _compile, _extensions and _load in module.js
The question is already answered by Andrey, but I ran into a shortcoming that I had to solve and which might be of interest for others.
I wanted the module in the memorized string to be able to load other modules via require, but the module path was broken with the above solution (so e.g. needle wasn't found).
I tried to find an elegant solution to maintain the paths, by using some existing function but I ended up with hard wiring the paths:
function requireFromString(src, filename) {
var m = new module.constructor();
m.paths = module.paths;
m._compile(src, filename);
return m.exports;
}
var codeString = 'var needle = require(\'needle\');\n'
+ '[...]\n'
+ 'exports.myFunc = myFunc;';
var virtMod = requireFromString(codeString);
console.log('Available public functions: '+Object.keys(virtMod));
After that I was able to load all existing modules from the stringified module.
Any comments or better solutions highly appreciated!
The require-from-string package does the job.
Usage:
var requireFromString = require('require-from-string');
requireFromString('module.exports = 1');
//=> 1
After analyzing the source code of such solutions as pirates and require-from-string, I came to the conclusion that a simple mock of fs and Module methods would be no worse in terms of support. And in terms of functionality it will be better, because it supports #babel/register, pirates and other modules that changes the module loading process.
You can try this npm module require-from-memory
import fs from 'fs'
import BuiltinModule from 'module'
const Module = module.constructor.length > 1 ? module.constructor : BuiltinModule
function requireFromString(code, filename) {
if (!filename) {
filename = ''
}
if (typeof filename !== 'string') {
throw new Error(`filename must be a string: ${filename}`)
}
let buffer
function getBuffer() {
if (!buffer) {
buffer = Buffer.from(code, 'utf8')
}
return buffer
}
const now = new Date()
const nowMs = now.getTime()
const size = Buffer.byteLength(code, 'utf8')
const fileStat = {
size,
blksize : 4096,
blocks : Math.ceil(size / 4096),
atimeMs : nowMs,
mtimeMs : nowMs,
ctimeMs : nowMs,
birthtimeMs: nowMs,
atime : now,
mtime : now,
ctime : now,
birthtime : now
}
const resolveFilename = Module._resolveFilename
const readFileSync = fs.readFileSync
const statSync = fs.statSync
try {
Module._resolveFilename = () => {
Module._resolveFilename = resolveFilename
return filename
}
fs.readFileSync = (fname, options, ...other) => {
if (fname === filename) {
console.log(code)
return typeof options === 'string'
? code
: getBuffer()
}
console.log(code)
return readFileSync.apply(fs, [fname, options, ...other])
}
fs.statSync = (fname, ...other) => {
if (fname === filename) {
return fileStat
}
return statSync.apply(fs, [fname, ...other])
}
return require(filename)
} finally {
Module._resolveFilename = resolveFilename
fs.readFileSync = readFileSync
fs.statSync = statSync
}
}
Based on Andrey Sidorov & Dominic solutions, I was saddened by the fact of not being able to require a stringified module then I suggest this version *.
Code:
void function() {
'use strict';
const EXTENSIONS = ['.js', '.json', '.node'];
var Module,
path,
cache,
resolveFilename,
demethodize,
hasOwnProperty,
dirname,
parse,
resolve,
stringify,
virtual;
Module = require('module');
path = require('path');
cache = Module._cache;
resolveFilename = Module._resolveFilename;
dirname = path.dirname;
parse = path.parse;
resolve = path.resolve;
demethodize = Function.bind.bind(Function.call);
hasOwnProperty = demethodize(Object.prototype.hasOwnProperty);
Module._resolveFilename = function(request, parent) {
var filename;
// Pre-resolution
filename = resolve(parse(parent.filename).dir, request);
// Adding extension, if needed
if (EXTENSIONS.indexOf(parse(filename).ext) === -1) {
filename += '.js';
}
// If the module exists or is virtual, return the filename
if (virtual || hasOwnProperty(cache, filename)) {
return filename;
}
// Preserving the native behavior
return resolveFilename.apply(Module, arguments);
};
Module._register = function(request, parent, src) {
var filename,
module;
// Enabling virtual resolution
virtual = true;
filename = Module._resolveFilename(request, parent);
// Disabling virtual resolution
virtual = false;
// Conflicts management
if (hasOwnProperty(cache, filename)) {
error = new Error('Existing module "' + request + '"');
error.code = 'MODULE_EXISTS';
throw error;
}
// Module loading
cache[filename] = module = new Module(filename, parent);
module.filename = filename;
module.paths = Module._nodeModulePaths(dirname(filename));
module._compile(stringify(src), filename);
module.loaded = true;
return module;
};
stringify = function(src) {
// If src is a function, turning to IIFE src
return typeof src === 'function'
? 'void ' + src.toString() + '();'
: src;
};
}();
void function() {
var Module,
parentModule,
child;
Module = require('module');
// Creating a parent module from string
parentModule = Module._register('parent', process.mainModule, `
module.exports = {
name: module.filename,
getChild: function() {
return require('child');
}
};
`);
// Creating a child module from function
Module._register('child', parentModule, function() {
module.exports = {
name: module.filename,
getParent: function() {
return module.parent.exports;
}
};
});
child = require('child');
console.log(child === child.getParent().getChild());
}();
Usage:
void function() {
var Module,
parentModule,
child;
Module = require('module');
// Creating a parent module from string
parentModule = Module._register('parent', process.mainModule, `
module.exports = {
name: module.filename,
getChild: function() {
return require('child');
}
};
`);
// Creating a child module from function
Module._register('child', parentModule, function() {
module.exports = {
name: module.filename,
getParent: function() {
return module.parent.exports;
}
};
});
child = require('child');
console.log(child === child.getParent().getChild());
}();
* as you can see, it contains a function formater which provides a way to create some modules from functions.
I think the better way to approach this would be to have a parameter that you could set afterwards...
such as inside the file: myUnalteredModule.js
exports.setChanges = function( args )...
Then you could do:
var loadedModule = require( 'myUnalteredModule' );
loadedModule
I have a module "sitescollection" like this:
var site = require('./site'); // <- this should be stubbed
var sitesCollection = function(spec) {
var that = {};
that.sites = {};
that.findOrCreateById = function(id) {
if (typeof(that.sites[id]) == "undefined") {
that.sites[id] = site({id: id}); // <- its used here
}
return that.sites[id];
};
return that;
};
module.exports = sitesCollection;
so within sitescollection, site is a module that is not exported. But inside the code, i use it. Now i'm writing jasmine specs for #findOrCreateById().
I want to spec my the findOrCreateBy() function. But i want to stub the site() function, because the spec should be independent from the implementation. Where do i have to create the spyed method on?
var sitescollection = require('../../lib/sitescollection');
describe("#findOrCreateById", function() {
it("should return the site", function() {
var sites = sitescollection();
mysite = { id: "bla" };
// Here i want to stub the site() method inside the sitescollection module.
// spyOn(???,"site").andRetur(mysite);
expect(sites.findOrCreateById(mysite.id)).toEqual(mysite);
});
});
You can achieve this using https: //github.com/thlorenz/proxyquire
var proxyquire = require('proxyquire');
describe("#findOrCreateById", function() {
it("should return the site", function() {
// the path '../../lib/sitescollection' is relative to this test file
var sitesCollection = proxyquire('../../lib/sitescollection', {
// the path './site' is relative to sitescollection, it basically
// should be an exact match for the path passed to require in the
// file you want to test
'./site': function() {
console.log('fake version of "./site" is called');
}
});
// now call your sitesCollection, which is using your fake './site'
var sites = sitesCollection();
var mysite = {
id: "bla"
};
expect(sites.findOrCreateById(mysite.id)).toEqual(mysite);
});
});