Why _ as variable name in node REPL mode to require failed - node.js

Node version v5.4.1 and I installed lodash in current directory's node_modules. And I encouter this issue where '_' seems special.
> var _ = require('lodash');
undefined
> _.defaults
TypeError: Cannot read property 'defaults' of undefined
at repl:1:2
at REPLServer.defaultEval (repl.js:252:27)
at bound (domain.js:287:14)
at REPLServer.runBound [as eval] (domain.js:300:12)
at REPLServer.<anonymous> (repl.js:417:12)
at emitOne (events.js:82:20)
at REPLServer.emit (events.js:169:7)
at REPLServer.Interface._onLine (readline.js:210:10)
at REPLServer.Interface._line (readline.js:549:8)
at REPLServer.Interface._ttyWrite (readline.js:826:14)
> var l = require('lodash');
undefined
> l.defaults
[Function]
>
I saw a question related to undefined output. But no question yet cares about the failure of 'require' when '_' as variable name. Do you know any about it?

The underscore is a special variable that holds the result of the most recent expression. Prior to node v6.0.0, this behavior could not be disabled. However, node v6.0.0 introduced a change that allowed the underscore variable to be overwritten. So you will need to upgrade your copy of node if you want to reassign the _ variable in the node REPL.

Related

Improving error reporting for a dynamically built module.exports in NODEJS

I have an app that dynamically loads individual smaller files into module.export at runtime, it has been working well for a couple of years.
This is the main part of the code that iterates folders, reads them, and then appends them to the desired module.exports after a bit of validation.
var path = module.path+"/lib";
require('async').eachSeries(require('fs').readdirSync(path),function(file,next){
require('fs').readFile(path+'/'+file,'utf8',function(err,code){
var id = file.split('.')[0];
if(id in module.exports) module.exports._errs.push({'lib':id,'err':'mod/'+module.name+'/lib/'+file.replace(/\.obj|\.fnc|\.str/,'.xxx')+' filename duplicated.'});
try{module.exports[id] = eval('false ||'+code);}
catch(err){
module.exports[id]={'_err':err.message};
module.exports._errs.push({'lib':id,'err':err.message})
}
next();
})
},function(){
This all works, but the issue is when there is an error generated, it appears that all of the files / objects / functions are loaded anonymously which make it impossible to determine from the stack trace errors exactly which function failed, as all appear to be anonymous.
Is it possible to either modify the code so that they are non-anonymous or alternatively modify the stack trace error reporting to show the code (& preferably variables) that actually caused the crash ??
TypeError: Cannot read property 'replace' of undefined
at eval (eval at <anonymous> (eval at <anonymous> (/snapshot/src/x3-bin/source/mod/va/index.js:1:462)), <anonymous>:1:1642)
at Query.<anonymous> (/snapshot/src/x3-bin/source/lib/mysql.js:1:2474)
at Query.<anonymous> (/snapshot/src/x3-bin/source/node_modules/mysql/lib/Connection.js:502:10)
at Query.domain [as _callback] (/snapshot/src/x3-bin/source/node_modules/mysql/lib/Connection.js:468:16)
at Query.Sequence.end (/snapshot/src/x-bin/source/node_modules/mysql/lib/protocol/sequences/Sequence.js:83:24)
at Query._handleFinalResultPacket (/snapshot/src/x3-bin/source/node_modules/mysql/lib/protocol/sequences/Query.js:139:8)
at Query.EofPacket (/snapshot/src/x3-bin/source/node_modules/mysql/lib/protocol/sequences/Query.js:123:8)
at Protocol._parsePacket (/snapshot/src/x-bin/source/node_modules/mysql/lib/protocol/Protocol.js:278:23)
at Parser.write (/snapshot/src/x3-bin/source/node_modules/mysql/lib/protocol/Parser.js:76:12)
at Protocol.write (/snapshot/src/x-bin/source/node_modules/mysql/lib/protocol/Protocol.js:38:16)
at Socket.<anonymous> (/snapshot/src/x3-bin/source/node_modules/mysql/lib/Connection.js:91:28)
at Socket.<anonymous> (/snapshot/src/x-bin/source/node_modules/mysql/lib/Connection.js:502:10)
at emitOne (events.js:116:13)
at Socket.emit (events.js:211:7)
at addChunk (_stream_readable.js:263:12)
at readableAddChunk (_stream_readable.js:250:11)
As you can see, the info to identify the source of the problem is very vague (eval at ) and impossible to identify exactly which module.exports function was the cause ?
TypeError: Cannot read property 'replace' of undefined
at eval (eval at <anonymous> (eval at <anonymous> (/snapshot/src/x3-bin/source/mod/va/index.js:1:462)), <anonymous>:1:1642)
Is it possible to either modify the code so that they are non-anonymous
Well, function(err,code){...} is an anonymous function, function NotAnonymous(err, code){...} is not. See this example Node session:
> (function() { eval("let x; x.replace;"); })()
Uncaught TypeError: Cannot read properties of undefined (reading 'replace')
at eval (eval at <anonymous> (REPL9:1:15), <anonymous>:1:10)
at REPL9:1:15
> (function NotAnonymous() { eval("let x; x.replace;"); })()
Uncaught TypeError: Cannot read properties of undefined (reading 'replace')
at eval (eval at NotAnonymous (REPL10:1:28), <anonymous>:1:10)
at NotAnonymous (REPL10:1:28)
> (function NotAnonymous() { eval("(function AlsoNicelyNamed() {let x; x.replace;})()"); })()
Uncaught TypeError: Cannot read properties of undefined (reading 'replace')
at AlsoNicelyNamed (eval at NotAnonymous (REPL11:1:28), <anonymous>:1:39)
at eval (eval at NotAnonymous (REPL11:1:28), <anonymous>:1:49)
at NotAnonymous (REPL11:1:28)
Note how the second and third versions contain the snippet eval at NotAnonymous.
The other occurrence of <anonymous> (<anonymous>:1:10 in my example, <anonymous>:1:1642 in yours) is due to the fact that strings that get evaled don't have a file name. To get change that, you'd have to stop using eval. The line/column information should be accurate though (at least, it is when I put an \n into a test string); so it looks like the file you loaded in this particular example was minified and had at least 1642 characters on its first line.
impossible to identify exactly which module.exports function was the cause ?
You already have that information: in
module.exports._errs.push({'lib':id,'err':err.message}),
id is the export that caused the problem.
Aside from that, it really depends on what's in the files you're evaling. If there are anonymous functions in there, then of course they'll show up as "anonymous", and you'd have to fix that in there by providing names for all functions.

AWS Lambda Node error logging: Is it necessary to separately log the stack?

Some of the example code in the AWS Javascript SDK docs log the stack of an error separately to the error itself, for example this is from the AWS.CloudWatch overview:
var cloudwatch = new AWS.CloudWatch();
cloudwatch.getMetricWidgetImage(params, function (err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
So far as I can tell, this is redundant in Node (I have only tested on v10.16). If I log just the error object, it includes the stack trace:
> console.log(e)
Error: x
at repl:2:8
at Script.runInThisContext (vm.js:122:20)
at REPLServer.defaultEval (repl.js:332:29)
at bound (domain.js:402:14)
at REPLServer.runBound [as eval] (domain.js:415:12)
at REPLServer.onLine (repl.js:642:10)
at REPLServer.emit (events.js:198:13)
at REPLServer.EventEmitter.emit (domain.js:448:20)
at REPLServer.Interface._onLine (readline.js:308:10)
at REPLServer.Interface._normalWrite (readline.js:451:12)
undefined
Whereas if I log both, as Amazon have done above, I get an ugly ouput with duplication:
> console.log(e, e.stack)
Error: x
at repl:2:8
at Script.runInThisContext (vm.js:122:20)
at REPLServer.defaultEval (repl.js:332:29)
at bound (domain.js:402:14)
at REPLServer.runBound [as eval] (domain.js:415:12)
at REPLServer.onLine (repl.js:642:10)
at REPLServer.emit (events.js:198:13)
at REPLServer.EventEmitter.emit (domain.js:448:20)
at REPLServer.Interface._onLine (readline.js:308:10)
at REPLServer.Interface._normalWrite (readline.js:451:12) 'Error: x\n at repl:2:8\n at Script.runInThisContext (vm.js:122:20)\n at REPLServer.defaultEval (repl.js:332:29)\n at bound (domain.js:402:14)\n at REPLServer.runBound [as eval] (domain.js:415:12)\n at REPLServer.onLine (repl.js:642:10)\n at REPLServer.emit (events.js:198:13)\n at REPLServer.EventEmitter.emit (domain.js:448:20)\n at REPLServer.Interface._onLine (readline.js:308:10)\n at REPLServer.Interface._normalWrite (readline.js:451:12)'
undefined
Is there some reason that Amazon are using the console.log(err, err.stack) pattern that I am missing if I just do console.log(err)?
(I am primarily using Node in an AWS Lambda, version 10.x)
The console.log(err, err.stack) formula was required for Node versions < 6, as they did not log the error stack by default.
More recent Node versions can just do console.log(err).
See https://stackoverflow.com/a/33593443/1695906 for a bit more detail
(The oldest Node offered by AWS Lambda at the time of writing is Node.js 8.10, so perhaps AWS should update their docs now to remove this.)
(From "#Michael - sqlbot"'s comment, thanks!)

How to get line and file name from Error in Node.js?

Suppose I have:
var err = new Error('My error!');
How to get line and file name, where Error was created?
Like in PHP:
$ex = new \Exception();
$ex->getLine();
$ex->getFile();
To get the file name for the current file: use __filename.
To get the folder for the current file: use __dirname
To parse the file from an Error object, you need an Error with a stack property, that hopefully points to files. You would need then to parse the filename and line from the stack string or use a module that does so.
> new Error().stack
Error
at repl:1:1
at sigintHandlersWrap (vm.js:22:35)
at sigintHandlersWrap (vm.js:73:12)
at ContextifyScript.Script.runInThisContext (vm.js:21:12)
at REPLServer.defaultEval (repl.js:346:29)
at bound (domain.js:280:14)
at REPLServer.runBound [as eval] (domain.js:293:12)
at REPLServer.<anonymous> (repl.js:545:10)
at emitOne (events.js:101:20)
at REPLServer.emit (events.js:188:7)
You can use stack-trace node module. This gives module name and line number

Object.keys returns an object in node 4.2.2. I need an Array

In the node repl Object.keys seems to work just fine, returning an Array of the keys.
> Object.keys({id: 1})
[ 'id' ]
But Array functions like includes don't work.
> Object.keys({id: 1}).includes('id')
TypeError: Object.keys(...).includes is not a function
at repl:1:22
at REPLServer.defaultEval (repl.js:248:27)
at bound (domain.js:280:14)
at REPLServer.runBound [as eval] (domain.js:293:12)
at REPLServer.<anonymous> (repl.js:412:12)
at emitOne (events.js:82:20)
at REPLServer.emit (events.js:169:7)
at REPLServer.Interface._onLine (readline.js:210:10)
at REPLServer.Interface._line (readline.js:549:8)
at REPLServer.Interface._ttyWrite (readline.js:826:14)
This led me to check the type and surprise surpise! It's an Object.
> typeof(Object.keys({id: 1}))
'object'
Does anyone know why this is happening? How can I get an Array from Object.keys?

Custom REPL in nodejs

I'm trying to play with the nodejs built in REPL from the documentation.
http://nodejs.org/api/repl.html
The example of adding an item is as follows:
repl.start().context.m = msg;
I cant seem to find away to add multiple menus. I've tried doing:
menus = {m = 'hello', f = 'foo'}
repl.start().context = menus
But that doesn't work either. I get:
testREPL> m
TypeError: needs a 'context' argument.
at REPLServer.self.eval (repl.js:113:21)
at Interface.<anonymous> (repl.js:250:12)
at Interface.EventEmitter.emit (events.js:88:17)
at Interface._onLine (readline.js:199:10)
at Interface._normalWrite._line_buffer (readline.js:308:12)
at Array.forEach (native)
at Interface._normalWrite (readline.js:307:11)
at Socket.ondata (readline.js:90:10)
at Socket.EventEmitter.emit (events.js:115:20)
at TCP.onread (net.js:395:14)
Does anybody know how to get this working?
You can't assign to the context property, you have to add properties to it. What you are trying is "overwriting" it with your own object. Try to assign each property by itself instead:
var context = repl.start({}).context;
context.m = 'hello';
context.f = 'foo';

Resources