Scenario: Consider I am having multiple methods doing different tasks and handled by different developers. I am trying to make a generic method call which logs if error occurs. So the need is I have to log a Line No, Method name, etc..
I wrote a generic function, as follows:
function enterLog(sourcefile, methodName, LineNo)
{
fs.appendFile('errlog.txt', sourcefile +'\t'+ methodName +'\t'+ LineNo +'\n', function(e){
if(e)
console.log('Error Logger Failed in Appending File! ' + e);
});
}
So, the call for the above method has to pass source file, method name and the Line No. Which may change at any point of time during development.
E.g. for calling method with hard-coded values:
enterLog('hardcodedFileName.js', 'TestMethod()', '27');
Question: Is it better to hard-code the values (as above example) required or is there any way to get the method name & line no reference from any way in Node.js?
there is a nice module out there which we use in our applications-logger. you can even fetch the line number. https://npmjs.org/package/traceback
so you could rewrite it like that:
var traceback = require('traceback');
function enterLog(sourcefile, methodName, LineNo) {
var tb = traceback()[1]; // 1 because 0 should be your enterLog-Function itself
fs.appendFile('errlog.txt', tb.file +'\t'+ tb.method +'\t'+ tb.line +'\n', function(e) {
if(e) {
console.log('Error Logger Failed in Appending File! ' + e);
}
});
}
and just call:
enterLog();
from wherever you want and always get the correct results
Edit: another hint. not hardcoding your filename is the easiest to achieve in node.js without 3rd-party-module-dependencies:
var path = require('path');
var currentFile = path.basename(__filename); // where __filename always has the absolute path of the current file
Related
I'm writing a script to automatically make a new directory with 3 files inside. The "mkdirSync" part works but the "writeFileSync" is returning an error that my argument is not a string.
const fs = require('fs')
const folderName = process.argv[2] || 'Project'
try {
fs.mkdirSync(folderName);
fs.writeFileSync(`${folderName}/index.html`);
fs.writeFileSync(`${folderName}/styles.css`);
fs.writeFileSync(`${folderName}/script.js`);
} catch (e) {
console.log("Error:")
console.log(e)
}
This is the error I am getting in the terminal for reference
If you only want to create a file without writing anything to it immediately, consider using fs.openSync (instead of fs.writeFileSync) with either 'a' or 'w' flag passed. You can even close this file descriptor right after creating it, like this:
fs.closeSync(fs.openSync(`${folderName}/index.html`, 'a')); // etc
The difference between 'a' and 'w' is subtle: both create a file if it doesn't exist. Yet if it does, it will be truncated with 'w' (the same behavior as you have with writeFile) - and left untouched with 'a' (the same as appendFile).
Since I didnt want the files to come with anything written in them, passing in an empty string as the second argument fixed the error.
const fs = require('fs')
const folderName = process.argv[2] || 'Project'
try {
fs.mkdirSync(folderName);
fs.writeFileSync(`${folderName}/index.html`, "");
fs.writeFileSync(`${folderName}/styles.css`, "");
fs.writeFileSync(`${folderName}/script.js`,"");
} catch (e) {
console.log("Error:")
console.log(e)
}
I'm using UiPath Orchestrator. This runs as expected. But I now additionally want to reduce the authentication to a single call (instead of always do an auth when requesting an odata). So my idea was to write the object to a file and on the odata request read that object and re-use it.
The following orchestrator object comes from the constructor of new Orchestrator. This object is ready to be used and has the following structure (via console.log(orchestrator)):
In my tool I need the object functions of odata. So this works:
console.log(orchestrator['v2']['odata']);
I now want to save that object as file to be able to re-use it, so I did:
fs.writeFileSync('./data.json', orchestrator, 'utf-8')
But sadly I get the error:
Converting circular structure to JSON
That is intended as the node package is using a circulare structure. So my idea was to use the circular-json package to fix that issue:
const {parse, stringify} = require('circular-json');
...
var savetofile = stringify(orchestrator);
...
var readfromfile = parse(savetofile);
...
console.log(readfromfile['v2']['odata']);
But sadly than readfromfile['v2']['odata'] is not available anymore. The reason is that stringify(orchestrator) is already minifying too heavy:
So how I achieve that I am able to read the Orchestrator object from the file and being able to use the functions again? Or is it more useful to use a memory tool in my case?
The issue was not located in the Orchestrator object itself. So there is no need to do a single authentication.
My problem was that I put the res.send outside of the callback. So it never waited for the actual finish of the REST api call.
This was the base code where it just took the static result of the first request, it never updated the results:
app.get('/jobs', function (req, res) {
...
var orchestrator = require('./authenticate');
var results = {};
var apiQuery= {};
orchestrator.get('/odata/Jobs', apiQuery, function (err, data) {
for (row in data) {
results[i] =
{
'id' : row.id,
...
};
}
});
return res.send({results});
});
The solution is to moving the res.send({results}); into the orchestrator.get, then it properly overwrites the results as it waits correctly for the callback:
app.get('/jobs', function (req, res) {
...
var orchestrator = require('./authenticate');
var results = {};
var apiQuery= {};
orchestrator.get('/odata/Jobs', apiQuery, function (err, data) {
for (row in data) {
results[i] =
{
'id' : row.id,
...
};
}
return res.send({results});
});
});
I have created a code that it actually works, but I call a library that has an error, and I would like to know if it is possible to avoid that specific line of code. I will try to explain the case as well as possible:
Error
Uncaught TypeError: fs.openSync is not a function
Previous code
function synthesizeToAudioFile(authorizationToken, message) {
// replace with your own subscription key,
// service region (e.g., "westus"), and
// the name of the file you save the synthesized audio.
var serviceRegion = "westus"; // e.g., "westus"
var filename = "./audiocue.wav";
//Use token.Otherwise use the provided subscription key
var audioConfig, speechConfig;
audioConfig = SpeechSDK.AudioConfig.fromAudioFileOutput(filename);
speechConfig = SpeechSDK.SpeechConfig.fromAuthorizationToken(authorizationToken, serviceRegion);
// create the speech synthesizer.
var synthesizer = new SpeechSDK.SpeechSynthesizer(speechConfig, audioConfig);
// start the synthesizer and wait for a result.
synthesizer.speakTextAsync(message,
function (result) {
if (result.reason === SpeechSDK.ResultReason.SynthesizingAudioCompleted) {
console.log("synthesis finished.");
} else {
console.error("Speech synthesis canceled, " + result.errorDetails +
"\nDid you update the subscription info?");
}
synthesizer.close();
synthesizer = undefined;
},
function (err) {
console.trace("err - " + err);
synthesizer.close();
synthesizer = undefined;
});
console.log("Now synthesizing to: " + filename);
}
I created a method, which later I have replicated in my current code. The difference was that I was using Browserify in order to import a library from a script of a HTML file:
<script type="text/javascript" src="js/dist/sub_mqtt.js"></script>
This file had my method, and the whole library, which made it crazy unreadable, and therefore I started using ScriptJS to import it. The problem is that, using browserify I was able to remove the line of code that it was failing using fs.openSync(and I do not even need), but by importing it with ScriptJS I do not have access to the source code.
I assume that what is missing is that I am not importing the library fs, which is being used by the library that I am importing with ScriptJS before importing that one, but how could I do it? I have tried:
<script src="../text-to-speech/node_modules/fs.realpath/index.js"></script>
, or
<script type="text/javascript" src="../text-to-speech/node_modules/fs.realpath/index.js"></script>
and also wrapping the content of synthesizeToAudioFile() with
require(["node_modules/fs.realpath/index.js"], function (fs) { });
but I get the following error:
Uncaught ReferenceError: module is not defined
at index.js:1
After researching about this question I found out the next statement:
The fs package on npm was empty and didn't do anything, however many
packages mistakenly depended on it. npm, Inc. has taken ownership of
it.
It's also a built-in Node module. If you've depended on fs, you can
safely remove it from your package dependencies.
therefore what I have done is to access to the file that I was requiring with ScriptJS
require(["../text-to-speech/microsoft.cognitiveservices.speech.sdk.bundle.js"]
and directly remove that line on it. Note that, at least in my case, clearing the cache of the browser was needed.
Does anyone know how to get the name of a module in node.js / javascript
so lets say you do
var RandomModule = require ("fs")
.
.
.
console.log (RandomModule.name)
// -> "fs"
If you are trying to trace your dependencies, you can try using require hooks.
Create a file called myRequireHook.js
var Module = require('module');
var originalRequire = Module.prototype.require;
Module.prototype.require = function(path) {
console.log('*** Importing lib ' + path + ' from module ' + this.filename);
return originalRequire(path);
};
This code will hook every require call and log it into your console.
Not exactly what you asked first, but maybe it helps you better.
And you need to call just once in your main .js file (the one you start with node main.js).
So in your main.js, you just do that:
require('./myRequireHook');
var fs = require('fs');
var myOtherModule = require('./myOtherModule');
It will trace require in your other modules as well.
This is the way transpilers like babel work. They hook every require call and transform your code before load.
I don't know why you would need that, but there is a way.
The module variable, which is loaded automatically in every node.js file, contains an array called children. This array contains every child module loaded by require in your current file.
This way, you need to strict compare your loaded reference with the cached object version in this array in order to discover which element of the array corresponds to your module.
Look this sample:
var pieces = require('../src/routes/headers');
var childModuleId = discoverChildModuleId(pieces, module);
console.log(childModuleId);
function discoverChildModuleId(object, moduleObj) {
"use strict";
var childModule = moduleObj.children.find(function(child) {
return object === child.exports;
});
return childModule && childModule.id;
}
This code will find the correspondence in children object and bring its id.
I put module as a parameter of my function so you can export it to a file. Otherwise, it would show you modules of where discoverChildModule function resides (if it is in the same file won't make any difference, but if exported it will).
Notes:
Module ids have the full path name. So don't expect finding ../src/routes/headers. You will find something like: /Users/david/git/...
My algorithm won't detect exported attributes like var Schema = require('mongoose').Schema. It is possible to make a function which is capable of this, but it will suffer many issues.
From within a module (doesn't work at the REPL) you can...
console.log( global.process.mainModule.filename );
And you'll get '/home/ubuntu/workspace/src/admin.js'
There are some third party Javascript libraries that have some functionality I would like to use in a Node.js server. (Specifically I want to use a QuadTree javascript library that I found.) But these libraries are just straightforward .js files and not "Node.js libraries".
As such, these libraries don't follow the exports.var_name syntax that Node.js expects for its modules. As far as I understand that means when you do module = require('module_name'); or module = require('./path/to/file.js'); you'll end up with a module with no publicly accessible functions, etc.
My question then is "How do I load an arbitrary javascript file into Node.js such that I can utilize its functionality without having to rewrite it so that it does do exports?"
I'm very new to Node.js so please let me know if there is some glaring hole in my understanding of how it works.
EDIT: Researching into things more and I now see that the module loading pattern that Node.js uses is actually part of a recently developed standard for loading Javascript libraries called CommonJS. It says this right on the module doc page for Node.js, but I missed that until now.
It may end up being that the answer to my question is "wait until your library's authors get around to writing a CommonJS interface or do it your damn self."
Here's what I think is the 'rightest' answer for this situation.
Say you have a script file called quadtree.js.
You should build a custom node_module that has this sort of directory structure...
./node_modules/quadtree/quadtree-lib/
./node_modules/quadtree/quadtree-lib/quadtree.js
./node_modules/quadtree/quadtree-lib/README
./node_modules/quadtree/quadtree-lib/some-other-crap.js
./node_modules/quadtree/index.js
Everything in your ./node_modules/quadtree/quadtree-lib/ directory are files from your 3rd party library.
Then your ./node_modules/quadtree/index.js file will just load that library from the filesystem and do the work of exporting things properly.
var fs = require('fs');
// Read and eval library
filedata = fs.readFileSync('./node_modules/quadtree/quadtree-lib/quadtree.js','utf8');
eval(filedata);
/* The quadtree.js file defines a class 'QuadTree' which is all we want to export */
exports.QuadTree = QuadTree
Now you can use your quadtree module like any other node module...
var qt = require('quadtree');
qt.QuadTree();
I like this method because there's no need to go changing any of the source code of your 3rd party library--so it's easier to maintain. All you need to do on upgrade is look at their source code and ensure that you are still exporting the proper objects.
There is a much better method than using eval: the vm module.
For example, here is my execfile module, which evaluates the script at path in either context or the global context:
var vm = require("vm");
var fs = require("fs");
module.exports = function(path, context) {
context = context || {};
var data = fs.readFileSync(path);
vm.runInNewContext(data, context, path);
return context;
}
And it can be used like this:
> var execfile = require("execfile");
> // `someGlobal` will be a global variable while the script runs
> var context = execfile("example.js", { someGlobal: 42 });
> // And `getSomeGlobal` defined in the script is available on `context`:
> context.getSomeGlobal()
42
> context.someGlobal = 16
> context.getSomeGlobal()
16
Where example.js contains:
function getSomeGlobal() {
return someGlobal;
}
The big advantage of this method is that you've got complete control over the global variables in the executed script: you can pass in custom globals (via context), and all the globals created by the script will be added to context. Debugging is also easier because syntax errors and the like will be reported with the correct file name.
The simplest way is: eval(require('fs').readFileSync('./path/to/file.js', 'utf8'));
This works great for testing in the interactive shell.
AFAIK, that is indeed how modules must be loaded.
However, instead of tacking all exported functions onto the exports object, you can also tack them onto this (what would otherwise be the global object).
So, if you want to keep the other libraries compatible, you can do this:
this.quadTree = function () {
// the function's code
};
or, when the external library already has its own namespace, e.g. jQuery (not that you can use that in a server-side environment):
this.jQuery = jQuery;
In a non-Node environment, this would resolve to the global object, thus making it a global variable... which it already was. So it shouldn't break anything.
Edit:
James Herdman has a nice writeup about node.js for beginners, which also mentions this.
I'm not sure if I'll actually end up using this because it's a rather hacky solution, but one way around this is to build a little mini-module importer like this...
In the file ./node_modules/vanilla.js:
var fs = require('fs');
exports.require = function(path,names_to_export) {
filedata = fs.readFileSync(path,'utf8');
eval(filedata);
exported_obj = {};
for (i in names_to_export) {
to_eval = 'exported_obj[names_to_export[i]] = '
+ names_to_export[i] + ';'
eval(to_eval);
}
return exported_obj;
}
Then when you want to use your library's functionality you'll need to manually choose which names to export.
So for a library like the file ./lib/mylibrary.js...
function Foo() { //Do something... }
biz = "Blah blah";
var bar = {'baz':'filler'};
When you want to use its functionality in your Node.js code...
var vanilla = require('vanilla');
var mylibrary = vanilla.require('./lib/mylibrary.js',['biz','Foo'])
mylibrary.Foo // <-- this is Foo()
mylibrary.biz // <-- this is "Blah blah"
mylibrary.bar // <-- this is undefined (because we didn't export it)
Don't know how well this would all work in practice though.
I was able to make it work by updating their script, very easily, simply adding module.exports = where appropriate...
For example, I took their file and I copied to './libs/apprise.js'. Then where it starts with
function apprise(string, args, callback){
I assigned the function to module.exports = thus:
module.exports = function(string, args, callback){
Thus I'm able to import the library into my code like this:
window.apprise = require('./libs/apprise.js');
And I was good to go. YMMV, this was with webpack.
A simple include(filename) function with better error messaging (stack, filename etc.) for eval, in case of errors:
var fs = require('fs');
// circumvent nodejs/v8 "bug":
// https://github.com/PythonJS/PythonJS/issues/111
// http://perfectionkills.com/global-eval-what-are-the-options/
// e.g. a "function test() {}" will be undefined, but "test = function() {}" will exist
var globalEval = (function() {
var isIndirectEvalGlobal = (function(original, Object) {
try {
// Does `Object` resolve to a local variable, or to a global, built-in `Object`,
// reference to which we passed as a first argument?
return (1, eval)('Object') === original;
} catch (err) {
// if indirect eval errors out (as allowed per ES3), then just bail out with `false`
return false;
}
})(Object, 123);
if (isIndirectEvalGlobal) {
// if indirect eval executes code globally, use it
return function(expression) {
return (1, eval)(expression);
};
} else if (typeof window.execScript !== 'undefined') {
// if `window.execScript exists`, use it
return function(expression) {
return window.execScript(expression);
};
}
// otherwise, globalEval is `undefined` since nothing is returned
})();
function include(filename) {
file_contents = fs.readFileSync(filename, "utf8");
try {
//console.log(file_contents);
globalEval(file_contents);
} catch (e) {
e.fileName = filename;
keys = ["columnNumber", "fileName", "lineNumber", "message", "name", "stack"]
for (key in keys) {
k = keys[key];
console.log(k, " = ", e[k])
}
fo = e;
//throw new Error("include failed");
}
}
But it even gets dirtier with nodejs: you need to specify this:
export NODE_MODULE_CONTEXTS=1
nodejs tmp.js
Otherwise you cannot use global variables in files included with include(...).