-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New example request: @cypress/webpack-preprocessor
using exact same webpack config Cypress uses #25998
Description
Current behavior
It's hard to find the correct way to extend the webpack configuration which Cypress already uses (to keep the features that Cypress already has, such as TypeScript compilation).
It's even hard to find the Cypress webpack configuration, with multiple locations including fragments of webpack configuration:
cypress/npm/webpack-dev-server/src/makeDefaultWebpackConfig.ts
Lines 71 to 122 in e674f43
const finalConfig = { mode: 'development', optimization, output: { filename: '[name].js', path: OUTPUT_PATH, publicPath, }, plugins: [ new (HtmlWebpackPlugin as typeof import('html-webpack-plugin-5'))({ template: indexHtmlFile, // Angular generates all of it's scripts with <script type="module">. Live-reloading breaks without this option. // We need to manually set the base here to `/__cypress/src/` so that static assets load with our proxy ...(framework === 'angular' ? { scriptLoading: 'module', base: '/__cypress/src/' } : {}), }), new CypressCTWebpackPlugin({ files, projectRoot, devServerEvents, supportFile, webpack, indexHtmlFile, }), ], devtool: 'inline-source-map', } as any if (isRunMode) { // Disable file watching when executing tests in `run` mode finalConfig.watchOptions = { ignored: '**/*', } } if (webpackDevServerMajorVersion === 4) { return { ...finalConfig, devServer: { client: { overlay: false, }, }, } } // @ts-ignore return { ...finalConfig, devServer: { overlay: false, }, } cypress/packages/runner/webpack.config.ts
Lines 7 to 119 in e674f43
const commonConfig = getCommonConfig() const CopyWebpackPlugin = getCopyWebpackPlugin() // @ts-ignore const babelLoader = _.find(commonConfig.module.rules, (rule) => { // @ts-ignore return _.includes(rule.use.loader, 'babel-loader') }) // @ts-ignore babelLoader.use.options.plugins.push([require.resolve('babel-plugin-prismjs'), { 'languages': ['javascript', 'coffeescript', 'typescript', 'jsx', 'tsx'], 'plugins': ['line-numbers', 'line-highlight'], 'theme': 'default', 'css': false, }]) let pngRule // @ts-ignore const nonPngRules = _.filter(commonConfig.module.rules, (rule) => { // @ts-ignore if (rule.test.toString().includes('png')) { pngRule = rule return false } return true }) pngRule.use[0].options = { name: '[name].[ext]', outputPath: 'img', publicPath: '/__cypress/runner/img/', } // @ts-ignore const mainConfig: webpack.Configuration = { ...commonConfig, module: { rules: [ ...nonPngRules, pngRule, ], }, entry: { cypress_runner: [path.resolve(__dirname, 'src/index.js')], }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js', }, } // @ts-ignore mainConfig.plugins = [ // @ts-ignore ...mainConfig.plugins, new CopyWebpackPlugin([{ // @ts-ignore // There's a race condition in how these types are generated. from: cyIcons.getPathToFavicon('favicon.ico'), }]), ] mainConfig.resolve = { ...mainConfig.resolve, alias: { 'bluebird': require.resolve('bluebird'), 'lodash': require.resolve('lodash'), 'mobx': require.resolve('mobx'), 'mobx-react': require.resolve('mobx-react'), 'react': require.resolve('react'), 'react-dom': require.resolve('react-dom'), }, } // @ts-ignore const crossOriginConfig: webpack.Configuration = { ...commonConfig, entry: { cypress_cross_origin_runner: [path.resolve(__dirname, 'src/cross-origin.js')], }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js', }, } // @ts-ignore const mainInjectionConfig: webpack.Configuration = { ...getSimpleConfig(), mode: 'production', entry: { injection: [path.resolve(__dirname, 'injection/main.js')], }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js', }, } // @ts-ignore const crossOriginInjectionConfig: webpack.Configuration = { ...getSimpleConfig(), mode: 'production', entry: { injection_cross_origin: [path.resolve(__dirname, 'injection/cross-origin.js')], }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js', }, } cypress/packages/web-config/webpack.config.base.ts
Lines 113 to 286 in e674f43
export const getCommonConfig = () => { const commonConfig: Configuration = { mode: 'none', node: { fs: 'empty', child_process: 'empty', net: 'empty', tls: 'empty', module: 'empty', }, resolve: { extensions: ['.ts', '.js', '.jsx', '.tsx', '.scss', '.json'], }, stats, optimization, module: { rules: [ { test: /\.(ts|js|jsx|tsx)$/, exclude: /node_modules/, use: { loader: require.resolve('babel-loader'), options: { plugins: [ // "istanbul", [require.resolve('@babel/plugin-proposal-decorators'), { legacy: true }], [require.resolve('@babel/plugin-proposal-class-properties'), { loose: true }], ], presets: [ babelPresetEnvConfig, require.resolve('@babel/preset-react'), babelPresetTypeScriptConfig, ], babelrc: false, }, }, }, { test: /\.s?css$/, exclude: /node_modules/, use: [ { loader: MiniCSSExtractWebpackPlugin.loader }, ], }, makeSassLoaders({ modules: false }), makeSassLoaders({ modules: true }), { test: /\.(eot|svg|ttf|woff|woff2)$/, use: [ { loader: require.resolve('file-loader'), options: { name: './fonts/[name].[ext]', }, }, ], }, { test: /\.(png|gif)$/, use: [ { loader: require.resolve('file-loader'), options: { name: './img/[name].[ext]', }, }, ], }, { test: /\.wasm$/, type: 'javascript/auto', use: [ { loader: require.resolve('arraybuffer-loader'), }, ], }, ], }, plugins: [ new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }), new MiniCSSExtractWebpackPlugin(), // Enable source maps / eval maps // 'EvalDevtoolModulePlugin' is used in development // because it is fast and maps to filenames while showing compiled source // 'SourceMapDevToolPlugin' is used in production for the same reasons as 'eval', but it // shows full source and does not cause crossorigin errors like 'eval' (in Chromium < 63) // files will be mapped like: `cypress://../driver/cy/commands/click.coffee` // other sourcemap options: // [new webpack.SourceMapDevToolPlugin({ // moduleFilenameTemplate: 'cypress://[namespace]/[resource-path]', // fallbackModuleFilenameTemplate: 'cypress://[namespace]/[resourcePath]?[hash]' // })] : ...[ (env === 'production' ? new DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production') }) : evalDevToolPlugin ), ], ...(liveReloadEnabled ? [new LiveReloadPlugin({ appendScriptTag: 'true', port: 0, hostname: 'localhost', protocol: 'http' })] : []), ], cache: true, } return commonConfig } // eslint-disable-next-line @cypress/dev/arrow-body-multiline-braces export const getSimpleConfig = () => ({ node: { fs: 'empty', child_process: 'empty', net: 'empty', tls: 'empty', module: 'empty', }, resolve: { extensions: ['.js', '.ts', '.json'], }, stats, optimization, cache: true, module: { rules: [ { test: /\.(js|ts)$/, exclude: /node_modules/, use: { loader: require.resolve('babel-loader'), options: { plugins: [ [require.resolve('@babel/plugin-proposal-class-properties'), { loose: true }], ], presets: [ babelPresetEnvConfig, babelPresetTypeScriptConfig, ], babelrc: false, }, }, }, // FIXME: we don't actually want or need wasm support in the // cross origin bundle that uses this config, but we need to refactor // the driver so that it doesn't load the wasm code in // packages/driver/src/cypress/source_map_utils.js when creating // the cross origin bundle. for now, this is necessary so the build // doesn't fail // https://github.com/cypress-io/cypress/issues/19888 { test: /\.wasm$/, type: 'javascript/auto', use: [ { loader: require.resolve('arraybuffer-loader'), }, ], }, ], }, plugins: [ new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }), ], })
Desired behavior
It would be great to have a new example project with the exact same webpack configuration as is used inside Cypress for bundling user test code, similar to the 4 existing examples in the system-tests/projects
folder:
- webpack-preprocessor-awesome-typescript-loader
- webpack-preprocessor-ts-loader-compiler-options
- webpack-preprocessor-ts-loader
- webpack-preprocessor-webpack-5
This example project should have the cypress.config.js
file with the setupNodeEvents
method configured, similar to the other examples above.
Additionally, this example + documentation should be:
- easy to use
- copy + paste into Cypress config, not multiple steps
- documented (what are some common use cases of extending the config?)
- tested by the Cypress maintainers
- maintained by the Cypress maintainers
This way, it's easy to extend the existing webpack config used by Cypress by copying and pasting this example config and then adding your own configuration.
Test code to reproduce
The point of this issue is that it is difficult to find working code. There are many examples of outdated or non-working code throughout the issues in this repository and other locations.
Cypress Version
12.6.0
Node version
18.14.1
Operating System
macOS Ventura 13.2.1 (22D68)
Debug Logs
No response
Other
No response
Activity