I was accessing a project that builds a nextJs website.
It was originally launched with npm run dev.
The project used devcert as a local proxy to allow HTTPS access locally.
Normally I would launch it in an Ubuntu environment running on Windows with WSL2.
This time I ran it direct from Windows. After all, node is multiplatform.
However I started hitting some issues:
Running devcert to install the certificates.
requires OpenSSL and sh
devcert errors with Node 22
devcert was errorring because it uses a deprecated createCipher method. This is now deprecated in Node 22. To workaround this I used nvm, installed Node 21, and ran the installation code. This was a bit of a hack.
conditional sudo code
// This script detects the Operating System. If it is not Windows, it applies the sudo command before running the target script. // Usage: // node ./run-elevated.js [script.js] const { exec } = require("child_process"); const os = require("os"); const isWindows = os.platform() === "win32"; // Detect the OS const targetScript = process.argv[2]; // The first argument after the script name if (!targetScript) { console.error( "Error: No script specified. Usage: node run-script.js [your-script.js]", ); process.exit(1); } // Command to execute const command = isWindows ? `node ${targetScript}` : `sudo node ${targetScript}`; // Execute the command exec(command, (error, stdout, stderr) => { if (error) { console.error(`Error: ${error.message}`); return; } if (stderr) { console.error(`Stderr: ${stderr}`); return; } console.log(`Output: ${stdout}`); });
stdio not accessible
When creating a certificate, devcert prompts for a password. The code above wouldn't work because stido was not available to the launching console.
The code was changed to:
// This script detects the Operating System. If it is not Windows, it applies the sudo command before running the target script. // Usage: // node ./run-elevated.js [script.js] const { spawn } = require("child_process"); // Consider cross-spawn (https://www.npmjs.com/package/cross-spawn) const os = require("os"); const isWindows = os.platform() === "win32"; // Detect the OS const targetScript = process.argv[2]; // The first argument after the script name if (!targetScript) { console.error( "Error: No script specified. Usage: node run-elevated.js [your-script.js]", ); process.exit(1); } // Spawn the child process and inherit stdio to enable user interaction const child = isWindows ? spawn("node", [targetScript], { stdio: "inherit", // Inherit stdio streams from the parent process }) : spawn("sudo", ["node", targetScript], { stdio: "inherit", // Inherit stdio streams from the parent process }); // Handle errors if the child process fails child.on("error", (error) => { console.error(`Error: ${error.message}`); }); // Handle when the child process exits child.on("exit", (code) => { console.log(`Child process exited with code ${code}`); });
cross-spawn should still be considered as an improvement.
devcert uses environment variables on Windows. turbo doesn't pass them
The final problem was that devcert tries to locate the certificates using environment variables on Windows:
function win32 (name) { if (process.env.LOCALAPPDATA) { return path.join(process.env.LOCALAPPDATA, name) } return path.join(process.env.USERPROFILE, 'Local Settings', 'Application Data', name) }
If you launch the project with npm run dev, the environment variables are passed.
This was verified by adding the following script to package.json and running it with npm run dev and npx turbo run dev. The first logged the environment variables and the second didn't.
{ "scripts": { "check-env": "node -e \"console.log(process.env)\"" } }
If you launch it with turbo dev, they are undefined, and the code errors.
The solution was to add the following to turbo.json:
"globalPassThroughEnv": ["LOCALAPPDATA"],