cypress: Cannot consume ES module based cypress config when using Yarn 3

Current behavior

Does not work correctly with cypress.config.ts containing ES module syntax when using Yarn 3 (does not create node_modules). I get an error and cannot proceed.

I think the problem is we try to resolve your TS install from projectRoot but new versions of yarn install modules in some other place that isn’t in <project>/node_modules.

There’s a lot of information in this issue, and related ones - before working on this, spend a bit of time reading and understanding the issue. It looks like it is primarily related to Plug n Play feature.

Note: many users are excited for this fix - if you are working it, please build a pre-release binary and share it here, so users can test it out.

Test code to reproduce

https://github.com/lmiller1990/yarn-3-issue-ts

  • clone
  • make sure you get 3.x when you do yarn --version
  • install
  • open cypress CT

Cypress Version

10.3.0+

Other

Created from https://github.com/cypress-io/cypress/issues/22071#issuecomment-1180554866

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Reactions: 26
  • Comments: 63 (24 by maintainers)

Commits related to this issue

Most upvoted comments

I learned a lot about Yarn PnP recently and am knocking out issues, one by one. https://github.com/cypress-io/cypress/issues/25960 is closed, the ones https://github.com/cypress-io/cypress/issues/22747#issuecomment-1445788919 should be next.

This is the workaround I found to run it with yarn v3 #22699 (comment)

This seems to only apply to the webpack-dev-server. Some (including me) are using vite.

@ZachJW34 I went over this, thanks for the info – I think you might have made one mistake, let me clarify.

yarn pnp projects using cypress.config.js with type=“module” or cypress.config.mjs won’t work

I found Yarn PnP projects with cypress.config.mjs work just fine - regardless of the value of "module" in package.json.

I created three separate issues to make it easier to debug and fix this.

  1. Yarn 3 + cypress.config.ts does not work https://github.com/cypress-io/cypress/issues/25958
  2. Yarn 3 does not work with type=module and cypress.config.js https://github.com/cypress-io/cypress/issues/25959
  3. Dependencies are not detected during onboarding when using Yarn 3 #25960

I think (1) is the biggest main problem. type=module and cypress.config.js has an easy fix - change to cypress.config.mjs.

If you need this to work right now, here are the options:

1. cypress.config.ts does not work

  • Fix #1: cypress.config.js and CJS
  • Fix #2: Use cypress.config.mjs and ESM

Right now, it will not work with TypeScript.

Before

import { defineConfig } from 'cypress'

export default defineConfig({
  e2e: { /*...*/ }
});

After

const { defineConfig } = require("cypress");

module.exports = defineConfig({
   e2e: { /*...*/ }
});

If you are in VS Code, you can still get type hints via defineConfig.

2. Not working with type: module and cypress.config.js

Fix: Just rename cypress.config.js to cypress.config.mjs, and it should work.

Before

// cypress.config.js
import { defineConfig } from 'cypress'

export default defineConfig({
  e2e: {}
});

After

Just rename cypress.config.js to cypress.config.mjs, and it should work.

3. In Component Testing setup, modules are flagged as "not installed (like webpack, vite etc) even if they are.

Just skip/ignore warnings - it actually works. The UI is incorrect, we need to ensure we resolve yarn pnp modules correctly when checking.

@slikts

Again, the issue is not with anyone’s flexibility, but with Cypress not supporting a Yarn feature that people go through the trouble of adopting because it’s useful.

I am also hoping that the Cypress.io team will fix this and other issues relating to Yarn Plug’n’Play!

I found Yarn PnP projects with cypress.config.mjs work just fine - regardless of the value of “module” in package.json.

We use setupNodeEvents() in our cypress.config.ts, which used to be in the plugins file. Because of this, it’s difficult to convert the cypress.config.ts file to cypress.config.mjs file. We would have to provide the compiled .js files for everything, and also the package.json files so that Yarn 3 knows where to find all of the dependencies. Is there some kind of workaround for this situation? Perhaps having the plugins defined in a separate typescript file (as was done in the past)?

So we are still stuck.

We have exactly same problem. When you use yarn berry with nodeLinker: node-modules everything works, problem is when you change config to nodeLinker: pnp, install dependecies and run cypress tests. We use cypress with typescript.

I haven’t had a chance to look at this in more depth yet. I’m sorry. I don’t think I can look at this during Dec, but I would like to see this triaged and fixed in January. Let me try and bump this one up in priority.

I think I was misleading slightly - the title should not be ES module based but TS based cypress config. I will update it.

Basically, yarn 3 + TS isn’t working? I think it’s because we try to load your local TS from node_modules, which is not a thing in Yarn 3… digging into the various module problems now (eg https://github.com/cypress-io/cypress/issues/22795) so hopefully can look into this, too.

Probably need to do something around here, not sure what. I think what might be happening is this line is erroneously false, since it is attempting to resolve the user’s TypeScript version using the usual node module algorithm, which doesn’t work with Yarn 3.

What would be interesting is to see what happens in a Yarn 3 project without TypeScript - just using regular CJS. If that works, my suspicion is probably correct? cc @rockindahizzy

@slikts

That’s not a workaround, that’s just not using Yarn PnP at all.

Sorry if the suggestion was not helpful to you. According to other posts I have seen from @lmiller1990 , he has not been working for Cypress.io for several months now. I can’t see any feedback in the issue from anybody else in the Cypress.io organization, so I don’t know if there are plans to fix the Yarn Plug’n’Play support in Cypress.

If Yarn Classic projects are migrated to Yarn Modern they retain node-modules as nodeLinker parameter. Newly created Yarn Modern projects take pnp by default.

If you are using Yarn Modern and you are tied to using pnp by policy or for technical reasons, then I don’t know of a workaround. My suggestion was only for those who might have the flexibility to use node-modules.

@MikeMcC399 That’s not a workaround, that’s just not using Yarn PnP at all.

@elizww

  • If you are using nodeLinker: pnp mode like @vire you may have more success if you convert to Yarn Modern nodeLinker: node-modules (also used by Yarn Classic) as a workaround.

  • Cypress has multiple unresolved issues using Yarn Modern Plug’n’Play, especially for Component Testing, where it does not work at all. (See https://github.com/cypress-io/cypress-documentation/issues/5394.)

I actually think i got something working yesterday, will try and make a PR tomorrow, thanks for the advice anyway and gl with the new job!

Cypress@13 has just been released and this still seems to be an issue… I have tried ts and mjs extensions to no avail. Is there a chance that this will get revisited soon?

Edit:

I might have gotten a little ahead of myself here, as the following change allowed me to open cypress@13 without that “invalid config file” error.

image

I looked into this a bit and this is what I found (and reiterate what we’ve found so far in this discussion):

  • yarn pnp projects using cypress.config.ts won’t work. The Cypress parent process fails to detect Typescript in the project and won’t spawn the config child process with the ts-node experimental loaders that allow us to require(<project-root>/cypress.config.ts)

    • The child/config process can detect typescript just fine since the child process contains the necessary NODE_OPTIONS for yarn pnp: --require <project-root>/.pnp.cjs --experimental-loader file://<project-root>/.pnp.loader.mjs. I tested this by running DEBUG=cypress:lifecycle:child:run_require_async_child*,cypress:lifecycle:ProjectConfigIpc* yarn cypress open. You can edit the <cypress-cache-path>/12.6.0/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/child/run_require_async_child.js and throw a require.resolve('typescript', { paths: [projectRoot] }) to see the child process successfully finding typescript. The debug logs will show that the parent process doesn’t find typescript and therefore doesn’t tack on the ts-node loaders.
      • Should we run dep detection in a child process (for both Typescript detection and CT deps detection? Not sure why the parent process doesn’t have the pnp loaders set but if it did I think it would cause a lot of issues.
    • If we make changes to get the ts-loaders to detect properly, still not sure if it would work out of the box. There is a lengthy discussion about pnp + ts-node. Some code has been merged adding the ability to stack loaders to work with pnp but not sure what Node version contains this change.
  • yarn pnp projects using cypress.config.js with type="module" or cypress.config.mjs won’t work. The config process tries to require(<configPath>)) and if it fails will fall back to a dynamic import. In this case, the fallback to dynamic import never runs since the error produced from the failed require is different from the normal Node ES Module error due to it being first processed by the pnp loader. If we change the esm detection to

     const isNotESMError =
     	!err.stack.includes('[ERR_REQUIRE_ESM]')
      	&& !err.stack.includes('SyntaxError: Cannot use import statement outside a module')
      	&& !err.stack.includes('Error: require() of ES Module') // specific yarn pnp check
    

    it will work.

Takeaway from my findings is if we tweak the ESM error detection we can get yarn pnp to work for projects using type="module". Projects utilizing Typescript will require further work. A workaround for TS project could be to just use a javascript config file in the meantime.

Note: With the tweak mentioned above I was able to get a yarn pnp project to work with CT testing. The launchpad walkthrough is borked due to all of the dependency detection we do not working with yarn pnp, but the suggestion of running the dep detection in a child process could be the fix here.

I’ve never used yarn pnp so this is just a summary of what I found as a yarn pnp newb!

This is going to be worked on in our next block of work (between 28th Feb and 13th March). Assuming we solve the problem, we can ship the fix in the following release. There will definitely be pre-release binaries that can be tested, whoever picks up this issue will post here. Thanks for your patience!

I’m happy to try an experimental distribution if you want. And if you have questions about how to do it, we can correspond.

On Tue, Feb 21, 2023 at 3:35 PM Lachlan Miller @.***> wrote:

Gotcha. I will try and get this into our next sprint of work. I don’t know much about the scope of the problem, but it looks like there is a good chunk of information in this issue to help with debugging it.

— Reply to this email directly, view it on GitHub https://github.com/cypress-io/cypress/issues/22747#issuecomment-1437846785, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAHGWNM7CTTIW73Y3DRE3ILWYRAZDANCNFSM53JHH6TQ . You are receiving this because you were mentioned.Message ID: @.***>

CTO, Founder SnapStrat http://www.snapstrat.com | +1 510 432 1589

Just general Cypress usage w/ Yarn 3 zero installs.

Hello @francisu, good suggestion! I didn’t know that the process needed to be launched via yarn to make it “know” about the pnp config. I will try this trick with NODE_OPTIONS (hopefully this week).

I think we need to solve Yarn 3 PnP once and for all, this has dragged out for way too long. I will try and get this into our sprint work in the near future.

@lmiller1990 - this might help

This remains an issue in Cypress 12.3.0

I have this as well. I think the problem is in the code of ProjectConfigIpc in forkConfigProcess where it launches a plain node instance to do things. As this node instance was not launched via yarn, it does not get the required patching to resolve packages according to the Yarn 2/3 (PNP) rules. If the NODE_OPTIONS were set to require the .pnp-cjs file then it should work. I have not been able to test this because I don’t have time to figure out how to build a cypress distribution from source and try it.

I also wanted to try it by patching cypress as it was installed on my machine, but the cypress npm dist does not seem to contain this code.

I’m happy to help test any proposed solution. And if I can get help in patching the cypress dist as it’s installed (via yarn), then I can make the patch and try it.

@lmiller1990 Thanks for looking into this, I’m trying to switch a project over to Yarn PNP and cypress is my last hold up.

Yarn PNP uses a custom loader and virtual filesystem, so there is not a ‘node_modules’ to point to. The trick should be to make sure ts-node is using the yarn loader, which is usually accomplished when loading a node script using yarn command eg ‘yarn cypress run’.

But not working in this case because I suspect the cypress command is spinning off another process to run ts-node?

I’m happy to help play around with this if you have some suggestions. Also, I tried renaming my cypress.config.ts file to cypress.config.mts, which should be supported by the latest ts-node, but cypress doesn’t like the mts extension yet.