NodeJS - Can I detect when called from command line (ES6 + Babel)..? - javascript

In my module I need to detect when it's being called from either the command line, or from another module.
const isFromCLI = '????'
I'm using Babel/ES6, so when called from a command line, index.js is called (with the babel code), which hands off to script.js (with ES6 code). So from the script file, module.parent returns module (the index.js file). So I can't use module.parent!
Also, module.main is undefined (in script.js) when called from either the command line or from another module. So I can't use module.main!
Those are the two solutions that others are suggesting, but they don't work for me.
Is there a simple to detect this when using Babel/ES6..?
Update
require.main returns module when called from either the command line or from another module.

You could use Process in Node.
https://nodejs.org/api/process.html#process_process_argv
Check the value of the second parameter (do a contains type match). Not sure if this is the only way but it's a straight forward way to achieve what you need
A code snippet might be:
const isFromCLI = (process.argv[1].indexOf('my-script.js') !== -1);

You can use Node Environment Variables.
You can set an environment variable like this:
CLI=true node app.js
and then get the environment variable like this:
const isFromCLI = process.env.CLI === 'true'
Note: process.env.CLI will be a string.
Update:
If you want to do something like node app.js --cli, you can do the following:
let isFromCLI
process.argv.forEach(function (val, index, array) {
if (array[index] === '--cli') {
isFromCLI = true
}
})
console.log(isFromCLI)

Related

Hijacking node require even when spawning another script

I am trying to replace a specific package using
import Module from 'module';
const {require: oldRequire} = Module.prototype;
Module.prototype.require = function twilioRequire(file) {
if (file === 'the-package-of-interest) {
// return something else
}
return oldRequire.apply(this, arguments);
};
const p = require('the-package-of-interest');
// this gives me the replacement
This would work fine, but if this was placed inside a script that spawns another script, this does not work in the other script, i.e
// main.js
import Module from 'module';
import spawn from 'cross-spawn';
const {require: oldRequire} = Module.prototype;
Module.prototype.require = function twilioRequire(file) {
if (file === 'the-package-of-interest) {
// return something else
}
return oldRequire.apply(this, arguments);
};
spawn.sync('node', ['/path/to/another/script.js'], { stdio: "inherit" });
// another/script.js
require('the-package-of-interest');
// gives the original package, not the replacement
I don't suppose there is a way to spawn another script, but keep the hijacked require scope the same?
Even though mocking libraries are usually used in tests, this might be a good situation to stub another library or file you wrote with another one based on a configuration.
There are a lot of mocking libraries for node, and there's a good article which covers some of them.
The library it recommends is pretty good but you can use whatever you want to do this
Create a mock definitions file that replaces that import with the second file
First - define your mocks. You can do it in any place, this is just a setup.
Easiest way is to do this in the same file, but you can create a mock defitions file if you like
import rewiremock from 'rewiremock';
...
// by mocking the module library, you actually replace it everywhere
rewiremock('module')
.with(otherModule);
your other module need to have the same export for this to work and act with the same interface.
Replace the library based on a variable
To use, you need some sort of condition that will select if to use the original library or the second one
// This is only needed if you put the mock definitions in another file
// Just put the rest of the code under the previous snippter
// if everything is in the same file
require('<your_rewiremock_definitions_file>');
if (replaceImport) {
rewiremock.enable();
}
// run the code here
if (replaceImport) {
rewiremock.disabled();
}
Your module should be replaced everywhere by the second module.
You can use any other mocking library to do this. there's a short section at the start of the readme with different options if you want a different style or another library.
rewiremock works by replacing the node.js cache so it should change the dependency everywhere.

How to pass arguments/parameters to mocha tests invoked via Grunt

I have a Gruntfile.js through which i'm invoking mochaTest using grunt-mocha-test module. I can pass an argument/parameter to the gruntTask from command line but i'm struggling to pass the same parameter into the spec file running via the above module. The code looks like below,
mochaTest: {
test: {
options: {
reporter: 'spec',
quiet: false,
clearRequireCache: false,
clearCacheFilter: (key) => true,
noFail: false
},
src: [
'test/createSpec.js'
]
}
}
Task is registered like below,
grunt.registerTask('e2etest', function(scope) {
console.log(scope); // logs user/session based on the parameter passed
grunt.task.run('mochaTest');
});
// Above task is invoked like,
grunt e2etest:user
(or)
grunt e2etest:session
I need to pass this value (user/session) into mochaTest so it can be accessed inside the spec file. Fundamentally the aim is to run the createSpec.js file both for user and session, this values is parametrized inside the spec file and based on the value passed the suite would run.
Is there a possibility to do this? Please advise.
Please refer to this issue for detail, and I think the solution you need here is:
node <node flags here> node_modules/mocha/bin/_mocha <mocha arguments here>
You can utilize nodes process.argv to read the argument (i.e. user or session) from within the file named createSpec.js.
To better understand how, follow these steps:
At the top of createSpec.js add the following line of code:
console.log(process.argv);
Then run grunt e2etest:user via your CLI and you should see the following logged to your console:
[ 'node', '/usr/local/bin/grunt', 'e2etest:user' ]
Note: the information you want is positioned at index two of the array.
Now, delete the line we just added which reads console.log(process.argv); from createSpec.js.
createSpec.js
So, the steps above (1-3) illustrated that the arguments (user or session) can be accessed in createSpec.js utilizing process.argv. In which case you could do something like the following inside createSpec.js.
const argument = process.argv[2].split(':').pop();
if (argument === 'user') {
// Run `user` specific tests here...
} else if (argument === 'session') {
// Run `session` specific tests here...
}
Note, we're using process.argv[2].split(/:/).pop(); to extract either user or session from the array item positioned at index two, whose initial value will be either e2etest:user or e2etest:session respectively.
Gruntfile
Your createSpec.js file is now somewhat dependent on the grunt task named e2etest being invoked correctly. For example, if a user were to run grunt e2etest without providing the arguments then createSpec.js is not going to do much.
To enforce the correct usage of the e2etest task (i.e. it must be run using either grunt e2etest:user or grunt e2etest:session), you could change your task in your Gruntfile as follows:
grunt.registerTask('e2etest', function(scope) {
if (!scope || !(scope === 'user' || scope === 'session')) {
grunt.warn(`Must be invoked with: ${this.name}:user or ${this.name}:session`);
}
grunt.task.run('mochaTest');
});
The gist above initially checks that an argument has been provided and is either user or session. If the argument is incorrect or missing then grunt.warn is utilized to warn the user.
If your version of nodejs does not support ES6 Template literals then use grunt.warn as follows instead:
grunt.warn('Must be invoked with: ' + this.name + ':user or ' + this.name + ':session');
Additional comment
The code/gist shown in the createSpec.js section above will work if your use-case is exactly as you mention in your question. I.e. you invoke via the commandline using grunt e2etest:user or grunt e2etest:session. However, if that changes and you cannot guarantee that e2etest:user or e2etest:session will be exactly positioned at index two of the process.argv array, then you may need to do the following at the top of createSpec.js instead:
// Return the value in the array which starts with
// `e2etest` then extract the string after the colon `:`
const argument = process.argv.filter(arg => {
return arg.match(/^e2etest:.*/);
}).toString().split(':').pop();
if (argument === 'user') {
// Run `user` specific tests here...
} else if (argument === 'session') {
// Run `session` specific tests here...
}

Require module in Node JS

I used Node JS for web application development. I have a confusion in require() module. I am requiring a JS file located in file_handler directory.
What is the difference between both of the following?
// in server.js
var chat = require("./file_handler/chat.js"); // Does not work
OR
var chat = require("./file_handler/chat.js")(); // It works
Why is the extra parenthesis in the last of the statement?
In the first line the exported function is assigned to chat variable so then you can call it like next like chat();
In the second one the return of exported function is returned to chat variable.
It is actually based on what you export in your module. If you export the object you need, you can just directly do require('module'). If you export a function which returns the object you need, you have to execute that exported function require('module')() to get the desired object.
Read the documentation https://nodejs.org/api/modules.html

Using require('...') with a variable vs. using a string in webpack

I have a problem that doesn't make much sense to me.
I'm mapping an array of objects that have a "name" and a "href" property.
let appleIcons = _.map(appleIcons, appleIcon => {
appleIcon.href = require(appleIcon.href);
return appleIcon;
});
Inside of the loop I want to require the image but it throws an error ".*$:11 Uncaught Error: Cannot find module".
When I print the value of appleIcon.href and i try to put it directly into the require('') it works.
appleIcons = _.map(appleIcons, appleIcon => {
appleIcon.href = require('./../../mobile-config/apple-icon-57x57.png');
return appleIcon;
});
So can you explain me why the second example works and the first one throws an error? How do i put a variable inside of require('')?
Thanks!
Since Webpack is running in build-time, it can't figure out which modules to bundle when the name is a dynamic variable. You can give it hints by specifying part of the path (for example, if you know all the modules are in a single directory).
This answer can help:
https://stackoverflow.com/a/33048000
(Also check require.context by Webpack. Another example is karma tests, here.)
Alternatively - if you know the filenames in advanced, it's better to add another build step to output them a strings to the file, that way Webpack can bundle them.
Adding an empty string fixed the problem for me. So, below code should work:
let appleIcons = _.map(appleIcons, appleIcon => {
appleIcon.href = require('' + appleIcon.href);
return appleIcon;
});

How do you manage namespace in Meteor?

So here is my problem :
Currently, I have a dozen of functions related to WEBRTC within a template js file. My objective is to have those functions in a separate file, called webRTCWrapper.js for example, and to call those functions in my template without using global variable.
I think I must use namespaces, am I correct ?
If so, how do you use them ?
EDIT : For anyone interested, this is exactly what I was looking for :
http://themeteorchef.com/snippets/using-the-module-pattern-with-meteor/
Make a directory called packages/ parallel to your .meteor/ directory. You can create a package that exports a single object/function. On the command line, use meteor create --package <yourpackagename> and meteor add <yourpackagename> You can edit the js file to add a namespace.
MyNamespace = {};
MyNamespace.myFunction = function () { };
Then, in the package.js, simply export that namespace.
api.export('MyNamespace');
You can use a common pattern of having a global object and your functions inside that object.
Greetings = {
hello: function(name) { return "Hello "+name+" how are you?"; }
}
And then you can call it inside the template helpers :
Template.GreetingsTemplate.helpers({
sayHello: function() { return Greetings.hello('Maxence'); }
})
Take note of the loading order of files in Meteor, anything inside the lib folders is loaded first. If you run into problems where "Greetings" object is not defined, then its because that file was not loaded already.
Edit:
You can reuse the same pattern for adding more functions in different files (you could use App = App || {} but it will throw error in Chrome for example).
App = (typeof App === 'undefined')? {} : App;
App.someFunction = function(){};
or even, if you use underscore.js:
App = (typeof App === 'undefined')? {} : App;
_.extend(App, {
someFunction: function(){}
});
Since now the regular way to use the code from another file was going through a global (server and client). As Joao suggested you can make your own global App variable where you will store or more generically a global MODULE one (basically the same solution as Joao but with explanation).
But with with the arrival of ES2015 support, we will very soon be able to have an official pattern to achieve this. However as the 1.2 does not supports yet the import/export syntax:
Note, The ES2015 module syntax (import/export) is not supported yet in Meteor 1.2.
If you want to start using those features earlier, I would recommend using this package which is an temporary solution to fill the current import/export gap, the meteor team development are currently looking for an elegant solution to support this.

Categories

Resources