From 814287d66fa516bc14bb89f748a4e3b78da0caeb Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Sat, 14 Feb 2026 02:44:05 -0800 Subject: [PATCH] fix: require path separators in isInNodeModules check The `isInNodeModules` utility used a loose `id.includes('node_modules')` check that would incorrectly match paths where "node_modules" appeared as a substring of a directory name (e.g., `/path/to/node_modules_bug/`). This caused several downstream issues, most notably: - `ensureVersionQuery` would inject version hashes into URLs for project files when the project root contained "node_modules" as a substring - This broke the html-proxy regex matching, causing inline script tags in HTML to fail with "invalid JS syntax" errors The fix requires proper path separators (`/` or `\`) on both sides of "node_modules", ensuring it's matched only as a real directory segment. Fixes #17467 --- .../vite/src/node/__tests__/utils.spec.ts | 36 +++++++++++++++++++ packages/vite/src/node/utils.ts | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/packages/vite/src/node/__tests__/utils.spec.ts b/packages/vite/src/node/__tests__/utils.spec.ts index e86074a37c91c8..00b623401af6ad 100644 --- a/packages/vite/src/node/__tests__/utils.spec.ts +++ b/packages/vite/src/node/__tests__/utils.spec.ts @@ -16,6 +16,7 @@ import { getServerUrlByHost, injectQuery, isFileReadable, + isInNodeModules, isParentDirectory, mergeWithDefaults, normalizePath, @@ -1058,3 +1059,38 @@ describe('resolveServerUrls', () => { expect(result.local).toContain('https://localhost:3000/') }) }) + +describe('isInNodeModules', () => { + test('should return true for paths containing /node_modules/ as a directory', () => { + expect(isInNodeModules('/path/to/node_modules/package/index.js')).toBe(true) + expect(isInNodeModules('/project/node_modules/.vite/dep.js')).toBe(true) + expect( + isInNodeModules('C:/Users/user/project/node_modules/foo/bar.js'), + ).toBe(true) + }) + + test('should return false for paths containing node_modules as substring of directory name', () => { + expect(isInNodeModules('/path/to/node_modules_bug/index.js')).toBe(false) + expect(isInNodeModules('/path/to/my_node_modules/index.js')).toBe(false) + expect(isInNodeModules('/path/to/node_modules_test/src/main.ts')).toBe( + false, + ) + }) + + test('should return true for backslash paths on Windows', () => { + expect( + isInNodeModules('C:\\Users\\user\\project\\node_modules\\foo\\bar.js'), + ).toBe(true) + }) + + test('should return false for backslash paths with node_modules as substring', () => { + expect(isInNodeModules('C:\\Users\\user\\node_modules_bug\\index.js')).toBe( + false, + ) + }) + + test('should return false when node_modules is not present', () => { + expect(isInNodeModules('/path/to/project/src/main.ts')).toBe(false) + expect(isInNodeModules('/path/to/project/index.html')).toBe(false) + }) +}) diff --git a/packages/vite/src/node/utils.ts b/packages/vite/src/node/utils.ts index e88e373b87c3bc..018e8294c34c1c 100644 --- a/packages/vite/src/node/utils.ts +++ b/packages/vite/src/node/utils.ts @@ -134,7 +134,7 @@ export function isNodeBuiltin(id: string): boolean { } export function isInNodeModules(id: string): boolean { - return id.includes('node_modules') + return id.includes('/node_modules/') || id.includes('\\node_modules\\') } export function moduleListContains(