herd-community icon indicating copy to clipboard operation
herd-community copied to clipboard

[Bug]:Laravel Herd on Windows using exec(), importing Puppeteer fails while Playwright and simple scripts work. Works on Xampp

Open ysufmv opened this issue 1 year ago • 1 comments

Platform

Windows

Operating system version

Windows 11 Pro (23H2 22631.3880)

System architecture

Windows

Herd Version

1.8.0 (Pro)

PHP Version

No response

Bug description

In Laravel Herd for Windows, attempting to run exec( ) on a Node.js script that imports Puppeteer fails with an EISDIR error. Simple scripts work. And to test for an alternative I tried importing Playwright which works fine and the script executes succesfully.

The same code to import Puppeteer works correctly when executed in a XAMPP server instead of Laravel Herd, and the script succesfully executes.

The issue cropped up when I was doing some testing on my main site. To make sure there is no intereference from any other packages / scripts, I added a new Laravel instance called nodetest using Herd and chose default configurations.

The reason why I am trying to access Puppeteer on the back-end, via Laravel exec(), is because its a business logic that needs to be hidden from the front-end users, and, right now it is the only way possible I know of that can achieve the same results on my actual production server. That is not the goal of raising this issue.

The outcome from raising this issue would be to identify and resolve the reason why it fails to work on Laravel Herd while the same code works on a Xampp server. I am assuming that it is not specific to Puppeteer and other packages may also fail to import.

My Laravel Herd Active Services

image

The php & node version image

image

Sites: image

Further background: I have the following installed on Windows, prior to installing Laravel Herd: Composer Xampp for Windows PHP 8.2.12 (All services stopped for the test) Node v21.5.0 (Might be interferring - but I don't know if its relevant) npm v10.2.4 pnpm v9.0.6

Also tested on a Hyper-V Virtual Machine with a fresh Windows installation via ISO image.

Steps to reproduce

Assuming that you have Laravel Herd (1.8.0) installed. If you have Xampp or any other similar services installed, you can stop all of its services and close it.

  • Enable all Services on Herd.
  • Click "Open Sites."
  • Click "+Add" on the top-left corner to open the Create New Site Dialog. Choose: "New Laravel project" > "No starter kit" > Give any project name you wish. In my case, I am naming it nodetest2, and proceed.

image

  • Wait for Laravel installation to complete.

image

  • Click to Enable HTTPS and allow the creation and addition of certificates by allowing Windows PowerShell to run. I don't think this is relevant but since we are retracing my steps to reproduce the error.

  • Open Terminal and install Puppeteer:

pnpm i puppeteer

image

  • After installation is complete. Open your Project on your code editor.

  • Go to routes/web.php and change the route for: " / " (welcome) route to the following:


Route::get('/', function () {
    $output = [];
    $return_var = 0;

    $nodePath = 'node';
    $scriptPath = base_path('public/checkPuppeteer.js'); //doesn't matter if it is .mjs .js 

    $command = "\"$nodePath\" \"$scriptPath\" 2>&1";
    exec($command, $output, $return_var);

    dd($command, $output, $return_var);

    if ($return_var != 0) {
        return response()->json([
            'success' => false,
            'message' => 'Could not run Puppeteer check',
            'output' => $output,
            'return_var' => $return_var
        ], 200);
    } else {
        return response()->json([
            'success' => true,
            'message' => 'Puppeteer check ran successfully',
            'output' => $output,
            'return_var' => $return_var
        ], 200);
    }
});
  • Go to the public folder and create a file named: checkPuppeteer.js

Add the following code:

import puppeteer from 'puppeteer';
import { writeFileSync } from 'fs';

(async () => {
    try {
        const browser = await puppeteer.launch({ headless: true });
        const page = await browser.newPage();
        await page.goto('https://example.com');
        console.log('Puppeteer launched successfully');
        writeFileSync('./log.txt', 'Puppeteer launched successfully', 'utf8');
        await browser.close();
    } catch (error) {
        console.error('Error launching Puppeteer:', error);
        writeFileSync('./log.txt', `Error: ${error}`, 'utf8');
    }
})();

As seen:

image

  • Open the site on your browser to see the error result:

image

To check if it works normally, you can run the checkPuppeteer.js on the terminal as below: 'cd' into the 'public' folder and type:

node checkPuppeteer.js

image

It should execute successfully, as above.

  • Now to check if its related to exec( ) we can replace the code in checkPuppeteer.js to something that doesn't include the puppeteer import, like:
import { writeFileSync } from 'fs';

const testFunction = () => {
    writeFileSync('./log.txt', 'Laravel Herds is for Sheep Sites', 'utf8');
    console.log('Laravel Herds is for Sheep Sites');
};

testFunction();

It should execute succesfully:

image

For good measure I wanted to see if an alternative package would work, so I installed Playwright

  • You can try Playwright by installing it from the terminal:
pnpm install playwright 

// if you are installing playwright fresh you would need to get the binaries by executing: 

npx playwright install 

Change your checkPuppeteer.js code to below to check with Playwright:

import { chromium } from 'playwright';
import { writeFileSync } from 'fs';

(async () => {
    try {
        const browser = await chromium.launch({ headless: true });
        const page = await browser.newPage();
        await page.goto('https://example.com');
        console.log('Playwright launched successfully');
        writeFileSync('./log.txt', 'Playwright launched successfully', 'utf8');
        await browser.close();
    } catch (error) {
        console.error('Error launching Playwright:', error);
        writeFileSync('./log.txt', `Error: ${error}`, 'utf8');
    }
})();

Open the website on your browser. You would see that it takes a few more seconds to load because in the background the script is succesfully executing. Afterwards, you should see the dump on the screen showing it is succesful: image

All this works just fine on Xampp server, which was what I was using till recently. But after exploring Laravel Herd, I immediately wanted to make it my go-to development server. I subscribed to Laravel Herd Pro and was on my way to get everything sorted when this popped up, and it's been nagging me.

It may not be a major issue or something to do with my configurations. (I've tested the same on a frest Hyper-V Virtual Machine (Windows 11 with Windows ISO image)

Steps Taken

  • Setup Hyper-V Machine and install Windows 11

  • Download latest version of Laravel Herd and install it.

  • php, laravel, composer is installed. node is also installed. Pressing the terminal opens up Windows Powershell but node is not available there. So I encountered errors when I tried to replicate above steps because node was not available in Powershell and when Laravel was calling node via exec(), it was opening Powershell. I assumed it was due to not being added to PATH or Powershell. So I proceeded to download nodejs v22.4.1 prebuilt installer for Windows and installed it with Additional binaries option checked on. After installation, it was still not available on Powershell so I added it manually by going to Settings > System > About > Advanced system settings > Environment Variables > PATH > New > Type in: C:\Program Files\nodejs\ and click OK. Then open Windows Powershell with administrator rights and Type: [Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\Program Files\nodejs\", [EnvironmentVariableTarget]::Machine) It should add node to Powershell. Restart Windows to ensure all settings are ok.

  • Afterwards, repeat the instructions from the top of this section to replicate the same error. (instead of pnpm I used npm since I would need to install pnpm as well)

Why I am posting this: I love the simplicity of Laravel Herd and want to work with it in the future. This may be an edge case. But it forces me to go back and use Xampp or change the packages I currently use.

I may have missed some very simple solutions that could circumvent this error. So I'm all ears.

Relevant log output

""node" "D:\Sheep\nodetest2\public/checkPuppeteer.js" 2>&1"
array:21 [▼
  0 => "node:net:428"
  1 => "      throw new ErrnoException(err, 'open');"
  2 => "            ^"
  3 => ""
  4 => "Error: open EISDIR"
  5 => "    at new Socket (node:net:428:13)"
  6 => "    at process.getStdin [as stdin] (node:internal/bootstrap/switches/is_main_thread:224:17)"
  7 => "    at get (<anonymous>)"
  8 => "    at getOwn (node:internal/bootstrap/realm:202:5)"
  9 => "    at BuiltinModule.syncExports (node:internal/bootstrap/realm:377:31)"
  10 => "    at ModuleWrap.<anonymous> (node:internal/bootstrap/realm:357:17)"
  11 => "    at BuiltinModule.getESMFacade (node:internal/bootstrap/realm:362:17)"
  12 => "    at BuiltinModule.compileForPublicLoader (node:internal/bootstrap/realm:340:10)"
  13 => "    at loadBuiltinModule (node:internal/modules/helpers:101:7)"
  14 => "    at ModuleLoader.builtinStrategy (node:internal/modules/esm/translators:454:18) {"
  15 => "  errno: -4068,"
  16 => "  code: 'EISDIR',"
  17 => "  syscall: 'open'"
  18 => "}"
  19 => ""
  20 => "Node.js v21.5.0"
]

ysufmv avatar Jul 11 '24 19:07 ysufmv

I assume that this is somehow related to the way PHP resolves the node binary (and possibly other environment variables needed for executing this).

Could you try and re-run the same code in Herd but pass an absolute path to "node"?

mpociot avatar Aug 09 '24 10:08 mpociot