Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add next preset to webpack-dev-server-fresh #21069

Merged
merged 10 commits into from
Apr 19, 2022
74 changes: 74 additions & 0 deletions npm/webpack-dev-server-fresh/cypress/e2e/next.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// <reference types="cypress" />
/// <reference path="../support/e2e.ts" />
import type { ProjectFixtureDir } from '@tooling/system-tests/lib/fixtureDirs'

const WEBPACK_REACT: ProjectFixtureDir[] = ['next-11', 'next-12', 'next-11-webpack-4']

// Add to this list to focus on a particular permutation
const ONLY_PROJECTS: ProjectFixtureDir[] = []

for (const project of WEBPACK_REACT) {
if (ONLY_PROJECTS.length && !ONLY_PROJECTS.includes(project)) {
continue
}

describe(`Working with ${project}`, () => {
beforeEach(() => {
cy.scaffoldProject(project)
cy.openProject(project)
cy.startAppServer('component')
})

it('should mount a passing test', () => {
cy.visitApp()
cy.contains('index.cy.js').click()
cy.get('.passed > .num').should('contain', 1)
})

it('should live-reload on src changes', () => {
cy.visitApp()

cy.contains('index.cy.js').click()
cy.get('.passed > .num').should('contain', 1)

cy.withCtx(async (ctx) => {
const indexPath = ctx.path.join('pages', 'index.js')

await ctx.actions.file.writeFileInProject(
indexPath,
(await ctx.file.readFileInProject(indexPath)).replace('Welcome to', 'Hello from'),
)
})

cy.get('.failed > .num', { timeout: 10000 }).should('contain', 1)

cy.withCtx(async (ctx) => {
const indexTestPath = ctx.path.join('pages', 'index.cy.js')

await ctx.actions.file.writeFileInProject(
indexTestPath,
(await ctx.file.readFileInProject(indexTestPath)).replace('Welcome to', 'Hello from'),
)
})

cy.get('.passed > .num').should('contain', 1)
})

it('should detect new spec', () => {
cy.visitApp()

cy.withCtx(async (ctx) => {
const newTestPath = ctx.path.join('pages', 'New.cy.js')
const indexTestPath = ctx.path.join('pages', 'index.cy.js')

await ctx.actions.file.writeFileInProject(
newTestPath,
await ctx.file.readFileInProject(indexTestPath),
)
})

cy.contains('New.cy.js').click()
cy.get('.passed > .num').should('contain', 1)
})
})
}
54 changes: 29 additions & 25 deletions npm/webpack-dev-server-fresh/src/devServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import type WebpackDevServer from 'webpack-dev-server'
import type { Compiler, Configuration } from 'webpack'

import { createWebpackDevServer } from './createWebpackDevServer'
import { sourceRelativeWebpackModules } from './helpers/sourceRelativeWebpackModules'
import type { AddressInfo } from 'net'
import debugLib from 'debug'
import type { Server } from 'http'
import { vueCliHandler } from './helpers/vueCliHandler'
import { nuxtHandler } from './helpers/nuxtHandler'
import { createReactAppHandler } from './helpers/createReactAppHandler'
import { nextHandler } from './helpers/nextHandler'
import { sourceDefaultWebpackDependencies, SourceRelativeWebpackResult } from './helpers/sourceRelativeWebpackModules'

const debug = debugLib('cypress:webpack-dev-server-fresh:devServer')

Expand All @@ -23,7 +24,7 @@ export type WebpackDevServerConfig = {
webpackConfig?: unknown // Derived from the user's webpack
}

const ALL_FRAMEWORKS = ['create-react-app', 'nuxt', 'react', 'vue-cli'] as const
const ALL_FRAMEWORKS = ['create-react-app', 'nuxt', 'react', 'vue-cli', 'next', 'vue'] as const

/**
* @internal
Expand Down Expand Up @@ -111,36 +112,39 @@ export function devServer (devServerConfig: WebpackDevServerConfig): Promise<Cyp
})
}

export type PresetHandlerResult = { frameworkConfig?: Configuration, sourceWebpackModulesResult: SourceRelativeWebpackResult }

async function getPreset (devServerConfig: WebpackDevServerConfig): Promise<PresetHandlerResult> {
switch (devServerConfig.framework) {
case 'create-react-app':
return createReactAppHandler(devServerConfig)
case 'nuxt':
return await nuxtHandler(devServerConfig)

case 'vue-cli':
return vueCliHandler(devServerConfig)

case 'next':
return await nextHandler(devServerConfig)

case 'react':
case 'vue':
case undefined:
return { sourceWebpackModulesResult: sourceDefaultWebpackDependencies(devServerConfig) }

default:
throw new Error(`Unexpected framework ${devServerConfig.framework}, expected one of ${ALL_FRAMEWORKS.join(', ')}`)
}
}

/**
* Synchronously create the webpack server instance, without starting.
* Useful for testing
*
* @internal
*/
devServer.create = async function (devServerConfig: WebpackDevServerConfig) {
const sourceWebpackModulesResult = sourceRelativeWebpackModules(devServerConfig)

let frameworkConfig: Configuration | undefined

// If we have a framework specified, source the associated config
if (typeof devServerConfig.framework === 'string') {
switch (devServerConfig.framework) {
case 'create-react-app':
frameworkConfig = createReactAppHandler({ devServerConfig, sourceWebpackModulesResult })
break
case 'react':
break
case 'nuxt':
frameworkConfig = await nuxtHandler({ devServerConfig, sourceWebpackModulesResult })
break

case 'vue-cli':
frameworkConfig = vueCliHandler({ devServerConfig, sourceWebpackModulesResult })
break
default:
throw new Error(`Unexpected framework ${devServerConfig.framework}, expected one of ${ALL_FRAMEWORKS.join(', ')}`)
}
}
const { frameworkConfig, sourceWebpackModulesResult } = await getPreset(devServerConfig)

const { server, compiler } = createWebpackDevServer({
devServerConfig,
Expand Down
18 changes: 10 additions & 8 deletions npm/webpack-dev-server-fresh/src/helpers/createReactAppHandler.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
import type { CreateFinalWebpackConfig } from '../createWebpackDevServer'
import debugLib from 'debug'
import type { Configuration, ResolvePluginInstance, RuleSetRule } from 'webpack'
import path from 'path'

type PresetHandlerOptions = Omit<CreateFinalWebpackConfig, 'frameworkConfig'>
import type { PresetHandlerResult, WebpackDevServerConfig } from '../devServer'
import { sourceDefaultWebpackDependencies } from './sourceRelativeWebpackModules'

const debug = debugLib('cypress:webpack-dev-server-fresh:create-react-app')

/**
* Sourcing the config for create-react-app
*/
export function createReactAppHandler (presetHandler: PresetHandlerOptions) {
const { devServerConfig, sourceWebpackModulesResult } = presetHandler
export function createReactAppHandler (devServerConfig: WebpackDevServerConfig): PresetHandlerResult {
const sourceWebpackModulesResult = sourceDefaultWebpackDependencies(devServerConfig)

// this is required because
// 1) we use our own HMR and we don't need react-refresh transpiling overhead
// 2) it doesn't work with process.env=test @see https://github.com/cypress-io/cypress-realworld-app/pull/832
process.env.FAST_REFRESH = 'false'
const webpackConfig = loadWebpackConfig(presetHandler)
const webpackConfig = loadWebpackConfig(devServerConfig)

addCypressToWebpackEslintRulesInPlace(webpackConfig)

Expand All @@ -32,10 +31,13 @@ export function createReactAppHandler (presetHandler: PresetHandlerOptions) {
reactScriptsFiveModifications(webpackConfig)
}

return webpackConfig
return {
frameworkConfig: webpackConfig,
sourceWebpackModulesResult,
}
}

function loadWebpackConfig ({ devServerConfig }: PresetHandlerOptions): Configuration {
function loadWebpackConfig (devServerConfig: WebpackDevServerConfig): Configuration {
let webpackConfigPath: string

const envName = 'test'
Expand Down