Some context:
This issue was opened first on the Spago repo, but it turned out that purs
is also affected.
Yarn 2 PnP (possibly even using the much more widely used node_modules
approach) can’t run purs
or spago
installed from npm. More generally, Yarn 2 can’t run a binary unless it is written in JS. It seems that this behavior is intentional and is a wontfix.
One workaround stated in the Yarn issue is to use execFileSync
. @hdgarrood mentioned that using a JS file is possible and actually was a previous approach. However, it causes weird issues with the REPL, presumably since all input and output is directed through an extra node process.
cc @thomashoneyman this might be a reason to recommend npm over yarn?
It’s certainly disappointing. The recommended tools post ought to at least mention this as a problem with Yarn 2.
The linked GitHub issue states that shell scripts aren’t meant to be supported via the bin
field, but I’d imagine that the compiler is not the only tool that will be affected by this change. I’d be surprised if they truly disallow running anything that’s not JS. Ideally either Yarn addresses this or we are able to work around the restriction rather than give up on Yarn altogether.
For our part at work (where we use Nix to manage all dependencies, and Yarn to describe (but not install) JavaScript dependencies) we’ll continue using Yarn because yarn2nix
is so nice, but I understand that this isn’t the most common way people manage PureScript projects and I wouldn’t recommend it as the default.
So with all that in mind, I don’t want to give up on Yarn if we don’t have to, but I do think the recommended tools should prefer NPM if Yarn 2 is picking up steam.
Edit: I’ve edited the recommended tools post.
3 Likes
Here’s a hack I did to test out the execFileSync
approach. I used spawn
instead:
// first rename purs.bin to purs2.bin
const { spawn } = require('child_process');
spawn('./.yarn/unplugged/purescript-npm-0.13.6-7f5d88f564/node_modules/purescript/purs2.bin', process.argv.slice(2), { detatched: true, stdio: 'inherit' });
Obvious problem with this is the fact that I’ve had to hardcode the path. Wish yarn
had an option to provide that for me based on the package name.
Also, this was just a proof-of-concept. I don’t know if this would work in production. I suspect there are a few wrinkles still.
1 Like
Okay, so a little more hacking and I got them working. All the hacking is under the .yarn/unplugged
folder.
First, to make purescript work I renamed purs.bin
to purs2.bin
and I created a new purs.bin
with:
// first rename purs.bin to purs2.bin
const { spawn } = require('child_process');
spawn(__dirname + '/purs2.bin', process.argv.slice(2), { detatched: true, stdio: 'inherit' });
And to get spago to work I renamed spago
to spago2
and created spago.js
with:
// first rename spago to spago2
const { spawn } = require('child_process');
const { chmod, constants } = require('fs');
const spago = __dirname + '/spago2';
chmod(spago, constants.S_IXUSR | constants.S_IXG | constants.S_IXOTH, _ => spawn(spago, process.argv.slice(2), { detatched: true, stdio: 'inherit' }));
This change would have to be made every time you install or upgrade purescript
or spago
. And notice how I had to make spago2
executable. Not sure why it wasn’t. Seems as if purs2.bin
was. Not really motivated ATM to figure this part out.
Still a nightmare to patch unless it could be automated somehow. I did notice that the yarn.lock
is where the bin
is stored and referenced. Must be cached from the package.json
. That’s another possible patch point which eliminates the need for renaming. I’m not too familiar with yarn
(actually, today was my first encounter with it), so I’m not sure if this file gets overwritten often and I was worried I’d lose my patches, which is why I chose the rename option.
So an automated hack could be done here. Obviously, it would be far better to fix this at the yarn
level, but I thought I’d at least document my findings in case someone else finds this useful.
2 Likes