nightmare-lambda-tutorial icon indicating copy to clipboard operation
nightmare-lambda-tutorial copied to clipboard

electronPath: require(electronPath) Not working !!

Open rjsalem opened this issue 7 years ago • 6 comments

Hi,

When I am using your packages to make work nightmare on aws lambda, I get this error :

(function (exports, require, module, __filename, __dirname) { ELF╗╔╔ ^ SyntaxError: Invalid or unexpected token at createScript (vm.js:56:10) at Object.runInThisContext (vm.js:97:10) at Module._compile (module.js:542:28) at Object.Module._extensions..js (module.js:579:10) at Module.load (module.js:487:32) at tryModuleLoad (module.js:446:12) at Function.Module._load (module.js:438:3) at Module.require (module.js:497:17) at require (internal/module.js:20:19) at xvfb.start (/var/task/media-server/index.js:39:27)

This error is provoked when we require electron for nightmare :

let nightmare = Nightmare({ show: false, electronPath: require(electronPath) });

rjsalem avatar Feb 27 '18 17:02 rjsalem

TL;DR;

Within your code you are using require() function which is meant to include JS-files. Whereas electronPath points to binary linux executable (ELF - Executable and Linking Format).

If you use use it without require it should work:

        var nightmare = Nightmare({
          show: true,                  
          electronPath: electronPath  
        });

Longer explanation (if you still can't make it work)

In order to make Electron run on lambda, you would need to follow very precisely the steps outlined in the README

I would recommend to scaffold your project with this code:

cd nightmare-tut-hello
npm init -y
npm install nightmare 
mkdir -p lib/bootstrap
wget -O lib/bootstrap/nightmare-lambda-pack.js https://raw.githubusercontent.com/dimkir/nightmare-lambda-tutorial/master/lib/bootstrap/nightmare-lambda-pack.js
wget -O lib/bootstrap/xvfb.js https://raw.githubusercontent.com/dimkir/nightmare-lambda-tutorial/master/lib/bootstrap/xvfb.js

And later create your main script index.js using EXACTLY THIS structure:

Within this code, your electronPath is provided by the binaryPack module, which is responsible for downloading electron binary version for Lambda. And also you have flag isOnLambda which allows you to run the same script both on labmda and local machine (for debugging) without extra modifications.

var 
  binaryPack = require('./lib/bootstrap/nightmare-lambda-pack'), // WIP: should be `require('nightmare-lambda-pack')`
  Xvfb       = require('./lib/bootstrap/xvfb'),                  // WIP: should be `require('xvfb')`
  Nightmare  = require('nightmare')
;

var isOnLambda = binaryPack.isRunningOnLambdaEnvironment;

var electronPath = binaryPack.installNightmareOnLambdaEnvironment();

exports.handler = function(event, context){
    var xvfb = new Xvfb({
        xvfb_executable: '/tmp/pck/Xvfb',  // Xvfb executable will be at this path when unpacked from nigthmare-lambda-pack
        dry_run: !isOnLambda         // in local environment execute callback of .start() without actual execution of Xvfb (for running in dev environment)
    });
    
    xvfb.start((err, xvfbProcess) => {

        if (err) context.done(err);

        function done(err, result){
            xvfb.stop((err) => context.done(err, result));
        }

        // ...
        // Main logic with call to done() upon completion or error
        // ...

        var nightmare = Nightmare({
          show: true,                   // show actual browser window as Nightmare clicks through
          electronPath: electronPath    // you MUST specify electron path which you receive from installation
        });

        nightmare
            .goto('https://duckduckgo.com')
            .type('#search_form_input_homepage', 'github nightmare')
            .click('#search_button_homepage')
            .wait('#r1-0 a.result__a')
            .evaluate(function () {
                return document.querySelector('#r1-0 a.result__a').href;
            })
            .end()
            .then(function (result) {
                console.log(result);
                done(null, result);  // done() instead of context.done()
            })
            .catch(function (error) {
                console.error('Search failed:', error);
                done(error);         // done() instead of context.done()
            });    

    });
};

dimkir avatar Feb 28 '18 21:02 dimkir

I followed all the steps with your example. No problem happened. But I never get a response and the timeout limit is always reach even if i put 2 minutes for the timeout.

rjsalem avatar Mar 01 '18 01:03 rjsalem

It would be helpful if you can post additional details of your problem:

  • actual code you're using would be helpful
  • what's the configuration of the lambda function (eg output of aws lambda get-function-configuration --function-name your-funciton would be helpful)
  • screenshots of errors (or specific error messages)

those will help me understand your problem better and come up with solutions.

ps. and additional question - does your script run correctly on local machine?

dimkir avatar Mar 01 '18 02:03 dimkir

Hi, I found what is the cause but I dont know how I can make it work.

In Index.js I initialize everything and I have a package named Test. I call test.scrap to execute nightmare. If the nightmare code is in the index file it will work, but if I use my Test package it won't.

Index.js :

// index.js
var binaryPack = require('./lib/bootstrap/nightmare-lambda-pack');
var  Xvfb = require('./lib/bootstrap/xvfb');            
var Nightmare  = require('nightmare');
var test = require('./test');
var isOnLambda = binaryPack.isRunningOnLambdaEnvironment;
var electronPath = binaryPack.installNightmareOnLambdaEnvironment();

exports.handler = function(event, context, callback) {

    var xvfb = new Xvfb({
        xvfb_executable: '/tmp/pck/dist/Xvfb',
        dry_run: !isOnLambda
    });
    
    xvfb.start((err, xvfbProcess) => {

        if (err) callback(err);

        function done(err, result){
            xvfb.stop((err) => callback(err, result));
        }

        // ...
        // Main logic with call to done() upon completion or error
        // ...

        var nightmare = Nightmare({
          show: false,                   // show actual browser window as Nightmare clicks through
          electronPath: electronPath   // you MUST specify electron path which you receive from installation
        });

        test.scrap(nightmare, done);

    });
};

Test module:

// test.js
function Test() { }

Test.prototype.scrap = function (nightmare, done) {
    nightmare
            .goto('https://duckduckgo.com')
            .type('#search_form_input_homepage', 'github nightmare')
            .click('#search_button_homepage')
            .wait('#r1-0 a.result__a')
            .evaluate(function () {
                return document.querySelector('#r1-0 a.result__a').href;
            })
            .end()
            .then(function (result) {
                console.log(result);
                done(null, result);
            })
            .catch(function (error) {
                console.error('Search failed:', error);
                done(error);      
            }); 
}
modules.export = new Test();

rjsalem avatar Mar 01 '18 20:03 rjsalem

I have tried running your code locally (not on lambda). And there's typo:

modules.export = new Test(); 
//    ^ no need for 's' here

module.exports = new Test(); 
//           ^ must have 's' at the end of exports

I would recommend always testing functions locally before pushing them to lambda. Getting logs and reasonable error messages from there is much harder than on local machine.

You can use the script below to run lambda locally

//
//  index-runner.js
//
const
  handler = require('./index').handler;
  
;

handler({}, { done }, done);

function done(err, result){
    if ( err ) console.error(err);
    else console.log(`Result: `, result);

    process.exit(1);

}

dimkir avatar Mar 02 '18 16:03 dimkir

Finally I repaired the error. The lambda wasn't working because I was using Download package. If I was doing var download = require('download') it was not working, when I removed the require it worked. So I decided to change the download package for miniget.

Thanks for your help :)

rjsalem avatar Mar 02 '18 21:03 rjsalem