I am working on a project which requires to use node with ES6 alongside Mocha. So I came across a small issue, When I export a variable from one file to another, It actually exports the entire file! However I just want the variable to be exported.
Simply said, I want the updated value of x but doesn't want foo.js to run the Test Suit(function doubleX). How can I achieve this or what is wrong with my approach?
// foo.js
import {x} from './bar.js'
console.log(x);
// bar.js
export var x = 5;
// Test Suit Function
function doubleX(){
describe("Test Suit", function(){
// Few Calculations & Updated Variable
x += x;
// Some Test Case
it("Test Case", function(){
})
})
}
// Need to run test suit by bar.js not by foo.js
doubleX();
console.log(x);
Mocha scripts:
"scripts": {
"foo": "node_modules/.bin/mocha -bail --compilers js:babel-core/register -r jsdom-global/register foo.js",
"bar": "node_modules/.bin/mocha -bail --compilers js:babel-core/register -r jsdom-global/register bar.js",
}
When I use npm run foo
Actual Output:
10
10
(node:4981) DeprecationWarning: "--compilers" will be removed in a future version of Mocha; see https://git.io/vdcSr for more info
Test Suit
✓ Test Case
1 passing (8ms)
Expected Output:
10
By importing the bar.js file Node is running through the entire file.
Since you're calling the doubleX function and console.logging in bar.js, the value of x is altered and extra logs are being spit out.
Instead, you should change your doubleX function so it takes a parameter and then returns with a value of the parameter plus itself. Also you probably shouldn't call the function directly after defining it.
function doubleNum(num){
return num + num;
}
The function doubleX() is not exported you can test this by
// foo.js
import {x} from './bar.js'
console.log(x);
doubleX(); // would give error
It is being called inside bar.js hence it is executed
In your bar.js file use the following code:
var doubleX = function(x) {
x += x;
return x; // or you can return the above statement too using return x+= x;
};
doubleX(5);
module.exports = doubleX;
In your foo.js file
import {doubleX} from './bar.js'
console.log(doubleX);
Related
I'm writing some test scripts and noticed that when I import a specific module and execute a specific function within that module, Mocha runs the entire module plus the one I have specified.
In test.js
const myModule = require('../someModule');
describe('Test',function(){
it('Should run a function in mu module',funciton(){
myModule.someFunction(test);
};
}
In myModule.js
module.exports = { someFunction };
function someFunction(someInput){
return true;
}
Now say I include a console log within my module as shown below:
module.exports = { someFunction };
console.log('Loging inside the module');
function someFunction(someInput){
return true;
}
When I run my Mocha test it will run my funciton but will also run my console.log
Can someone please explain this?
I have a project that is using babel-register to dynamically transpile ES6 source to ES5 when requiring that module in a Node 6.6 project. I've read that babel-register hooks into Node's require function in order to transpile a file when you try to load it, but I'm not always clear on which files will be affected by that change.
This question comes up for me a lot when I'm writing tests: is only my production code getting transpiled, or does the test code get transpiled too?This brings me to the more general question, which is the topic of this post:
How can I tell when Babel is actually running, and which files are being transpiled?
Example code
Let's say I have production classes like this that are written in ES6 syntax
//src/greeter.js
export default class Greeter {
sayHello() {
return 'Hello World';
}
}
and Babel is configured to transpile as so (.babelrc)
{
"presets": ["es2015"]
}
and then there's some test code
//features/step_definitions/greeter_steps.js
import Greeter from '../../src/greeter'; //Causes greeter.js to be transpiled
import expect from 'expect';
var stepWrapper = function() {
//Does Babel try to transpile this code too?
this.Given(/^a greeter$/, function() {
this.greeter = new Greeter();
});
this.When(/^I ask it for a general greeting$/, function() {
this.greeting = this.greeter.sayHello();
});
this.Then(/^it should greet the entire world$/, function() {
expect(this.greeting).toEqual('Hello World');
});
};
module.exports = stepWrapper;
and all of that runs on Node like so
cucumberjs --compiler js:babel-core/register
Example code is available here, if that is helpful.
I made a hack to node_modules/babel-register/lib/node.js to do some logging like so
function compile(filename) {
var result = void 0;
var opts = new _babelCore.OptionManager().init((0, _extend2.default)({ sourceRoot: _path2.default.dirname(filename) }, (0, _cloneDeep2.default)(transformOpts), { filename: filename }));
var cacheKey = (0, _stringify2.default)(opts) + ":" + babel.version;
var env = process.env.BABEL_ENV || process.env.NODE_ENV;
console.log('[babel-register::compile] filename=' + filename + '\n'); //Added logging here
if (env) cacheKey += ":" + env;
if (cache) {
var cached = cache[cacheKey];
if (cached && cached.mtime === mtime(filename)) {
result = cached;
}
}
...
}
which then reports that test and production code are at least passing through Babel on some level
$ npm t
> cucumber-js-babel#1.0.0 test /Users/krull/git/sandbox/node/cucumber-js-babel
> cucumberjs --compiler js:babel-core/register
[babel-register::compile] filename=.../node/cucumber-js-babel/features/step_definitions/greeter_steps.js
[babel-register::compile] filename=.../node/cucumber-js-babel/src/greeter.js
...test results...
However, I'm hoping for a better solution that
works by some means of plugins and/or configuration, instead of monkey patching
better distinguishes which files are actually being transpiled, and which ones pass through Babel without modification
Because of this:
cucumberjs --compiler js:babel-core/register
...babel is invoked for both your test and regular source code. Keep in mind that in node, the only way to import JS is through require, so obviously babel-register will always be invoked. Of course, what babel does depends on its configuration, but most likely you have a simple configuration where all files required by require except those under node_modules will be transpiled.
I have a mochajs test file and a javascript code file in setup as below:
/js/module/codefile.js
/js/test/testfile.js
The codefile.js contains some javascript functions eg:
function addNumbers(a, b){
return a+b;
}
the testfile.js calls functions in the codefile to test them:
describe("Add numbers test", function() {
it("checks valid result", function() {
var a = 2;
var b = 1;
var result = addNumbers(a, b);
expect(result).to.equal(3);
});
});
From the command line I cd to the js folder (parent of test and module directories) then I run mocha and get the following error: ReferenceError: addNumbers is not defined at Context <anonymous> (test/testfile.js).
I can't actually see how it could be defined as how can mocha know where this function is comming from? (NB I am using client side JS so can't use import, and havent see any way to specificy (in Mocha or Karma or JS in general) where functions are defined as you would in Python or Java). Any ideas on how I can get simple unit tests running in mocha?
I initially tried getting mocha to run in WebStorm but gave up after similar errors.
Well, the mocha command is a nodejs program. This means that you can use Nodejs's module system to load your function.
function addNumbers(a, b){
return a+b;
}
module.exports = addNumbers;
and in your test file you will have
var addNumbers = require('../module/codefile.js');
describe("Add numbers test", function() {
it("checks valid result", function() {
var a = 2;
var b = 1;
var result = addNumbers(a, b);
expect(result).toEqual(3);
});
});
However, you said that you are using your code on the front-end. Well in this case you simply check if the module object exists. If it exists that means that your file is required by mocha for unit testing.
function addNumbers(a, b){
return a+b;
}
if (module && module.exports) {
module.exports = addNumbers;
}
If you want to get rid of this nasty if's, you can bundle your modules using browserify. Browserify helps you code on the front-end using the Nodejs's module system. So your code will remain the same.
I am new to nodejs and browserify. I started with this link .
I have file main.js which contains this code
var unique = require('uniq');
var data = [1, 2, 2, 3, 4, 5, 5, 5, 6];
this.LogData =function(){
console.log(unique(data));
};
Now I Install the uniq module with npm:
npm install uniq
Then I bundle up all the required modules starting at main.js into a single file called bundle.js with the browserify command:
browserify main.js -o bundle.js
The generated file looks like this:
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var unique = require('uniq');
var data = [1, 2, 2, 3, 4, 5, 5, 5, 6];
this.LogData =function(){
console.log(unique(data));
};
},{"uniq":2}],2:[function(require,module,exports){
"use strict"
function unique_pred(list, compare) {
var ptr = 1
, len = list.length
, a=list[0], b=list[0]
for(var i=1; i<len; ++i) {
b = a
a = list[i]
if(compare(a, b)) {
if(i === ptr) {
ptr++
continue
}
list[ptr++] = a
}
}
list.length = ptr
return list
}
function unique_eq(list) {
var ptr = 1
, len = list.length
, a=list[0], b = list[0]
for(var i=1; i<len; ++i, b=a) {
b = a
a = list[i]
if(a !== b) {
if(i === ptr) {
ptr++
continue
}
list[ptr++] = a
}
}
list.length = ptr
return list
}
function unique(list, compare, sorted) {
if(list.length === 0) {
return []
}
if(compare) {
if(!sorted) {
list.sort(compare)
}
return unique_pred(list, compare)
}
if(!sorted) {
list.sort()
}
return unique_eq(list)
}
module.exports = unique
},{}]},{},[1])
After including bundle.js file into my index.htm page, how do I call logData function ??
The key part of bundling standalone modules with Browserify is the --s option. It exposes whatever you export from your module using node's module.exports as a global variable. The file can then be included in a <script> tag.
You only need to do this if for some reason you need that global variable to be exposed. In my case the client needed a standalone module that could be included in web pages without them needing to worry about this Browserify business.
Here's an example where we use the --s option with an argument of module:
browserify index.js --s module > dist/module.js
This will expose our module as a global variable named module.
Source.
Update:
Thanks to #fotinakis. Make sure you're passing --standalone your-module-name. If you forget that --standalone takes an argument, Browserify might silently generate an empty module since it couldn't find it.
Hope this saves you some time.
By default, browserify doesn't let you access the modules from outside of the browserified code – if you want to call code in a browserified module, you're supposed to browserify your code together with the module. See http://browserify.org/ for examples of that.
Of course, you could also explicitly make your method accessible from outside like this:
window.LogData =function(){
console.log(unique(data));
};
Then you could call LogData() from anywhere else on the page.
#Matas Vaitkevicius's answer with Browserify's standalone option is correct (#thejh's answer using the window global variable also works, but as others have noted, it pollutes the global namespace so it's not ideal). I wanted to add a little more detail on how to use the standalone option.
In the source script that you want to bundle, make sure to expose the functions you want to call via module.exports. In the client script, you can call these exposed functions via <bundle-name>.<func-name>. Here's an example:
My source file src/script.js will have this:
module.exports = {myFunc: func};
My browserify command will look something like this:
browserify src/script.js --standalone myBundle > dist/bundle.js
And my client script dist/client.js will load the bundled script
<script src="bundle.js"></script>
and then call the exposed function like this:
<script>myBundle.myFunc();</script>
There's no need to require the bundle name in the client script before calling the exposed functions, e.g. <script src="bundle.js"></script><script>var bundled = require("myBundle"); bundled.myFunc();</script> isn't necessary and won't work.
In fact, just like all functions bundled by browserify without standalone mode, the require function won't be available outside of the bundled script. Browserify allows you to use some Node functions client-side, but only in the bundled script itself; it's not meant to create a standalone module you can import and use anywhere client-side, which is why we have to go to all this extra trouble just to call a single function outside of its bundled context.
I just read through the answers and seems like nobody mentioned the use of the global variable scope? Which is usefull if you want to use the same code in node.js and in the browser.
class Test
{
constructor()
{
}
}
global.TestClass = Test;
Then you can access the TestClass anywhere.
<script src="bundle.js"></script>
<script>
var test = new TestClass(); // Enjoy!
</script>
Note: The TestClass then becomes available everywhere. Which is the same as using the window variable.
Additionally you can create a decorator that exposes a class to the global scope. Which is really nice but makes it hard to track where a variable is defined.
Read README.md of browserify about --standalone parameter
or google "browserify umd"
Minimal runnable example
This is basically the same as: https://stackoverflow.com/a/43215928/895245 but with concrete files that will allow you to just run and easily reproduce it yourself.
This code is also available at: https://github.com/cirosantilli/browserify-hello-world
index.js
const uniq = require('uniq');
function myfunc() {
return uniq([1, 2, 2, 3]).join(' ');
}
exports.myfunc = myfunc;
index.html
<!doctype html>
<html lang=en>
<head>
<meta charset=utf-8>
<title>Browserify hello world</title>
</head>
<body>
<div id="container">
</body>
</div>
<script src="out.js"></script>
<script>
document.getElementById('container').innerHTML = browserify_hello_world.myfunc();
</script>
</html>
Node.js usage:
#!/usr/bin/env node
const browserify_hello_world = require('./index.js');
console.log(browserify_hello_world.myfunc());
Generate out.js for browser usage:
npx browserify --outfile out.js --standalone browserify_hello_world index.js
Both the browser and the command line show the expected output:
1 2 3
Tested with Browserify 16.5.0, Node.js v10.15.1, Chromium 78, Ubuntu 19.10.
To have your function available from both the HTML and from server-side node:
main.js:
var unique = require('uniq');
function myFunction() {
var data = [1, 2, 2, 4, 3];
return unique(data).toString();
}
console.log ( myFunction() );
// When browserified - we can't call myFunction() from the HTML, so we'll externalize myExtFunction()
// On the server-side "window" is undef. so we hide it.
if (typeof window !== 'undefined') {
window.myExtFunction = function() {
return myFunction();
}
}
main.html:
<html>
<head>
<script type='text/javascript' src="bundle.js"></script>
<head>
<body>
Result: <span id="demo"></span>
<script>document.getElementById("demo").innerHTML = myExtFunction();</script>
</body>
</html>
Run:
npm install uniq
browserify main.js > bundle.js
and you should get same results when opening main.html in a browser as when running
node main.js
Whole concept is about wrapping.
1.) Alternative - Object "this"
for this purpose I'll assume you have "only 1 script for whole app {{app_name}}" and "1 function {{function_name}}"
add function {{function_name}}
function {{function_name}}(param) { ... }
to object this
this.{{function_name}} = function(param) { ... }
then you have to name that object to be available - you will do it add param "standalone with name" like others advised
so if you use "watchify" with "browserify" use this
var b = browserify({
...
standalone: '{{app_name}}'
});
or command line
browserify index.js --standalone {{app_name}} > index-bundle.js
then you can call the function directly
{{app_name}}.{{function_name}}(param);
window.{{app_name}}.{{function_name}}(param);
2.) Alternative - Object "window"
add function {{function_name}}
function {{function_name}}(param) { ... }
to object window
window.{{function_name}} = function(param) { ... }
then you can call the function directly
{{function_name}}(param);
window.{{function_name}}(param);
You have a few options:
Let plugin browserify-bridge auto-export the modules to a generated entry module. This is helpful for SDK projects or situations where you don't have to manually keep up with what is exported.
Follow a pseudo-namespace pattern for roll-up exposure:
First, arrange your library like this, taking advantage of index look-ups on folders:
/src
--entry.js
--/helpers
--- index.js
--- someHelper.js
--/providers
--- index.js
--- someProvider.js
...
With this pattern, you define entry like this:
exports.Helpers = require('./helpers');
exports.Providers = require('./providers');
...
Notice the require automatically loads the index.js from each respective sub-folder
In your subfolders, you can just include a similar manifest of the available modules in that context:
exports.SomeHelper = require('./someHelper');
This pattern scales really well and allows for contextual (folder by folder) tracking of what to include in the rolled-up api.
You can also call your function from the html file like this:
main.js: (will be in bundle.js)
window.onload = function () {
document.getElementById('build-file')
.addEventListener('click', buildFile)
}
function buildFile() {
...
}
index.html:
<button id="build-file"">Build file</button>
window.LogData =function(data){
return unique(data);
};
Call the function simply by LogData(data)
This is just a slight modification to thejh's answer but important one
For debugging purposes I added this line to my code.js:
window.e = function(data) {eval(data);};
Then I could run anything even outside the bundle.
e("anything();");
I'm writing a web app in Node. If I've got some JS file db.js with a function init in it how could I call that function from the command line?
No comment on why you want to do this, or what might be a more standard practice: here is a solution to your question.... Keep in mind that the type of quotes required by your command line may vary.
In your db.js, export the init function. There are many ways, but for example:
module.exports.init = function () {
console.log('hi');
};
Then call it like this, assuming your db.js is in the same directory as your command prompt:
node -e 'require("./db").init()'
If your db.js were a module db.mjs, use a dynamic import to load the module:
node -e 'import("./db.mjs").then( loadedModule => loadedModule.init() )'
To other readers, the OP's init function could have been called anything, it is not important, it is just the specific name used in the question.
Update 2020 - CLI
As #mix3d pointed out you can just run a command where file.js is your file and someFunction is your function optionally followed by parameters separated with spaces
npx run-func file.js someFunction "just some parameter"
That's it.
file.js called in the example above
const someFunction = (param) => console.log('Welcome, your param is', param)
// exporting is crucial
module.exports = { someFunction }
More detailed description
Run directly from CLI (global)
Install
npm i -g run-func
Usage i.e. run function "init", it must be exported, see the bottom
run-func db.js init
or
Run from package.json script (local)
Install
npm i -S run-func
Setup
"scripts": {
"init": "run-func db.js init"
}
Usage
npm run init
Params
Any following arguments will be passed as function parameters init(param1, param2)
run-func db.js init param1 param2
Important
the function (in this example init) must be exported in the file containing it
module.exports = { init };
or ES6 export
export { init };
As per the other answers, add the following to someFile.js
module.exports.someFunction = function () {
console.log('hi');
};
You can then add the following to package.json
"scripts": {
"myScript": "node -e 'require(\"./someFile\").someFunction()'"
}
From the terminal, you can then call
npm run myScript
I find this a much easier way to remember the commands and use them
Try make-runnable.
In db.js, add require('make-runnable'); to the end.
Now you can do:
node db.js init
Any further args would get passed to the init method, in the form of a list or key-value pairs.
Sometimes you want to run a function via CLI, sometimes you want to require it from another module. Here's how to do both.
// file to run
const runMe = () => {}
if (require.main === module) {
runMe()
}
module.exports = runMe
This one is dirty but works :)
I will be calling main() function from my script. Previously I just put calls to main at the end of script. However I did add some other functions and exported them from script (to use functions in some other parts of code) - but I dont want to execute main() function every time I import other functions in other scripts.
So I did this,
in my script i removed call to main(), and instead at the end of script I put this check:
if (process.argv.includes('main')) {
main();
}
So when I want to call that function in CLI: node src/myScript.js main
simple way:
let's say you have db.js file in a helpers directory in project structure.
now go inside helpers directory and go to node console
helpers $ node
2) require db.js file
> var db = require("./db")
3) call your function (in your case its init())
> db.init()
hope this helps
Updated for 2022 - If you've switched to ES Modules, you can't use the require tricks, you'd need to use dynamic imports:
node -e 'import("./db.js").then(dbMod => dbMod.init());'
or with the --experimental-specifier-resolution=node flag:
node --experimental-specifier-resolution=node -e 'import("./db").then(dbMod => dbMod.init());'
If you turn db.js into a module you can require it from db_init.js and just: node db_init.js.
db.js:
module.exports = {
method1: function () { ... },
method2: function () { ... }
}
db_init.js:
var db = require('./db');
db.method1();
db.method2();
I do a IIFE, something like that:
(() => init())();
this code will be executed immediately and invoke the init function.
You can also run TypeScript with ts-node similar to #LeeGoddard answer.
In my case, I wanted to use app and init separately for testing purposes.
// app.ts
export const app = express();
export async function init(): Promise<void> {
// app init logic...
}
npx ts-node -e 'require("./src/app").init();'
npx ts-node -e 'import("./src/app").then(a => a.init());' // esmodule
maybe this method is not what you mean, but who knows it can help
index.js
const arg = process.argv.splice(2);
function printToCli(text){
console.log(text)
}
switch(arg[0]){
case "--run":
printToCli("how are you")
break;
default: console.log("use --run flag");
}
and run command node . --run
command line
probuss-MacBook-Air:fb_v8 probus$ node . --run
how are you
probuss-MacBook-Air:fb_v8 probus$
and you can add more arg[0] , arg[1], arg[2] ... and more
for node . --run -myarg1 -myarg2
If you want to include environment variables from your .env files, you can use env-cmd:
npx env-cmd node -e 'require("./db").init()'
If you want run a specific function in the file too, use run-func:
npx env-cmd npx run-func db.js init someArg
Or, to provide an argument for the accepted answer you'd have to do something like:
npx env-cmd node -e 'require("./db").init(someArg)'
Writing/updating an expression here is less explicit (so easier to miss when you're checking back, for example) than providing different arguments to the commands, so I recommend using env-cmd with run-func.
Note: I also usually add --experimental-modules on the end when necessary.
Inspired by https://github.com/DVLP/run-func/blob/master/index.js
I create https://github.com/JiangWeixian/esrua
if file index.ts
export const welcome = (msg: string) => {
console.log(`hello ${msg}`)
}
just run
esrua ./index.ts welcome -p world
will output hello world
If your file just contains your function, for example:
myFile.js:
function myMethod(someVariable) {
console.log(someVariable)
}
Calling it from the command line like this nothing will happen:
node myFile.js
But if you change your file:
myFile.js:
myMethod("Hello World");
function myMethod(someVariable) {
console.log(someVariable)
}
Now this will work from the command line:
node myFile.js