Chris
Chris

Reputation: 59501

Alternative to eval() in node script

I am working on a script that runs during our build process in Jenkins right before npm install. My issue is that I need to download a JavaScript file from an external resource and read a variable from it.

unzipper.on('extract', () => { 
  const content = fs.readFileSync(`${outputDir}/js/en.js`, 'utf8');
  eval(content); // Less smellier alternative?

  if (obj) {
    const str = JSON.stringify(obj);
    fs.writeFileSync(`${outputDir}/public/data.json`, str);
  } else {
    throw 'Variable `obj` not found';
  }
});

I know that "eval is evil", but any suggested alternatives I've found online don't seem to work. I have tried different variations of new Function(obj)(), but Node seems to exit the script after (the if-case never runs).

Ideas?

Upvotes: 2

Views: 6455

Answers (2)

Chris
Chris

Reputation: 59501

This answer is heavily based on @georg's comments, but since it helped me I'll provide it as an alternative answer.

Explanation in the comments.

let content = fs.readFileSync(`${outputDir}/js/en.js`, 'utf8');
content += '; module.exports=obj'; // Export "obj" variable

fs.writeFileSync(`${outputDir}/temp`, content); // Create a temporary file
const obj = require(`${outputDir}/temp`); // Import the variable from the temporary file
fs.unlinkSync(`${outputDir}/temp`); // Remove the temporary file

Upvotes: 1

georg
georg

Reputation: 214949

Since node.js provides the API to talk to the V8 runner directly, it might be a good idea to use it. Basically, it's the API used by node's require under the hood.

Assuming the js file in question contains the variable obj we're interested in, we do the following:

  • read the code from the file
  • append ; obj to the code to make sure it's the last expression it evaluates
  • pass the code to V8 for evaluation
  • grab the return value (which is our obj):
    const fs = require('fs'),
        vm = require('vm');

    const code = fs.readFileSync('path-to-js-file', 'utf8');
    const obj = vm.runInNewContext(code + ';obj');

Upvotes: 5

Related Questions