Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zero install support #37

Open
sberney opened this issue Sep 12, 2022 · 2 comments
Open

Zero install support #37

sberney opened this issue Sep 12, 2022 · 2 comments

Comments

@sberney
Copy link

sberney commented Sep 12, 2022

Hello, I have a few small yarn projects, and I use the latest yarn features, like zero install. This is the main difference between yarn 1 and yarn 2/3 other than plugin support: instead of installing dependencies recursively in nested folders (or even running deduping to flatten the hierarchy), it installs all the dependencies flat and compressed into the .yarn/cache folder. It then overrides the node linker when you run the bin entry point, so that imports are resolved using yarn instead of node.

I have a working project, but I can't add a nix build using this plugin, because it doesn't support the new installation paradigm. When it tries to execute the entry point defined using "bin": "index.js" in package.json, I get the message Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'whatever-package-it-tries-to-import' .... I would replace it with "bin": "yarn start" since bootstrapping via yarn makes it kick in and use the yarn linker, but I get The lockfile would have been modified by this install, which is explicitly forbidden.. That indicates that the plugin is still circumventing the yarn configuration and installing using the legacy node linker dependency layout.

As a side note for why I'm trying to do this at all, I have figured out how to use nix to build bazel targets, so I would love to have a nix way to build yarn projects, and thus a bazel way to build well encapsulated yarn projects I can drop into my repo. This plugin does work if I abandon the new yarn installation paradigm, but that doesn't make any sense to me. I would have to change my (non-nix) build to something years out of date, I would have to build using nix every time since the entire setup would be structured around it (instead of structured around yarn), and I don't see the utility of using yarn at all over npm in that case if changes are to be made at that scale.

It seems to me the yarn v2/3 installation paradigm is very philosophically compatible with nix, in that both are basically just stepping in to act as linkers based on configuration with transient caches. The whole "zero" part of zero install I believe references the idea that you're commiting all the dependencies to your monorepo (which might use bazel for example) so you aren't dependent on any outside (mutable) downloads, but I see that as a cool side effect of the overall process.

I'm pretty new to nix or I'd try to help out or do it myself. What would it take to get a full implementation of this? Thanks for your very cool project.

@stephank
Copy link
Owner

So, zero-installs is how I started out packaging Yarn projects for Nix. I've not felt the need to move those projects over to this plugin, though, because the existing expressions are not too ugly and work fine.

It'd still be nice if the plugin had support, because it would automate some things, and offer an all-around solution for any Yarn project. Not sure if/when I'll get around to it, though.

Any way, here's an example default.nix you can use as a starting point:

{ pkgs ? import <nixpkgs> { } }:

pkgs.stdenv.mkDerivation rec {
  pname = "myproject";
  version = "0.1";
  src = ./.;

  buildInputs = with pkgs; [ nodejs ];

  configurePhase = ''
    # Define an alias for Yarn.
    NIX_YARN_PATH=$PWD/.yarn/releases/yarn-*.cjs
    yarn() { CI=1 yarn_global_folder=$TMP $NIX_YARN_PATH "$@"; }

    # Run Yarn zero-install.
    yarn install --immutable --immutable-cache
  '';

  buildPhase = ''
    yarn build  # or some other build step
  '';

  installPhase = ''
    mkdir -p $out/bin $out/libexec

    # Move the whole project to libexec in the output.
    projectDir=$out/libexec/${pname}
    cd $NIX_BUILD_TOP
    mv $sourceRoot $projectDir

    # Create a binary wrapper.
    cat > $out/bin/myproject << EOF
    #!${pkgs.runtimeShell}
    export NODE_OPTIONS='--require $projectDir/.pnp.cjs'
    exec ${pkgs.nodejs}/bin/node $projectDir/server.js "\$@"
    EOF
    chmod a+x $out/bin/myproject
  '';
}

@sberney
Copy link
Author

sberney commented Sep 20, 2022

Thank you, I will try this. 😁

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants