remix: [Bug]: Yarn 3 support?

Which Remix packages are impacted?

  • remix (Remix core)
  • create-remix

What version of Remix are you using?

1.0.5

Steps to Reproduce

Trying to install dependencies with Yarn berry. My current Yarn version is v3.2.0-rc.5.

Log file:

# This file contains the result of Yarn building a package (azat-io@workspace:.)
# Script name: postinstall

(node:67592) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
EROFS: read-only filesystem, open '/node_modules/remix/package.json'

Dependencies in my package.json:

{
  "dependencies": {
    "@remix-run/node": "1.0.5",
    "@remix-run/react": "1.0.5",
    "@remix-run/serve": "1.0.5",
    "@remix-run/server-runtime": "1.0.5",
    "react": "17.0.2",
    "react-dom": "17.0.2",
    "remix": "1.0.5"
  },
  "devDependencies": {
    "@remix-run/dev": "1.0.5",
    "@types/react": "17.0.24",
    "@types/react-dom": "17.0.9",
    "typescript": "4.1.2"
  }
}

Expected Behavior

Installation successful

Actual Behavior

Installation failed

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 10
  • Comments: 41 (24 by maintainers)

Most upvoted comments

I made some more progress! The build now runs fine with PnP and the dev server starts up correctly as well. To test my changes:

Clone and build my changed version of remix:

git clone https://github.com/cmd-johnson/remix/tree/feature/yarn-pnp
cd remix
yarn install
yarn build

In the project you want to use yarn PnP in:

  • switch to the current canary build of yarn: yarn set version canary. Yarn < 3.2.0 has a bug preventing remix to work with PnP.
  • remove the remix dependency from your package.json, it just doesn’t work with PnP right now (I’ll see what I can do about that next)
  • while you’re at it, remove remix setup node from your postinstall script. It will fail when using PnP
  • add a resolutions entry to your package.json:
      "resolutions": {
        "@remix-run/dev": "file:///path/to/the/cloned/remix/project/build/node_modules/@remix-run/dev",
        "@remix-run/express": "file:///path/to/the/cloned/remix/project/build/node_modules/@remix-run/express",
        "@remix-run/node": "file:///path/to/the/cloned/remix/project/build/node_modules/@remix-run/node",
        "@remix-run/react": "file:///path/to/the/cloned/remix/project/build/node_modules/@remix-run/react",
        "@remix-run/serve": "file:///path/to/the/cloned/remix/project/build/node_modules/@remix-run/serve"
      },
    
    This makes sure you’re using the patched @remix-run/* versions instead of the original ones throughout your whole project
  • run yarn add @yarnpkg/esbuild-plugin-pnp. This is an optional peer dependency I’ve added to @remix-run/dev that you’ll need when using PnP for the esbuild build to go through
  • run yarn add express. @remix-run/serve depends on it as a peer dependency. Depending on your setup, you might be able to get away with adding express as a peer dependency instead
  • the last thing to change is fixing all imports from the remix package to imports from @remix-run/* packages. E.g.:
    • RemixBrowser@remix-run/react/browser
    • EntryContext, MetaFunction@remix-run/server-runtime (you might have to add a dependency to @remix-run/server-runtime to your project as well)
    • Most other dependencies appear to be exported by @remix-run/react

After that, you should be able to start the dev server using yarn dev as usual.

I hope I didn’t forget anything. I’ll draft a PR once I get around to get the remix package to work with PnP.

@tavoyne #1316 should be ready for merging, but feel free to test it yourself! Everything should work as long as you don’t import anything from the remix package and use the @remix-run/* packages directly instead. Also check out the README from the yarn example I added in the PR: https://github.com/remix-run/remix/blob/7af1ed35603839d85866d3d793ecc89c2244236b/examples/yarn-pnp/README.md

I dug a bit deeper into what it would take to support yarn PnP, and these are my findings so far:

TLDR: you’re better off sticking to the node-modules linker for now.

  • remix performs some dark magic with the remix package in the postinstall hook that will never be compatible with yarn PnP, and frankly is an anti-pattern (don’t overwrite files in packages, please). I ended up throwing out remix as a dependency and now import everything directly from the @remix-run/... packages as required
  • there’s a few dependency/peer dependency declarations missing in the remix packages. which is an easy fix.
  • after fixing the dependency declarations, you’ll run into a bug in the current version of yarn, which appears to be fixed in the current canary build of yarn (yarn-3.2.0-rc.8, install it using yarn set version canary). (ref. yarnpkg/berry/issues/3687)
  • now we run into an issue with esbuild: to make React available in all source files without having to explicitly import it, remix uses the inject option to add the contents of a react.ts file to every compiled source file. While it is possible to read files inside the zipped pnp dependencies wihle running in node, esbuild tries to read those files from an executable written in go, which fails. I added some code to compiler.ts in @remix-run/dev to copy the react.ts file contents to a temporary build folder and include that file instead. Now the build goes through.
  • however, we’re still not quite there yet. There’s a server listening on 8002 now, but it only returns 426 Upgrade Required without any indications of what protocol it expects instead of HTTP/1.1. Looks like it doesn’t start up properly, because the “Remix App Server started at …” never gets logged to the console.

For reference, this is my current .yarnrc.yml, and these are the changes I made to the compiler.ts file to get esbuild to work

That’s where I’m at now, I’ll continue digging once I find some more time.

Thank you, @MichaelDeBoey. yarn dlx create-remix@v0.0.0-nightly-34577c6-20220507 now runs without error in a Yarn 3 monorepository using PnP.

yarn dev still gives the following:

✘ [ERROR] Could not read from file: .yarn/cache/@remix-run-dev-npm-0.0.0-nightly-34577c6-20220507-bdbe7194e9-1f94a83917.zip/node_modules/@remix-run/dev/compiler/shims/react.ts

 ✘ [ERROR] Could not resolve "@remix-run/react"

    app/entry.client.tsx:1:29:
      1 │ import { RemixBrowser } from "@remix-run/react";
        ╵                              ~~~~~~~~~~~~~~~~~~

  You can mark the path "@remix-run/react" as external to exclude it from the bundle, which will remove this error.

 ✘ [ERROR] Could not resolve "react-dom"

    app/entry.client.tsx:2:24:
      2 │ import { hydrate } from "react-dom";
        ╵                         ~~~~~~~~~~~

  You can mark the path "react-dom" as external to exclude it from the bundle, which will remove this error.

 ✘ [ERROR] Could not resolve "@remix-run/react"

    app/root.tsx:9:7:
      9 │ } from "@remix-run/react";
        ╵        ~~~~~~~~~~~~~~~~~~

  You can mark the path "@remix-run/react" as external to exclude it from the bundle, which will remove this error.

Build failed with 4 errors:
error: Could not read from file: .yarn/cache/@remix-run-dev-npm-0.0.0-nightly-34577c6-20220507-bdbe7194e9-1f94a83917.zip/node_modules/@remix-run/dev/compiler/shims/react.ts
app/entry.client.tsx:1:29: ERROR: Could not resolve "@remix-run/react"
app/entry.client.tsx:2:24: ERROR: Could not resolve "react-dom"
app/root.tsx:9:7: ERROR: Could not resolve "@remix-run/react"
Error
    at Object.onBuildFailure (.yarn/cache/@remix-run-dev-npm-0.0.0-nightly-34577c6-20220507-bdbe7194e9-1f94a83917.zip/node_modules/@remix-run/dev/cli/commands.js:153:13)
    at buildEverything (.yarn/cache/@remix-run-dev-npm-0.0.0-nightly-34577c6-20220507-bdbe7194e9-1f94a83917.zip/node_modules/@remix-run/dev/compiler.js:280:13)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async Object.build (.yarn/cache/@remix-run-dev-npm-0.0.0-nightly-34577c6-20220507-bdbe7194e9-1f94a83917.zip/node_modules/@remix-run/dev/compiler.js:105:3)
    at async Object.build (.yarn/cache/@remix-run-dev-npm-0.0.0-nightly-34577c6-20220507-bdbe7194e9-1f94a83917.zip/node_modules/@remix-run/dev/cli/commands.js:148:3)
    at async Object.run (.yarn/cache/@remix-run-dev-npm-0.0.0-nightly-34577c6-20220507-bdbe7194e9-1f94a83917.zip/node_modules/@remix-run/dev/cli/run.js:468:7)

Not sure how I can go about fixing this issue. I tried adding @remix-run/react and react-dom to serverDependenciesToBundle in remix.config.js, to no avail. And it doesn’t seem like an error that can be fixed by configuring packageExtensions in .yarnrc.yml either. I also tried adding "dependenciesMeta": { "@remix-run/react": { "built": false }, "react-dom": { "built": false } } to the monorepo’s root package.json, but that didn’t help either.

Thoughts?

I was experiencing this as well! I actually recognized this as esbuild (which remix uses under the hood) not supporting yarn’s PnP resolver – (closed) issue for that is here: https://github.com/evanw/esbuild/issues/154

To fix this, I’d normally use @yarnpkg/esbuild-plugin-pnp, but remix does not support adding esbuild plugins – (closed) issue for that is here: https://github.com/remix-run/remix/issues/717. As a workaround, I used the extremely hacky override mentioned in that issue to add in @yarnpkg/esbuild-plugin-pnp to the compiler.

./esbuild.register.js:

const Module = require("node:module");

const { pnpPlugin } = require("@yarnpkg/esbuild-plugin-pnp");
const esbuild = require("esbuild");

const originalBuild = esbuild.build;
const build = (options) => {
    return originalBuild({
        ...options,
        plugins: [...options.plugins, pnpPlugin()],
    });
};

const originalRequire = Module.prototype.require;
Module.prototype.require = function (id) {
    if (id === "esbuild") {
        return { ...esbuild, build };
    }

    return originalRequire.apply(this, arguments);
};

package.json:

{
  "scripts": {
    "build": "NODE_OPTIONS=\"${NODE_OPTIONS} --require ./esbuild.register\" remix build",
    "start:dev": "NODE_OPTIONS=\"${NODE_OPTIONS} --require ./esbuild.register\" remix dev"
  }
}

The next issue that I ran into was this:

The following error is a bug in Remix; please open an issue! https://github.com/remix-run/remix/issues/new
Missing output for entry point
💿 Built in 453ms

Turns out, @yarnpkg/esbuild-plugin-pnp adds the pnp namespace to files loaded via PnP (so it knows how to process them later; this is common practice in the esbuild plugin world). The files it loads are emitted in the esbuild manifest with filenames looking like pnp:/path/to/filename.ext.

The issue is that to find the project’s entry point, @remix-run/dev is checking for entryPointFile === entryClientFile. This would normally be fine, because it’s an absolute path, but in this case, this check will be false for the correct file because of the pnp: prefix added by @yarnpkg/esbuild-plugin-pnp that’s not normally there.

So, the last step was to yarn patch @remix-run/dev:

diff --git a/compiler/assets.js b/compiler/assets.js
index c675d328d512ceff6e8e6c9f07578f132417d375..d837842d85a07158857778b00003a4ca089bd370 100644
--- a/compiler/assets.js
+++ b/compiler/assets.js
@@ -61,7 +61,7 @@ async function createAssetsManifest(config, metafile, cssModules) {
     if (!output.entryPoint) continue;
     let entryPointFile = path__namespace.resolve(output.entryPoint.replace(/(^browser-route-module:|\?browser$)/g, ""));
 
-    if (entryPointFile === entryClientFile) {
+    if (entryPointFile.endsWith(entryClientFile)) {
       entry = {
         module: resolveUrl(key),
         imports: resolveImports(output.imports)

After this patch was created and applied, I was able to get remix working with yarn@4.0.0-rc.6! Ya boi loves to live on the bleeding edge 🚀


To the remix dev team, I’d really love to properly add PnP support to remix. It seems like it should be as simple as adding in @yarnpkg/esbuild-plugin-pnp to the default list of esbuild plugins (it’s a no-op if there’s no PnP API available) and applying a patch similar to the one above to @remix-run/dev. Happy to submit a PR if you’re interested!~

@chaance This is has not been fixed — I still get an error when running yarn dlx create-remix@latest:

/private/var/folders/dk/h4tc0zx90g9fdr9pbw48k6pr0000gn/T/xfs-a2f22fa1/dlx-26512/.pnp.cjs:16327
      Error.captureStackTrace(firstError);
            ^

Error: @remix-run/dev tried to access .bin, but it isn't declared in its dependencies; this makes the require call ambiguous and unsound.

Required package: .bin (via ".bin/jscodeshift")
Required by: @remix-run/dev@npm:1.4.1 (via /Users/bart/.yarn/berry/cache/@remix-run-dev-npm-1.4.1-2ab50c1d1f-8.zip/node_modules/@remix-run/dev/cli/migrate/)

Require stack:
- /Users/bart/.yarn/berry/cache/@remix-run-dev-npm-1.4.1-2ab50c1d1f-8.zip/node_modules/@remix-run/dev/cli/migrate/jscodeshift.js
- /Users/bart/.yarn/berry/cache/@remix-run-dev-npm-1.4.1-2ab50c1d1f-8.zip/node_modules/@remix-run/dev/cli/migrate/migrations/replace-remix-imports/index.js
- /Users/bart/.yarn/berry/cache/@remix-run-dev-npm-1.4.1-2ab50c1d1f-8.zip/node_modules/@remix-run/dev/cli/commands.js
- /Users/bart/.yarn/berry/cache/@remix-run-dev-npm-1.4.1-2ab50c1d1f-8.zip/node_modules/@remix-run/dev/cli/run.js
- /Users/bart/.yarn/berry/cache/@remix-run-dev-npm-1.4.1-2ab50c1d1f-8.zip/node_modules/@remix-run/dev/cli/index.js
- /Users/bart/.yarn/berry/cache/@remix-run-dev-npm-1.4.1-2ab50c1d1f-8.zip/node_modules/@remix-run/dev/index.js
- /Users/bart/.yarn/berry/cache/create-remix-npm-1.4.1-9c74da98d5-8.zip/node_modules/create-remix/cli.js
    at Function.require$$0.Module._resolveFilename (/private/var/folders/dk/h4tc0zx90g9fdr9pbw48k6pr0000gn/T/xfs-a2f22fa1/dlx-26512/.pnp.cjs:16327:13)
    at Function.resolve (node:internal/modules/cjs/helpers:108:19)
    at Object.<anonymous> (/Users/bart/.yarn/berry/cache/@remix-run-dev-npm-1.4.1-2ab50c1d1f-8.zip/node_modules/@remix-run/dev/cli/migrate/jscodeshift.js:17:39)
    at Module._compile (node:internal/modules/cjs/loader:1103:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1155:10)
    at Object.require$$0.Module._extensions..js (/private/var/folders/dk/h4tc0zx90g9fdr9pbw48k6pr0000gn/T/xfs-a2f22fa1/dlx-26512/.pnp.cjs:16371:33)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.require$$0.Module._load (/private/var/folders/dk/h4tc0zx90g9fdr9pbw48k6pr0000gn/T/xfs-a2f22fa1/dlx-26512/.pnp.cjs:16211:14)
    at Module.require (node:internal/modules/cjs/loader:1005:19)
    at require (node:internal/modules/cjs/helpers:102:18)

This may be solved by using require.resolve("jscodeshift/bin/jscodeshift") instead, as mentioned here. That PR may include other fixes required for Yarn PNP.

Same error when using npx create-remix@latest to get around this error and then running yarn dev.

@lensbart Thanks for flagging, we’ll take a look 👍

If some googlers are specifically looking for Yarn workspaces support, I’ve created a separate discussion: https://github.com/remix-run/remix/discussions/3668

Somewhat related: when running yarn dlx create-remix@latest instead of npx create-remix@latest, you get the following error:

Error: @remix-run/dev tried to access .bin, but it isn’t declared in its dependencies; this makes the require call ambiguous and unsound.

yarn dlx is the idiomatic way to run packages without installing when using Yarn, and is analogous to npx.

The same error appears when running yarn dev.

I can also confirm that the install does not work with Yarn 2+ Plug N Play setup. The issue is mostly around the postinstall command. Here is the first error triggered:

# This file contains the result of Yarn building a package (jokes@workspace:.)
# Script name: postinstall

@remix-run/dev tried to access @remix-run/server-runtime, but it isn't declared in its dependencies; this makes the require call ambiguous and unsound.

Required package: @remix-run/server-runtime (via "@remix-run/server-runtime/package.json")
Required by: @remix-run/dev@npm:1.0.6 (via /Users/reydev/repos/jokes/.yarn/cache/@remix-run-dev-npm-1.0.6-73f0b55172-2fca586246.zip/node_modules/@remix-run/dev/)

Require stack:
- /Users/reydev/repos/jokes/.yarn/cache/@remix-run-dev-npm-1.0.6-73f0b55172-2fca586246.zip/node_modules/@remix-run/dev/setup.js
- /Users/reydev/repos/jokes/.yarn/cache/@remix-run-dev-npm-1.0.6-73f0b55172-2fca586246.zip/node_modules/@remix-run/dev/cli/commands.js
- /Users/reydev/repos/jokes/.yarn/cache/@remix-run-dev-npm-1.0.6-73f0b55172-2fca586246.zip/node_modules/@remix-run/dev/cli.js

When I then installed @remix-run/server-runtime, this is the error I received:

# This file contains the result of Yarn building a package (jokes@workspace:.)
# Script name: postinstall

EROFS: read-only filesystem, open '/node_modules/remix/package.json'
  • The escape hatch of using nodeLinker technically works BUT I would advocate that this isn’t really adding support for Yarn 2+, as this disables Plug N Play, which by default is enabled on new projects bootstrapped for Yarn 2+ (yarn init -2) and is forcing a pretty big architectural decision on a team for their entire project, that actually has some concrete benefits: https://yarnpkg.com/features/pnp. I would prefer just saying "we don’t support Yarn 2+ than going the route that has us recommending us opt out of its biggest feature.

Thanks @rhefner - looks like it won’t be too complicated to make remix work with Yarn 2/3 👍

I am able to install on yarn v 3.0.2 but when running yarn build using remix@1.0.5 I get the following error:

Building Remix app in production mode...

 > error: [plugin: remix-mdx] Cannot find package 'xdm' imported from C:\Users\Max\repos\goldstack-mega\.yarn\cache\@remix-run-dev-npm-1.0.6-73f0b55172-2fca586246.zip\node_modules\@remix-run\dev\compiler\plugins\mdx.js
Did you mean to import xdm-npm-2.1.0-e825e62593-5a3f2434a5.zip/node_modules/xdm/index.js?


Build failed with 1 error:
error: Cannot find package 'xdm' imported from C:\Users\Max\repos\goldstack-mega\.yarn\cache\@remix-run-dev-npm-1.0.6-73f0b55172-2fca586246.zip\node_modules\@remix-run\dev\compiler\plugins\mdx.js
Did you mean to import xdm-npm-2.1.0-e825e62593-5a3f2434a5.zip/node_modules/xdm/index.js?
Built in 170ms

Assuming this may also be related to module resolution using yarn 2?

@ezracelli That’s basically what I did in #1316 (https://github.com/remix-run/remix/pull/1316/files#diff-ef8b0a35f03b499672160742c2d6b9688717e8d6b923dde0e10d2631ab0c09d6) I even declared the eslint pnp plugin as an optional dependency, so it doesn’t even get loaded if you don’t need it. Unfortunately the PR never went anywhere and is outdated again, but I’d happily rebase it if it gets PnP support to remix

Thank you, @MichaelDeBoey. yarn dlx create-remix@v0.0.0-nightly-34577c6-20220507 now runs without error in a Yarn 3 monorepository using PnP.

yarn dev still gives the following:

✘ [ERROR] Could not read from file: .yarn/cache/@remix-run-dev-npm-0.0.0-nightly-34577c6-20220507-bdbe7194e9-1f94a83917.zip/node_modules/@remix-run/dev/compiler/shims/react.ts

 ✘ [ERROR] Could not resolve "@remix-run/react"

    app/entry.client.tsx:1:29:
      1 │ import { RemixBrowser } from "@remix-run/react";
        ╵                              ~~~~~~~~~~~~~~~~~~

  You can mark the path "@remix-run/react" as external to exclude it from the bundle, which will remove this error.

 ✘ [ERROR] Could not resolve "react-dom"

    app/entry.client.tsx:2:24:
      2 │ import { hydrate } from "react-dom";
        ╵                         ~~~~~~~~~~~

  You can mark the path "react-dom" as external to exclude it from the bundle, which will remove this error.

 ✘ [ERROR] Could not resolve "@remix-run/react"

    app/root.tsx:9:7:
      9 │ } from "@remix-run/react";
        ╵        ~~~~~~~~~~~~~~~~~~

  You can mark the path "@remix-run/react" as external to exclude it from the bundle, which will remove this error.

Build failed with 4 errors:
error: Could not read from file: .yarn/cache/@remix-run-dev-npm-0.0.0-nightly-34577c6-20220507-bdbe7194e9-1f94a83917.zip/node_modules/@remix-run/dev/compiler/shims/react.ts
app/entry.client.tsx:1:29: ERROR: Could not resolve "@remix-run/react"
app/entry.client.tsx:2:24: ERROR: Could not resolve "react-dom"
app/root.tsx:9:7: ERROR: Could not resolve "@remix-run/react"
Error
    at Object.onBuildFailure (.yarn/cache/@remix-run-dev-npm-0.0.0-nightly-34577c6-20220507-bdbe7194e9-1f94a83917.zip/node_modules/@remix-run/dev/cli/commands.js:153:13)
    at buildEverything (.yarn/cache/@remix-run-dev-npm-0.0.0-nightly-34577c6-20220507-bdbe7194e9-1f94a83917.zip/node_modules/@remix-run/dev/compiler.js:280:13)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async Object.build (.yarn/cache/@remix-run-dev-npm-0.0.0-nightly-34577c6-20220507-bdbe7194e9-1f94a83917.zip/node_modules/@remix-run/dev/compiler.js:105:3)
    at async Object.build (.yarn/cache/@remix-run-dev-npm-0.0.0-nightly-34577c6-20220507-bdbe7194e9-1f94a83917.zip/node_modules/@remix-run/dev/cli/commands.js:148:3)
    at async Object.run (.yarn/cache/@remix-run-dev-npm-0.0.0-nightly-34577c6-20220507-bdbe7194e9-1f94a83917.zip/node_modules/@remix-run/dev/cli/run.js:468:7)

Not sure how I can go about fixing this issue. I tried adding @remix-run/react and react-dom to serverDependenciesToBundle in remix.config.js, to no avail. And it doesn’t seem like an error that can be fixed by configuring packageExtensions in .yarnrc.yml either. I also tried adding "dependenciesMeta": { "@remix-run/react": { "built": false }, "react-dom": { "built": false } } to the monorepo’s root package.json, but that didn’t help either.

Thoughts?

@lensbart Your problem with jscodeshift will be fixed by #3114

https://github.com/remix-run/remix/pull/2359 has been merged and included in 1.3.4 which at least solves the basic setup for me.

I was able to get the remix “basic” template working with 1.3.4 in a yarn 3 monorepo (using node_modules resolution) by:

  1. Removing "postinstall": "remix setup node", from package.json before doing any install
  2. Changing root.tsx imports to:
import {
  Links,
  LiveReload,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
} from "@remix-run/react";
import type { MetaFunction } from "@remix-run/node";
  1. Changing the entry.server.tsx imports to:
import { RemixServer } from "@remix-run/react";
import type { EntryContext } from "@remix-run/node";
  1. Changing entry.client.ts imports to:
import { RemixBrowser } from "@remix-run/react";