From f63602175ee71d3e9f90fa1c7b2ce0af7d553f97 Mon Sep 17 00:00:00 2001 From: Dmitriy Kovalenko Date: Fri, 19 Feb 2021 14:35:34 +0200 Subject: [PATCH 1/6] Use lazy compilation for webpack-dev-server --- npm/react/cypress/plugins/index.js | 3 +- npm/webpack-dev-server/package.json | 1 + npm/webpack-dev-server/src/index.ts | 5 ++- .../src/makeWebpackConfig.ts | 45 +++++++++++++++---- npm/webpack-dev-server/src/startServer.ts | 3 +- npm/webpack-dev-server/src/webpack.config.js | 1 + yarn.lock | 10 ++++- 7 files changed, 54 insertions(+), 14 deletions(-) diff --git a/npm/react/cypress/plugins/index.js b/npm/react/cypress/plugins/index.js index 06bea18ed664..fb95e3de9b29 100644 --- a/npm/react/cypress/plugins/index.js +++ b/npm/react/cypress/plugins/index.js @@ -1,3 +1,4 @@ +// @ts-check const { startDevServer } = require('@cypress/webpack-dev-server') const babelConfig = require('../../babel.config.js') @@ -59,7 +60,7 @@ const webpackConfig = { * @type Cypress.PluginConfig */ module.exports = (on, config) => { - on('dev-server:start', (options) => startDevServer({ options, webpackConfig })) + on('dev-server:start', (options) => startDevServer({ options, webpackConfig, disableLazyCompilation: false })) return config } diff --git a/npm/webpack-dev-server/package.json b/npm/webpack-dev-server/package.json index 15cfed5da1c4..92c08d693980 100644 --- a/npm/webpack-dev-server/package.json +++ b/npm/webpack-dev-server/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "debug": "4.3.2", + "lazy-compile-webpack-plugin": "0.1.11", "semver": "^7.3.4", "webpack-merge": "^5.4.0" }, diff --git a/npm/webpack-dev-server/src/index.ts b/npm/webpack-dev-server/src/index.ts index 8a4a2f8773c7..8d7f875671da 100644 --- a/npm/webpack-dev-server/src/index.ts +++ b/npm/webpack-dev-server/src/index.ts @@ -2,12 +2,13 @@ import { EventEmitter } from 'events' import { debug as debugFn } from 'debug' import { AddressInfo } from 'net' import { start as createDevServer } from './startServer' +import { UserWebpackDevServerOptions } from './makeWebpackConfig' const debug = debugFn('cypress:webpack-dev-server:webpack') export interface DevServerOptions { specs: Cypress.Cypress['spec'][] config: { - supportFile: string + supportFile?: string projectRoot: string webpackDevServerPublicPathRoute: string } @@ -21,7 +22,7 @@ export interface ResolvedDevServerConfig { close: (done?: DoneCallback) => void } -export interface StartDevServer { +export interface StartDevServer extends UserWebpackDevServerOptions { /* this is the Cypress options object */ options: DevServerOptions /* support passing a path to the user's webpack config */ diff --git a/npm/webpack-dev-server/src/makeWebpackConfig.ts b/npm/webpack-dev-server/src/makeWebpackConfig.ts index 8cef9c6bafc4..f2b749f66c5b 100644 --- a/npm/webpack-dev-server/src/makeWebpackConfig.ts +++ b/npm/webpack-dev-server/src/makeWebpackConfig.ts @@ -1,28 +1,50 @@ import { debug as debugFn } from 'debug' import * as path from 'path' -import { Configuration } from 'webpack' +import * as webpack from 'webpack' import { merge } from 'webpack-merge' +import defaultWebpackConfig from './webpack.config' +import LazyCompilePlugin from 'lazy-compile-webpack-plugin' import CypressCTOptionsPlugin, { CypressCTOptionsPluginOptions } from './plugin' const debug = debugFn('cypress:webpack-dev-server:makeWebpackConfig') +const WEBPACK_MAJOR_VERSION = Number(webpack.version.split('.')[0]) + +export interface UserWebpackDevServerOptions { + /** + * if `true` will compile all the specs together when the first one is request and can slow up initial build time. + * @default false + */ + disableLazyCompilation?: boolean +} + +interface MakeWebpackConfigOptions extends CypressCTOptionsPluginOptions, UserWebpackDevServerOptions { + webpackDevServerPublicPathRoute: string +} const mergePublicPath = (baseValue, userValue = '/') => { return path.join(baseValue, userValue, '/') } -interface MakeWebpackConfigOptions extends CypressCTOptionsPluginOptions { - webpackDevServerPublicPathRoute: string +function getVersionRelatedWebpackConfig (version, options: MakeWebpackConfigOptions): webpack.Configuration { + if (options.disableLazyCompilation) { + return {} + } + + switch (version) { + case 4: + return { plugins: [new LazyCompilePlugin()] } + case 5: + return { experiments: { lazyCompilation: true } } as webpack.Configuration + default: + return { } + } } -export async function makeWebpackConfig (userWebpackConfig: Configuration, options: MakeWebpackConfigOptions): Promise { +export async function makeWebpackConfig (userWebpackConfig: webpack.Configuration, options: MakeWebpackConfigOptions): Promise { const { projectRoot, webpackDevServerPublicPathRoute, files, supportFile, devServerEvents } = options debug(`User passed in webpack config with values %o`, userWebpackConfig) - const defaultWebpackConfig = require('./webpack.config') - - debug(`Merging Evergreen's webpack config with users'`) - debug(`New webpack entries %o`, files) debug(`Project root`, projectRoot) debug(`Support file`, supportFile) @@ -45,7 +67,12 @@ export async function makeWebpackConfig (userWebpackConfig: Configuration, optio ], } - const mergedConfig = merge(userWebpackConfig, defaultWebpackConfig, dynamicWebpackConfig) + const mergedConfig = merge( + userWebpackConfig, + defaultWebpackConfig, + dynamicWebpackConfig, + getVersionRelatedWebpackConfig(WEBPACK_MAJOR_VERSION, options), + ) mergedConfig.entry = entry diff --git a/npm/webpack-dev-server/src/startServer.ts b/npm/webpack-dev-server/src/startServer.ts index 9863dac3c96e..b0a7995f62f0 100644 --- a/npm/webpack-dev-server/src/startServer.ts +++ b/npm/webpack-dev-server/src/startServer.ts @@ -6,7 +6,7 @@ import { makeWebpackConfig } from './makeWebpackConfig' const debug = Debug('cypress:webpack-dev-server:start') -export async function start ({ webpackConfig: userWebpackConfig, options }: StartDevServer): Promise { +export async function start ({ webpackConfig: userWebpackConfig, options, ...userOptions }: StartDevServer): Promise { if (!userWebpackConfig) { debug('User did not pass in any webpack configuration') } @@ -19,6 +19,7 @@ export async function start ({ webpackConfig: userWebpackConfig, options }: Star webpackDevServerPublicPathRoute, devServerEvents: options.devServerEvents, supportFile: options.config.supportFile, + ...userOptions, }) debug('compiling webpack') diff --git a/npm/webpack-dev-server/src/webpack.config.js b/npm/webpack-dev-server/src/webpack.config.js index 1f2eef5d09d2..968bec6c5311 100644 --- a/npm/webpack-dev-server/src/webpack.config.js +++ b/npm/webpack-dev-server/src/webpack.config.js @@ -1,6 +1,7 @@ const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') +/** @type {import('webpack').Configuration} */ module.exports = { mode: 'development', optimization: { diff --git a/yarn.lock b/yarn.lock index 28c5332d0349..a24e04c8632f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13421,7 +13421,7 @@ detect-node@^2.0.4: resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== -detect-port-alt@1.1.6: +detect-port-alt@1.1.6, detect-port-alt@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q== @@ -21463,6 +21463,14 @@ lazy-cache@^1.0.3: resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4= +lazy-compile-webpack-plugin@0.1.11: + version "0.1.11" + resolved "https://registry.yarnpkg.com/lazy-compile-webpack-plugin/-/lazy-compile-webpack-plugin-0.1.11.tgz#be3b9487ccc731a606dc55bcfcd80000c72e4237" + integrity sha512-d2D72x0XqFSj83SRmgx1dvgRvmyIoXpC2lMj+XVS+xzt0FxXDPzF/2FbxOVmW9gzp6d8U29Ne4RGF4x+MTYwow== + dependencies: + detect-port-alt "^1.1.6" + loader-utils "^1.2.3" + lazy-property@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lazy-property/-/lazy-property-1.0.0.tgz#84ddc4b370679ba8bd4cdcfa4c06b43d57111147" From b9405f69193a3e4dbb5cbe2c97870587f25161c9 Mon Sep 17 00:00:00 2001 From: Dmitriy Kovalenko Date: Fri, 19 Feb 2021 15:25:55 +0200 Subject: [PATCH 2/6] Do not lazy compile in run mode --- npm/webpack-dev-server/src/index.ts | 9 ++++----- npm/webpack-dev-server/src/makeWebpackConfig.ts | 9 +++++---- npm/webpack-dev-server/src/startServer.ts | 7 ++++--- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/npm/webpack-dev-server/src/index.ts b/npm/webpack-dev-server/src/index.ts index 8d7f875671da..28272a149350 100644 --- a/npm/webpack-dev-server/src/index.ts +++ b/npm/webpack-dev-server/src/index.ts @@ -6,13 +6,11 @@ import { UserWebpackDevServerOptions } from './makeWebpackConfig' const debug = debugFn('cypress:webpack-dev-server:webpack') export interface DevServerOptions { - specs: Cypress.Cypress['spec'][] - config: { - supportFile?: string - projectRoot: string + specs: Array + devServerEvents: EventEmitter + config: Cypress.ResolvedConfigOptions & Cypress.RuntimeConfigOptions & { webpackDevServerPublicPathRoute: string } - devServerEvents: EventEmitter } type DoneCallback = () => unknown @@ -23,6 +21,7 @@ export interface ResolvedDevServerConfig { } export interface StartDevServer extends UserWebpackDevServerOptions { + config: Cypress.RuntimeConfigOptions /* this is the Cypress options object */ options: DevServerOptions /* support passing a path to the user's webpack config */ diff --git a/npm/webpack-dev-server/src/makeWebpackConfig.ts b/npm/webpack-dev-server/src/makeWebpackConfig.ts index f2b749f66c5b..06efdfa3dd2e 100644 --- a/npm/webpack-dev-server/src/makeWebpackConfig.ts +++ b/npm/webpack-dev-server/src/makeWebpackConfig.ts @@ -19,18 +19,19 @@ export interface UserWebpackDevServerOptions { interface MakeWebpackConfigOptions extends CypressCTOptionsPluginOptions, UserWebpackDevServerOptions { webpackDevServerPublicPathRoute: string + isOpenMode: boolean } const mergePublicPath = (baseValue, userValue = '/') => { return path.join(baseValue, userValue, '/') } -function getVersionRelatedWebpackConfig (version, options: MakeWebpackConfigOptions): webpack.Configuration { - if (options.disableLazyCompilation) { +function getLazyCompilationWebpackConfig (options: MakeWebpackConfigOptions): webpack.Configuration { + if (options.disableLazyCompilation || !options.isOpenMode) { return {} } - switch (version) { + switch (WEBPACK_MAJOR_VERSION) { case 4: return { plugins: [new LazyCompilePlugin()] } case 5: @@ -71,7 +72,7 @@ export async function makeWebpackConfig (userWebpackConfig: webpack.Configuratio userWebpackConfig, defaultWebpackConfig, dynamicWebpackConfig, - getVersionRelatedWebpackConfig(WEBPACK_MAJOR_VERSION, options), + getLazyCompilationWebpackConfig(options), ) mergedConfig.entry = entry diff --git a/npm/webpack-dev-server/src/startServer.ts b/npm/webpack-dev-server/src/startServer.ts index b0a7995f62f0..01ea98772c20 100644 --- a/npm/webpack-dev-server/src/startServer.ts +++ b/npm/webpack-dev-server/src/startServer.ts @@ -6,19 +6,20 @@ import { makeWebpackConfig } from './makeWebpackConfig' const debug = Debug('cypress:webpack-dev-server:start') -export async function start ({ webpackConfig: userWebpackConfig, options, ...userOptions }: StartDevServer): Promise { +export async function start ({ webpackConfig: userWebpackConfig, options, config, ...userOptions }: StartDevServer): Promise { if (!userWebpackConfig) { debug('User did not pass in any webpack configuration') } - const { projectRoot, webpackDevServerPublicPathRoute } = options.config + const { projectRoot, webpackDevServerPublicPathRoute, isTextTerminal } = options.config const webpackConfig = await makeWebpackConfig(userWebpackConfig || {}, { files: options.specs, projectRoot, webpackDevServerPublicPathRoute, devServerEvents: options.devServerEvents, - supportFile: options.config.supportFile, + supportFile: options.config.supportFile as string, + isOpenMode: !isTextTerminal, ...userOptions, }) From b104c855f6a9c2115756249dde9206597339467d Mon Sep 17 00:00:00 2001 From: Dmitriy Kovalenko Date: Fri, 19 Feb 2021 15:50:55 +0200 Subject: [PATCH 3/6] Cache babel on CI --- .gitignore | 1 + circle.yml | 8 ++++++++ cli/types/cypress.d.ts | 6 +----- npm/react/cypress/plugins/index.js | 3 ++- npm/webpack-dev-server/src/index.ts | 13 ++----------- npm/webpack-dev-server/src/startServer.ts | 1 + 6 files changed, 15 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index c038b66451ca..fb4cf85ce7be 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ packages/server/test/support/fixtures/server/libs # from npm/react /npm/react/bin/* /npm/react/cypress/videos +/npm/react/.babel-cache # from runner-ct /packages/runner-ct/cypress/screenshots diff --git a/circle.yml b/circle.yml index 5ccc1a8a7724..8f7cdbdef0c3 100644 --- a/circle.yml +++ b/circle.yml @@ -1086,6 +1086,9 @@ jobs: - attach_workspace: at: ~/ - check-conditional-ci + - restore_cache: + name: Restore yarn cache + key: v{{ .Environment.CACHE_VERSION }}-{{ arch }}-npm-react-babel-cache - run: name: Build command: yarn workspace @cypress/react build @@ -1093,6 +1096,11 @@ jobs: name: Run tests command: yarn workspace @cypress/react test - store-npm-logs + - save_cache: + name: Save Cache + key: v{{ .Environment.CACHE_VERSION }}-{{ arch }}-npm-react-babel-cache + paths: + - cypress/npm/react/.babel-cache - persist_to_workspace: root: ~/ paths: cypress/npm/react diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 55cb1924ed9a..2af83a2e8e67 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -5134,11 +5134,7 @@ declare namespace Cypress { interface DevServerOptions { specs: Spec[] - config: { - supportFile?: string - projectRoot: string - webpackDevServerPublicPathRoute: string - }, + config: ResolvedConfigOptions & RuntimeConfigOptions, devServerEvents: NodeJS.EventEmitter, } diff --git a/npm/react/cypress/plugins/index.js b/npm/react/cypress/plugins/index.js index fb95e3de9b29..fb10532a5a13 100644 --- a/npm/react/cypress/plugins/index.js +++ b/npm/react/cypress/plugins/index.js @@ -1,5 +1,6 @@ // @ts-check const { startDevServer } = require('@cypress/webpack-dev-server') +const path = require('path') const babelConfig = require('../../babel.config.js') /** @type import("webpack").Configuration */ @@ -18,7 +19,7 @@ const webpackConfig = { { test: /\.(js|jsx|mjs|ts|tsx)$/, loader: 'babel-loader', - options: babelConfig, + options: { ...babelConfig, cacheDirectory: path.resolve(__dirname, '..', '..', '.babel-cache') }, }, { test: /\.modules\.css$/i, diff --git a/npm/webpack-dev-server/src/index.ts b/npm/webpack-dev-server/src/index.ts index 28272a149350..85069652e489 100644 --- a/npm/webpack-dev-server/src/index.ts +++ b/npm/webpack-dev-server/src/index.ts @@ -1,17 +1,9 @@ -import { EventEmitter } from 'events' import { debug as debugFn } from 'debug' import { AddressInfo } from 'net' import { start as createDevServer } from './startServer' import { UserWebpackDevServerOptions } from './makeWebpackConfig' -const debug = debugFn('cypress:webpack-dev-server:webpack') -export interface DevServerOptions { - specs: Array - devServerEvents: EventEmitter - config: Cypress.ResolvedConfigOptions & Cypress.RuntimeConfigOptions & { - webpackDevServerPublicPathRoute: string - } -} +const debug = debugFn('cypress:webpack-dev-server:webpack') type DoneCallback = () => unknown @@ -21,9 +13,8 @@ export interface ResolvedDevServerConfig { } export interface StartDevServer extends UserWebpackDevServerOptions { - config: Cypress.RuntimeConfigOptions /* this is the Cypress options object */ - options: DevServerOptions + options: Cypress.DevServerOptions /* support passing a path to the user's webpack config */ webpackConfig?: Record } diff --git a/npm/webpack-dev-server/src/startServer.ts b/npm/webpack-dev-server/src/startServer.ts index 01ea98772c20..9d3d93edad9f 100644 --- a/npm/webpack-dev-server/src/startServer.ts +++ b/npm/webpack-dev-server/src/startServer.ts @@ -11,6 +11,7 @@ export async function start ({ webpackConfig: userWebpackConfig, options, config debug('User did not pass in any webpack configuration') } + // @ts-expect-error ?? webpackDevServerPublicPathRoute is not a valid option of Cypress.Config const { projectRoot, webpackDevServerPublicPathRoute, isTextTerminal } = options.config const webpackConfig = await makeWebpackConfig(userWebpackConfig || {}, { From 3852a383bfb2c3dda4147c534e4cb96d06975c20 Mon Sep 17 00:00:00 2001 From: Dmitriy Kovalenko Date: Fri, 19 Feb 2021 15:57:39 +0200 Subject: [PATCH 4/6] Fix config --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 8f7cdbdef0c3..ca7e14bec0b6 100644 --- a/circle.yml +++ b/circle.yml @@ -1086,7 +1086,7 @@ jobs: - attach_workspace: at: ~/ - check-conditional-ci - - restore_cache: + - restore_cache: name: Restore yarn cache key: v{{ .Environment.CACHE_VERSION }}-{{ arch }}-npm-react-babel-cache - run: From 442fc14973a9cff605da837964ee3662cae323ae Mon Sep 17 00:00:00 2001 From: Dmitriy Kovalenko Date: Fri, 19 Feb 2021 16:04:04 +0200 Subject: [PATCH 5/6] Fix ts error --- npm/webpack-dev-server/src/startServer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/webpack-dev-server/src/startServer.ts b/npm/webpack-dev-server/src/startServer.ts index 9d3d93edad9f..dbab4f0f05b3 100644 --- a/npm/webpack-dev-server/src/startServer.ts +++ b/npm/webpack-dev-server/src/startServer.ts @@ -6,7 +6,7 @@ import { makeWebpackConfig } from './makeWebpackConfig' const debug = Debug('cypress:webpack-dev-server:start') -export async function start ({ webpackConfig: userWebpackConfig, options, config, ...userOptions }: StartDevServer): Promise { +export async function start ({ webpackConfig: userWebpackConfig, options, ...userOptions }: StartDevServer): Promise { if (!userWebpackConfig) { debug('User did not pass in any webpack configuration') } From 421b53e917044e72f1073126f872c8c026df8ad5 Mon Sep 17 00:00:00 2001 From: Dmitriy Kovalenko Date: Fri, 19 Feb 2021 18:17:18 +0200 Subject: [PATCH 6/6] Update webpack-dev-server unit tests --- npm/webpack-dev-server/test/e2e.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/npm/webpack-dev-server/test/e2e.spec.ts b/npm/webpack-dev-server/test/e2e.spec.ts index b74be273940e..7260b241566c 100644 --- a/npm/webpack-dev-server/test/e2e.spec.ts +++ b/npm/webpack-dev-server/test/e2e.spec.ts @@ -51,8 +51,9 @@ const specs: Cypress.Cypress['spec'][] = [ const config = { projectRoot: root, supportFile: '', + isTextTerminal: true, webpackDevServerPublicPathRoute: root, -} +} as any as Cypress.ResolvedConfigOptions & Cypress.RuntimeConfigOptions describe('#startDevServer', () => { it('serves specs via a webpack dev server', async () => {