Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions npm/vite-dev-server/src/plugins/cypress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ import type { ModuleNode, PluginOption, ViteDevServer } from 'vite-7'
import type { Vite } from '../getVite.js'
import { parse, HTMLElement } from 'node-html-parser'
import fs from 'fs'
import { promisify } from 'util'

import type { ViteDevServerConfig } from '../devServer.js'
import path from 'path'
import { fileURLToPath } from 'url'

const readFile = promisify(fs.readFile)

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

Expand All @@ -33,6 +36,7 @@ export const Cypress = (
vite: Vite,
): PluginOption => {
let base = '/'
let loaderPromise: Promise<string>

const projectRoot = options.cypressConfig.projectRoot
const supportFilePath = options.cypressConfig.supportFile ? path.resolve(projectRoot, options.cypressConfig.supportFile) : false
Expand All @@ -42,9 +46,21 @@ export const Cypress = (
const indexHtmlFile = options.cypressConfig.indexHtmlFile

let specsPathsSet = getSpecsPathsSet(specs)
// TODO: use async fs methods here
// eslint-disable-next-line no-restricted-syntax
let loader = fs.readFileSync(INIT_FILEPATH, 'utf8')

// Load the init file asynchronously with proper error handling
const loadInitFile = async (): Promise<string> => {
try {
const content = await readFile(INIT_FILEPATH, 'utf8')
debug(`Successfully loaded init file from ${INIT_FILEPATH}`)
return content
} catch (error) {
debug(`Failed to load init file from ${INIT_FILEPATH}:`, error)
throw new Error(`Failed to load Cypress init file: ${error instanceof Error ? error.message : 'Unknown error'}`)
}
}

// Initialize loader promise
loaderPromise = loadInitFile()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Unhandled Promise Rejection in Plugin Loader

Potential unhandled promise rejection. The loaderPromise is initialized immediately when the plugin is created, but if the file read fails and transformIndexHtml is never called (e.g., in certain Vite configurations or error scenarios), the rejected promise will never be awaited, causing an unhandled promise rejection warning in Node.js. The old synchronous code would fail immediately at plugin creation, providing clearer error feedback. Consider adding a .catch() handler to the promise initialization or deferring the file read until it's actually needed in transformIndexHtml.

Fix in Cursor Fix in Web

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lucgonp We have an AI bot that reviews our PRs - sometimes the feedback is helpful, sometimes irrelevant - if you can respond accordingly here please.


devServerEvents.on('dev-server:specs:changed', ({ specs, options }: { specs: Spec[], options?: { neededForJustInTimeCompile: boolean }}) => {
if (options?.neededForJustInTimeCompile) {
Expand Down Expand Up @@ -79,7 +95,7 @@ export const Cypress = (

debug('resolved the indexHtmlPath as', indexHtmlPath, 'from', indexHtmlFile)

let indexHtmlContent = await fs.promises.readFile(indexHtmlPath, { encoding: 'utf8' })
let indexHtmlContent = await readFile(indexHtmlPath, 'utf8')

// Inject the script tags
indexHtmlContent = indexHtmlContent.replace(
Expand All @@ -92,6 +108,9 @@ export const Cypress = (
// find </body> last index
const endOfBody = indexHtmlContent.lastIndexOf('</body>')

// Get the loader content asynchronously
const loader = await loaderPromise

// insert the script in the end of the body
const newHtml = `
${indexHtmlContent.substring(0, endOfBody)}
Expand Down