[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

refactor: refine `shouldExternalize` value + test: improve vite-ssr-resolve tests (#9112)

authored by

Hiroshi Ogawa and committed by
GitHub
(Nov 27, 2025, 1:53 PM +0100) ddf2d898 836a5be8

+72 -18
+24
pnpm-lock.yaml
··· 1226 1226 '@vitest/test-dep-conditions': 1227 1227 specifier: file:./deps/test-dep-conditions 1228 1228 version: file:test/config/deps/test-dep-conditions 1229 + inline-dep: 1230 + specifier: file:./deps/vite-ssr-resolve/inline-dep 1231 + version: file:test/config/deps/vite-ssr-resolve/inline-dep 1232 + other-dep: 1233 + specifier: file:./deps/vite-ssr-resolve/other-dep 1234 + version: file:test/config/deps/vite-ssr-resolve/other-dep 1235 + ssr-no-external-dep: 1236 + specifier: file:./deps/vite-ssr-resolve/ssr-no-external-dep 1237 + version: file:test/config/deps/vite-ssr-resolve/ssr-no-external-dep 1229 1238 tinyexec: 1230 1239 specifier: ^0.3.2 1231 1240 version: 0.3.2 ··· 6714 6723 ini@1.3.8: 6715 6724 resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} 6716 6725 6726 + inline-dep@file:test/config/deps/vite-ssr-resolve/inline-dep: 6727 + resolution: {directory: test/config/deps/vite-ssr-resolve/inline-dep, type: directory} 6728 + 6717 6729 internal-slot@1.0.7: 6718 6730 resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} 6719 6731 engines: {node: '>= 0.4'} ··· 7794 7806 resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} 7795 7807 engines: {node: '>= 0.8.0'} 7796 7808 7809 + other-dep@file:test/config/deps/vite-ssr-resolve/other-dep: 7810 + resolution: {directory: test/config/deps/vite-ssr-resolve/other-dep, type: directory} 7811 + 7797 7812 outvariant@1.4.3: 7798 7813 resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} 7799 7814 ··· 8714 8729 8715 8730 ssim.js@3.5.0: 8716 8731 resolution: {integrity: sha512-Aj6Jl2z6oDmgYFFbQqK7fght19bXdOxY7Tj03nF+03M9gCBAjeIiO8/PlEGMfKDwYpw4q6iBqVq2YuREorGg/g==} 8732 + 8733 + ssr-no-external-dep@file:test/config/deps/vite-ssr-resolve/ssr-no-external-dep: 8734 + resolution: {directory: test/config/deps/vite-ssr-resolve/ssr-no-external-dep, type: directory} 8717 8735 8718 8736 stackback@0.0.2: 8719 8737 resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} ··· 15252 15270 15253 15271 ini@1.3.8: {} 15254 15272 15273 + inline-dep@file:test/config/deps/vite-ssr-resolve/inline-dep: {} 15274 + 15255 15275 internal-slot@1.0.7: 15256 15276 dependencies: 15257 15277 es-errors: 1.3.0 ··· 16485 16505 prelude-ls: 1.2.1 16486 16506 type-check: 0.4.0 16487 16507 16508 + other-dep@file:test/config/deps/vite-ssr-resolve/other-dep: {} 16509 + 16488 16510 outvariant@1.4.3: {} 16489 16511 16490 16512 own-keys@1.0.1: ··· 17563 17585 sprintf-js@1.1.3: {} 17564 17586 17565 17587 ssim.js@3.5.0: {} 17588 + 17589 + ssr-no-external-dep@file:test/config/deps/vite-ssr-resolve/ssr-no-external-dep: {} 17566 17590 17567 17591 stackback@0.0.2: {} 17568 17592
+3
test/config/package.json
··· 11 11 "@vitest/browser-preview": "workspace:*", 12 12 "@vitest/browser-webdriverio": "workspace:*", 13 13 "@vitest/test-dep-conditions": "file:./deps/test-dep-conditions", 14 + "inline-dep": "file:./deps/vite-ssr-resolve/inline-dep", 15 + "other-dep": "file:./deps/vite-ssr-resolve/other-dep", 16 + "ssr-no-external-dep": "file:./deps/vite-ssr-resolve/ssr-no-external-dep", 14 17 "tinyexec": "^0.3.2", 15 18 "vite": "latest", 16 19 "vitest": "workspace:*"
+26 -12
test/config/test/vite-ssr-resolve.test.ts
··· 1 1 import type { CliOptions } from 'vitest/node' 2 + import { join } from 'pathe' 2 3 import { describe, expect, onTestFinished, test } from 'vitest' 3 4 import { createVitest } from 'vitest/node' 5 + 6 + const nodeModulesDir = join(import.meta.dirname, '../../node_modules') 4 7 5 8 describe.each(['deprecated', 'environment'] as const)('VitestResolver with Vite SSR config in %s style', (style) => { 6 9 test('merges vite ssr.resolve.noExternal with server.deps.inline', async () => { ··· 14 17 noExternal: ['ssr-no-external-dep'], 15 18 }) 16 19 20 + expect(resolver.options.inline).toEqual(['inline-dep', 'ssr-no-external-dep']) 21 + 17 22 // Both inline-dep and ssr-no-external-dep should be inlined (return false) 18 - expect(await resolver.shouldExternalize('/usr/a/project/node_modules/inline-dep/index.js')).toBe(false) 19 - expect(await resolver.shouldExternalize('/usr/a/project/node_modules/ssr-no-external-dep/index.js')).toBe(false) 23 + expect(await resolver.shouldExternalize(join(nodeModulesDir, 'inline-dep/index.js'))).toBe(false) 24 + expect(await resolver.shouldExternalize(join(nodeModulesDir, 'ssr-no-external-dep/index.js'))).toBe(false) 20 25 21 26 // Other deps should be externalized 22 - expect(await resolver.shouldExternalize('/usr/a/project/node_modules/other-dep/index.cjs.js')).toBeTruthy() 27 + expect(await resolver.shouldExternalize(join(nodeModulesDir, 'other-dep/index.js'))).toBeTruthy() 28 + 29 + // hard-coded pattern for external 30 + expect(await resolver.shouldExternalize('/usr/a/non-existing/node_modules/non-existing/index.cjs.js')).toBeTruthy() 31 + expect(await resolver.shouldExternalize('/usr/a/non-existing/node_modules/non-existing/index.mjs')).toBeTruthy() 32 + 33 + // non-existing files are inlined 34 + expect(await resolver.shouldExternalize('/usr/a/non-existing/node_modules/non-existing/index.js')).toBeUndefined() 23 35 }) 24 36 25 37 test('merges vite ssr.resolve.external with server.deps.external', async () => { ··· 33 45 external: ['ssr-external-dep'], 34 46 }) 35 47 48 + expect(resolver.options.external).toEqual(['external-dep', 'ssr-external-dep']) 49 + 36 50 // Both external-dep and ssr-external-dep should be externalized (return the ID) 37 - // Using .cjs.js extension which matches depsExternal pattern 38 - expect(await resolver.shouldExternalize('/usr/a/project/node_modules/external-dep/index.cjs.js')).toBeTruthy() 39 - expect(await resolver.shouldExternalize('/usr/a/project/node_modules/ssr-external-dep/index.cjs.js')).toBeTruthy() 51 + expect(await resolver.shouldExternalize('/usr/a/project/node_modules/external-dep/index.js')).toBeTruthy() 52 + expect(await resolver.shouldExternalize('/usr/a/project/node_modules/ssr-external-dep/index.js')).toBeTruthy() 40 53 }) 41 54 42 55 test('handles ssr.resolve.noExternal with wildcard patterns', async () => { ··· 154 167 const resolver = await getResolver(style, {}, {}) 155 168 156 169 // Files with Vite query parameters should be inlined (matched by defaultInline pattern) 157 - expect(await resolver.shouldExternalize('/usr/a/project/node_modules/some-lib/Component.vue?vue&type=script')).toBe(false) 170 + expect(await resolver.shouldExternalize('/usr/a/project/node_modules/some-lib/Component.vue?vue&type=script')).toBeUndefined() 158 171 expect(await resolver.shouldExternalize('/usr/a/project/node_modules/some-lib/data.txt?raw')).toBe(false) 159 172 expect(await resolver.shouldExternalize('/usr/a/project/node_modules/some-lib/image.png?url')).toBe(false) 160 173 expect(await resolver.shouldExternalize('/usr/a/project/node_modules/some-lib/style.css?inline')).toBe(false) ··· 185 198 // Note: depsExternal pattern only matches /node_modules/, not custom directories 186 199 // So .cjs.js files in custom directories won't be automatically externalized 187 200 // Regular .js files in custom directories are inlined by default 188 - expect(await resolver.shouldExternalize('/usr/a/project/custom_modules/other-dep/index.js')).toBe(false) 201 + expect(await resolver.shouldExternalize('/usr/a/project/custom_modules/other-dep/index.js')).toBeUndefined() 189 202 190 203 // But .cjs.js in node_modules IS externalized 191 204 expect(await resolver.shouldExternalize('/usr/a/project/node_modules/other-dep/index.cjs.js')).toBeTruthy() ··· 208 221 209 222 // Other packages: depsExternal pattern only matches /node_modules/ 210 223 // So regular .js files in /vendor/ are inlined by default 211 - expect(await resolver.shouldExternalize('/usr/a/project/vendor/other-lib/index.js')).toBe(false) 224 + expect(await resolver.shouldExternalize('/usr/a/project/vendor/other-lib/index.js')).toBeUndefined() 212 225 // But .cjs.js in node_modules IS externalized 213 226 expect(await resolver.shouldExternalize('/usr/a/project/node_modules/other-lib/index.cjs.js')).toBeTruthy() 214 227 }) ··· 231 244 232 245 // Other scopes: depsExternal pattern only matches /node_modules/ 233 246 // So regular .js files in /packages/ are inlined by default 234 - expect(await resolver.shouldExternalize('/usr/a/project/packages/@other/utils/index.js')).toBe(false) 247 + expect(await resolver.shouldExternalize('/usr/a/project/packages/@other/utils/index.js')).toBeUndefined() 235 248 // But .cjs.js in node_modules IS externalized 236 249 expect(await resolver.shouldExternalize('/usr/a/project/node_modules/@other/utils/index.cjs.js')).toBeTruthy() 237 250 }) ··· 254 267 // Multiple query parameters should still be inlined 255 268 expect(await resolver.shouldExternalize('/usr/a/project/node_modules/lib/file.js?url&used')).toBe(false) 256 269 expect(await resolver.shouldExternalize('/usr/a/project/node_modules/lib/style.css?inline&lang=scss')).toBe(false) 257 - expect(await resolver.shouldExternalize('/usr/a/project/node_modules/lib/Component.vue?vue&type=template&lang=pug')).toBe(false) 270 + expect(await resolver.shouldExternalize('/usr/a/project/node_modules/lib/Component.vue?vue&type=template&lang=pug')).toBeUndefined() 258 271 }) 259 272 }) 260 273 ··· 264 277 }) { 265 278 const ctx = await createVitest('test', { 266 279 watch: false, 267 - ...options, 268 280 }, style === 'environment' 269 281 ? { 270 282 environments: { ··· 272 284 resolve: externalOptions, 273 285 }, 274 286 }, 287 + test: options, 275 288 } 276 289 : { 277 290 ssr: externalOptions, 291 + test: options, 278 292 }) 279 293 onTestFinished(() => ctx.close()) 280 294 return ctx._resolver
+4 -6
packages/vitest/src/node/resolver.ts
··· 37 37 } 38 38 } 39 39 40 - public shouldExternalize(file: string): Promise<string | false> { 40 + public shouldExternalize(file: string): Promise<string | false | undefined> { 41 41 return shouldExternalize(normalizeId(file), this.options, this.externalizeCache) 42 42 } 43 43 } ··· 145 145 export async function shouldExternalize( 146 146 id: string, 147 147 options: ExternalizeOptions, 148 - cache: Map<string, Promise<string | false>>, 149 - ): Promise<string | false> { 148 + cache: Map<string, Promise<string | false | undefined>>, 149 + ): Promise<string | false | undefined> { 150 150 if (!cache.has(id)) { 151 151 cache.set(id, _shouldExternalize(id, options)) 152 152 } ··· 156 156 async function _shouldExternalize( 157 157 id: string, 158 158 options?: ExternalizeOptions, 159 - ): Promise<string | false> { 159 + ): Promise<string | false | undefined> { 160 160 if (isBuiltin(id)) { 161 161 return id 162 162 } ··· 200 200 if (isLibraryModule && (await isValidNodeImport(id))) { 201 201 return id 202 202 } 203 - 204 - return false 205 203 } 206 204 207 205 function matchPattern(
test/config/deps/vite-ssr-resolve/inline-dep/index.js
+5
test/config/deps/vite-ssr-resolve/inline-dep/package.json
··· 1 + { 2 + "name": "inline-dep", 3 + "type": "module", 4 + "exports": "./index.js" 5 + }
test/config/deps/vite-ssr-resolve/other-dep/index.js
+5
test/config/deps/vite-ssr-resolve/other-dep/package.json
··· 1 + { 2 + "name": "other-dep", 3 + "type": "module", 4 + "exports": "./index.js" 5 + }
test/config/deps/vite-ssr-resolve/ssr-no-external-dep/index.js
+5
test/config/deps/vite-ssr-resolve/ssr-no-external-dep/package.json
··· 1 + { 2 + "name": "ssr-no-external-dep", 3 + "type": "module", 4 + "exports": "./index.js" 5 + }