dimkir / nightmare-lambda-tutorial

Sample project and tutorial to run NightmareJS on AWS Lambda
75 stars 9 forks source link

electronPath: require(electronPath) Not working !! #5

Open rjsalem opened 6 years ago

rjsalem commented 6 years ago

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) });

dimkir commented 6 years ago

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()
            });    

    });
};
rjsalem commented 6 years ago

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.

dimkir commented 6 years ago

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

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?

rjsalem commented 6 years ago

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();
dimkir commented 6 years ago

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);

}
rjsalem commented 6 years ago

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 :)