cypress-dark
cypress-dark copied to clipboard
Webpack 5 breaking change / Yarn 2 PnP module resolution
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
{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 :)
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) => { //
onis used to hook into various events Cypress emits //configis 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.cssBecause this error occurred during a
before allhook 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.
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.