cypress-dark icon indicating copy to clipboard operation
cypress-dark copied to clipboard

Webpack 5 breaking change / Yarn 2 PnP module resolution

Open jcamden opened this issue 4 years ago • 2 comments

Hello, Bahmutov et al! Thank you for Cypress!

I've been using cypress-webpack-preprocessor-v5 (because I am using Yarn 2 PnP, which Webpack 5 plays nice with). After importing cypress-dark in support/index.ts, running a spec yielded the following error in the browser:

Error: Webpack CompLooked for and couldn't find the file at the following paths:resolve 'path' in '/home/john/Projects/current/djinndex-remastered/.yarn/cache/postcss-npm-7.0.16-2507f8c3e2-b867411523.zip/node_modules/postcss/lib'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:
	- add a fallback 'resolve.fallback: { "path": require.resolve("path-browserify") }'
	- install 'path-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
	resolve.fallback: { "path": false }

Accordingly, I added

resolve: {
        ...
        fallback: { path: require.resolve('path-browserify') },
      },

to webpackOptions in plugins/index.ts as follows:

/**
 * @type {Cypress.PluginConfig}
 */
//@ts-ignore
module.exports = (on, config) => {
  // `on` is used to hook into various events Cypress emits
  // `config` is the resolved Cypress config
  const options = {
    webpackOptions: {
      ...require('../../../client/webpack/webpack.cypress.js'),
      resolve: {
        plugins: [PnpWebpackPlugin],
        extensions: ['.ts', '.js'],
        fallback: { path: require.resolve('path-browserify') },
      },
      resolveLoader: {
        plugins: [PnpWebpackPlugin.moduleLoader(module)],
      },
      module: {
        rules: [
          {
            test: /\.ts$/,
            exclude: [/node_modules/],
            use: [
              {
                loader: 'ts-loader',
              },
            ],
          },
        ],
      },
    },
    watchOptions: {},
  };
  on('file:preprocessor', webpackPreprocessor(options));
};

Running a spec then yields the following error:

cypress_runner.js:199777 AssertionError: Timed out retrying: `cy.readFile("dark.css")` failed because the file does not exist at the following path:

`/home/[.../<workspace>/]packages/test/dark.css`

Because this error occurred during a `before all` hook we are skipping all of the remaining tests.
    at Context.eval (http://localhost:3000/__cypress/tests?p=cypress/support/index.ts:3280:8)

Webpack should be looking for dark.css in a Yarn 2 virtual pacakge from /.yarn/cache/cypress-dark-npm-1.7.14-295ea64c64-9b608eccac.zip, rather than in the root of the "test" package (of the workspace) from which I am running Cypress. So it seems like

{resolve: fallback: { path: require.resolve('path-browserify')} 

in webpackOptions falls back to something which doesn't know about PnPWebpackPlugin for resolutions. That's where I'm stuck. Any guidance would be much appreciated :)

jcamden avatar Dec 06 '20 15:12 jcamden

It’s hard for me to say what’s going on there since I don’t use yarn 2 or Webpack 5 myself

Sent from my iPhone

On Dec 6, 2020, at 10:31, John Camden [email protected] wrote:

 Hello, Bahmutov et al! Thank you for Cypress!

I've been using cypress-webpack-preprocessor-v5 (because I am using Yarn 2 PnP, which Webpack 5 plays nice with). After importing cypress-dark in support/index.ts, running a spec yielded the following error in the browser:

Error: Webpack CompLooked for and couldn't find the file at the following paths:resolve 'path' in '/home/john/Projects/current/djinndex-remastered/.yarn/cache/postcss-npm-7.0.16-2507f8c3e2-b867411523.zip/node_modules/postcss/lib'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default. This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:

  • add a fallback 'resolve.fallback: { "path": require.resolve("path-browserify") }'
  • install 'path-browserify' If you don't want to include a polyfill, you can use an empty module like this: resolve.fallback: { "path": false } Accordingly, I added

resolveLoader: { plugins: [PnpWebpackPlugin.moduleLoader(module)], }, to the my plugins/index.ts as follows:

/**

  • @type {Cypress.PluginConfig} */ //@ts-ignore module.exports = (on, config) => { // on is used to hook into various events Cypress emits // config is the resolved Cypress config const options = { webpackOptions: { ...require('../../../client/webpack/webpack.cypress.js'), resolve: { plugins: [PnpWebpackPlugin], extensions: ['.ts', '.js'], fallback: { path: require.resolve('path-browserify') }, }, resolveLoader: { plugins: [PnpWebpackPlugin.moduleLoader(module)], }, module: { rules: [ { test: /.ts$/, exclude: [/node_modules/], use: [ { loader: 'ts-loader', }, ], }, ], }, }, watchOptions: {}, }; on('file:preprocessor', webpackPreprocessor(options)); }; Running a spec then yields the following error:

cypress_runner.js:199777 AssertionError: Timed out retrying: cy.readFile("dark.css") failed because the file does not exist at the following path:

/home/[.../<workspace>/]packages/test/dark.css

Because this error occurred during a before all hook we are skipping all of the remaining tests. at Context.eval (http://localhost:3000/__cypress/tests?p=cypress/support/index.ts:3280:8) Webpack should be looking for dark.css in a Yarn 2 virtual pacakge from /.yarn/cache/cypress-dark-npm-1.7.14-295ea64c64-9b608eccac.zip, rather than in the root of the "test" package (of the workspace) from which I am running Cypress. So it seems like

{resolve: fallback: { path: require.resolve('path-browserify')} in webpackOptions falls back to something which doesn't know about PnPWebpackPlugin for resolutions. That's where I'm stuck. Any guidance would be much appreciated :)

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or unsubscribe.

bahmutov avatar Dec 06 '20 15:12 bahmutov

Turns out that the path polyfill for Webpack 5 is working fine with Yarn 2. The issue is that cypress-dark specifically seeks a node_modules directory in util.js at line 12: join('node_modules/cypress-dark/src') So, that's good news.

For other Yarn 2 users, in order to avoid the whole node_modules thing, I ended up just recreating a simplified version of the cypress-dark plugin locally in cypress/support/theme/index.ts:

import postcss from 'postcss';
 //@ts-ignore
 import cssVariables from 'postcss-css-variables';
 
 const getHead = () => Cypress.$(parent.window.document.head);
 const $head = getHead();
 const isStyleLoaded = ($head: JQuery<HTMLHeadElement>) =>
   $head.find('#cypress-dark').length > 0;
 
 const convertCssVariables = (mycss: any) =>
   postcss([cssVariables()]).process(mycss).css;
 
 export const loadTheme = (theme: string) => {
   return () => {
     const $head = getHead();
     if (isStyleLoaded($head)) {
       return;
     }
 
     const themeFilename = `${theme}.css`;
 
     cy.readFile(themeFilename, { log: false })
       .then(convertCssVariables)
       .then((css) => {
         $head.append(
           `<style type="text/css" id="cypress-dark" theme="${theme}">\n${css}</style>`
         );
       });
   };
 };

 export const stubMediaQuery = () => () => {
   Cypress.on('window:before:load', (win) => {
     cy.stub(win, 'matchMedia')
       .withArgs('(prefers-color-scheme: dark)')
       .returns({
         matches: true,
       });
   });
 };

I copied dark.css to cypress/support/theme (and made some personal preference tweaks). And then I called loadTheme("dark") in cypress/support/index.ts:

   import { loadTheme, stubMediaQuery } from './theme';
   before(stubMediaQuery());
   before(loadTheme('dark'));

Thanks again, @bahmutov, for the example of cypress-dark.

jcamden avatar Dec 07 '20 16:12 jcamden