Skip to content

Question: Recommended approach for migration to @angular/build for developer API integrations (Cypress Component Testing)Β #31931

@AtofStryker

Description

@AtofStryker

Description

Hi Angular Team. I am aware this is more in the realm of a support request, but this request is somewhat bespoke and really requires a response from a core team member. I am exploring migrating Cypress Component Testing support from webpack via @angular-devkit/build-angular to the newer @angular/build using esbuild/vite and have several questions. I used GPT to organize my questions below. If there is a better medium to ask this question, please let us know!

Angular Build System Migration: Questions for Angular Core Team

Overview

Cypress Component Testing currently supports Angular Component Testing applications through @cypress/webpack-dev-server, which directly integrates with @angular-devkit/build-angular's webpack configuration. As Angular has moved to the new @angular/build system (which uses Vite with esbuild), we need guidance on how to migrate our integration to support the new build system while maintaining compatibility with existing Angular projects.

Current Implementation

How Cypress Uses @angular-devkit/build-angular

Cypress currently integrates with Angular by directly importing and using webpack configuration functions from @angular-devkit/build-angular. The implementation is located in npm/webpack-dev-server/src/helpers/angularHandler.ts.

Key Integration Points

  1. Direct Module Imports: We dynamically import Angular's webpack configuration utilities:

    const angularCLiModules = [
      '@angular-devkit/build-angular/src/utils/webpack-browser-config.js',
      '@angular-devkit/build-angular/src/tools/webpack/configs/common.js',
      '@angular-devkit/build-angular/src/tools/webpack/configs/styles.js',
      '@angular-devkit/core/src/index.js',
    ]
  2. Webpack Config Generation: We use generateBrowserWebpackConfigFromContext to generate a webpack configuration:

    const { config } = await generateBrowserWebpackConfigFromContext(
      buildOptions,
      context,
      (wco: any) => {
        const stylesConfig = Promise.resolve(getStylesConfig(wco)).then((config) => {
          // Custom modifications for Cypress
          return config
        })
        return [getCommonConfig(wco), stylesConfig]
      },
    )
  3. Fake Builder Context: We create a minimal context object that mimics Angular's builder context:

    const context = {
      target: { project: 'angular' },
      workspaceRoot: projectRoot,
      getProjectMetadata: () => ({ root, sourceRoot, projectType: 'application' }),
      logger,
    }
  4. Configuration Merging: The generated Angular webpack config is merged with Cypress-specific webpack configuration (entry points, plugins, etc.) and then used to launch webpack-dev-server.

What This Approach Provides

  • Automatic Configuration: We inherit all of Angular's webpack configuration (loaders, plugins, optimizations, etc.)
  • Compatibility: Works with Angular's build options from angular.json
  • Maintenance: As Angular updates its webpack config, we automatically benefit from those changes
  • Customization: Users can still override the webpack config via Cypress configuration

Desired State: Migration to @angular/build

Goal

We want to migrate to using @angular/build (which uses Vite with esbuild) so that we can use @cypress/vite-dev-server instead of @cypress/webpack-dev-server for Angular component testing. This would provide:

  • Performance: Vite's faster dev server and HMR
  • Consistency: Align with Angular's new build system direction
  • Modern Tooling: Benefit from esbuild's speed and Vite's ecosystem

Current @cypress/vite-dev-server Architecture

Our Vite dev server works by:

  1. Sourcing Vite from the user's project dependencies
  2. Resolving or accepting a Vite configuration
  3. Merging user config with Cypress-specific config (plugins, entry points, etc.)
  4. Creating and launching a Vite dev server

For reference, see npm/vite-dev-server/src/resolveConfig.ts.

Technical Challenges

Architecture Differences

  1. Build System Integration:

    • @angular-devkit/build-angular: Exposes webpack config generation functions that we can call directly
    • @angular/build: Uses Vite internally via serveWithVite() but doesn't expose a way to extract just the Vite configuration
  2. Builder Context Requirements:

    • @angular-devkit/build-angular: We can create a minimal fake context
    • @angular/build: The serveWithVite() function requires a full BuilderContext from @angular-devkit/architect. Are we able to use a similar approach?
  3. Configuration Extraction:

Key Questions

  1. Is there a supported way to extract the Vite configuration that @angular/build:dev-server would use, similar to how we can extract webpack config from @angular-devkit/build-angular? I'm not sure the current method is recommended, but having a way to extract the Vite config from a public interface would be extremely helpful.

  2. Can we reuse setupServer() or similar functions to generate a Vite InlineConfig that we can then merge with Cypress-specific configuration?

  3. Should we use the builder directly (e.g., call executeDevServerBuilder programmatically) and somehow integrate it with our dev server, or is there a better approach? This would be fairly challenging for us to accomplish reliably.

  4. What's the recommended migration path for tools like Cypress that need to integrate with Angular's build system for component testing? Should we even be using @angular/build instead of @angular-devkit/build-angular or is it recommended to use a different method?

  5. Are there any plans for a public API that would allow tools to programmatically get the Vite configuration that Angular would use, without having to run the full dev server builder?

Proposed Approach (Seeking Feedback)

We're considering a few potential approaches:

Option A: Extract Vite Config from Builder

  • Use @angular/build's internal functions (if accessible) to generate the Vite config
  • Merge with Cypress-specific config
  • Create our own Vite dev server instance. The Cypress app today currently needs control over the Vite process

Option B: Use Builder with Custom Integration

  • Programmatically invoke the Angular dev server builder while being able to pass specific configuration options
  • Intercept or proxy the Vite server it creates via a plugin approach, which would be fairly disruptive to our process
  • Integrate with Cypress's test execution flow

Questions for Angular Team

  1. What is the recommended approach for tools that need to integrate with Angular's build system for component testing when using @angular/build?

  2. Is there a public API or function we can use to get the Vite configuration that @angular/build would generate, without running the full builder?

  3. Would it be feasible to export functions like setupServer() or create a new public API for generating Vite configurations?

  4. Are there any examples of other tools successfully integrating with @angular/build for similar use cases?

  5. What is the timeline for @angular/build becoming the default/recommended build system, and when should tools like Cypress prioritize migration? Cypress is aware that @angular-devkit/build-angular is still supported according to the migration documentation, but know eventually webpack will be phased out for esbuild/Vite.

  6. Are there any compatibility concerns we should be aware of when trying to reuse Angular's Vite configuration in a different context (Cypress's dev server)?

Additional Context

  • Cypress Component Testing: Allows developers to test individual Angular components in isolation
  • Current Support: Works with Angular 18-20 (Angular 21 support coming soon) using webpack-based builds
  • User Impact: Many Cypress users rely on Angular component testing, so a smooth migration path is important
  • Repository: cypress-io/cypress

Next Steps

We're eager to collaborate with the Angular team to find the best migration path. We're open to help where possible!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions