Skip to content

Commit cd8d63b

Browse files
authored
fix: avoid optimizing non-optimizable external deps (#8860)
1 parent ef749ed commit cd8d63b

File tree

12 files changed

+78
-21
lines changed

12 files changed

+78
-21
lines changed

packages/vite/src/node/constants.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export const DEFAULT_CONFIG_FILES = [
4141

4242
export const JS_TYPES_RE = /\.(?:j|t)sx?$|\.mjs$/
4343

44-
export const OPTIMIZABLE_ENTRY_RE = /\.(?:m?js|ts)$/
44+
export const OPTIMIZABLE_ENTRY_RE = /\.(?:(m|c)?js|ts)$/
4545

4646
export const SPECIAL_QUERY_RE = /[\?&](?:worker|sharedworker|raw|url)\b/
4747

packages/vite/src/node/optimizer/index.ts

+12-5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
emptyDir,
1515
flattenId,
1616
getHash,
17+
isOptimizable,
1718
lookupFile,
1819
normalizeId,
1920
normalizePath,
@@ -644,20 +645,26 @@ export async function findKnownImports(
644645
async function addManuallyIncludedOptimizeDeps(
645646
deps: Record<string, string>,
646647
config: ResolvedConfig,
647-
extra?: string[],
648+
extra: string[] = [],
648649
filter?: (id: string) => boolean
649650
): Promise<void> {
650-
const include = [...(config.optimizeDeps?.include ?? []), ...(extra ?? [])]
651-
if (include) {
651+
const optimizeDepsInclude = config.optimizeDeps?.include ?? []
652+
if (optimizeDepsInclude.length || extra.length) {
652653
const resolve = config.createResolver({ asSrc: false, scan: true })
653-
for (const id of include) {
654+
for (const id of [...optimizeDepsInclude, ...extra]) {
654655
// normalize 'foo >bar` as 'foo > bar' to prevent same id being added
655656
// and for pretty printing
656657
const normalizedId = normalizeId(id)
657658
if (!deps[normalizedId] && filter?.(normalizedId) !== false) {
658659
const entry = await resolve(id)
659660
if (entry) {
660-
deps[normalizedId] = entry
661+
if (isOptimizable(entry, config.optimizeDeps)) {
662+
deps[normalizedId] = entry
663+
} else if (optimizeDepsInclude.includes(id)) {
664+
config.logger.warn(
665+
`Cannot optimize included dependency: ${colors.cyan(id)}`
666+
)
667+
}
661668
} else {
662669
throw new Error(
663670
`Failed to resolve force included dependency: ${colors.cyan(id)}`

packages/vite/src/node/optimizer/scan.ts

+8-12
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,14 @@ import type { Loader, OnLoadResult, Plugin } from 'esbuild'
66
import { build, transform } from 'esbuild'
77
import colors from 'picocolors'
88
import type { ResolvedConfig } from '..'
9-
import {
10-
JS_TYPES_RE,
11-
KNOWN_ASSET_TYPES,
12-
OPTIMIZABLE_ENTRY_RE,
13-
SPECIAL_QUERY_RE
14-
} from '../constants'
9+
import { JS_TYPES_RE, KNOWN_ASSET_TYPES, SPECIAL_QUERY_RE } from '../constants'
1510
import {
1611
cleanUrl,
1712
createDebugger,
1813
dataUrlRE,
1914
externalRE,
2015
isObject,
16+
isOptimizable,
2117
moduleListContains,
2218
multilineCommentsRE,
2319
normalizePath,
@@ -189,10 +185,6 @@ function esbuildScanPlugin(
189185
'@vite/env'
190186
]
191187

192-
const isOptimizable = (id: string) =>
193-
OPTIMIZABLE_ENTRY_RE.test(id) ||
194-
!!config.optimizeDeps.extensions?.some((ext) => id.endsWith(ext))
195-
196188
const externalUnlessEntry = ({ path }: { path: string }) => ({
197189
path,
198190
external: !entries.includes(path)
@@ -235,7 +227,11 @@ function esbuildScanPlugin(
235227
// It is possible for the scanner to scan html types in node_modules.
236228
// If we can optimize this html type, skip it so it's handled by the
237229
// bare import resolve, and recorded as optimization dep.
238-
if (resolved.includes('node_modules') && isOptimizable(resolved)) return
230+
if (
231+
resolved.includes('node_modules') &&
232+
isOptimizable(resolved, config.optimizeDeps)
233+
)
234+
return
239235
return {
240236
path: resolved,
241237
namespace: 'html'
@@ -382,7 +378,7 @@ function esbuildScanPlugin(
382378
}
383379
if (resolved.includes('node_modules') || include?.includes(id)) {
384380
// dependency or forced included, externalize and stop crawling
385-
if (isOptimizable(resolved)) {
381+
if (isOptimizable(resolved, config.optimizeDeps)) {
386382
depImports[id] = resolved
387383
}
388384
return externalUnlessEntry({ path: id })

packages/vite/src/node/utils.ts

+11
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
DEFAULT_EXTENSIONS,
2525
ENV_PUBLIC_PATH,
2626
FS_PREFIX,
27+
OPTIMIZABLE_ENTRY_RE,
2728
VALID_ID_PREFIX,
2829
wildcardHosts
2930
} from './constants'
@@ -91,6 +92,16 @@ export function moduleListContains(
9192
return moduleList?.some((m) => m === id || id.startsWith(m + '/'))
9293
}
9394

95+
export function isOptimizable(
96+
id: string,
97+
optimizeDepsConfig: ResolvedConfig['optimizeDeps']
98+
): boolean {
99+
return (
100+
OPTIMIZABLE_ENTRY_RE.test(id) ||
101+
(optimizeDepsConfig.extensions?.some((ext) => id.endsWith(ext)) ?? false)
102+
)
103+
}
104+
94105
export const bareImportRE = /^[\w@](?!.*:\/\/)/
95106
export const deepImportRE = /^([^@][^/]*)\/|^(@[^/]+\/[^/]+)\//
96107

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@font-face {
2+
font-family: 'Not Real Sans';
3+
src: url('./i-throw-if-you-optimize-this-file.woff') format('woff');
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "non-optimizable-include",
3+
"private": true,
4+
"type": "module",
5+
"version": "0.0.0",
6+
"exports": {
7+
".": "./index.css"
8+
}
9+
}

playground/optimize-deps/vite.config.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ module.exports = {
1616
},
1717

1818
optimizeDeps: {
19-
include: ['dep-linked-include', 'nested-exclude > nested-include'],
19+
include: [
20+
'dep-linked-include',
21+
'nested-exclude > nested-include',
22+
// will throw if optimized (should log warning instead)
23+
'non-optimizable-include'
24+
],
2025
exclude: ['nested-exclude'],
2126
esbuildOptions: {
2227
plugins: [
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@font-face {
2+
font-family: 'Not Real Sans';
3+
src: url('./i-throw-if-you-optimize-this-file.woff') format('woff');
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "no-external-css",
3+
"private": true,
4+
"type": "module",
5+
"version": "0.0.0",
6+
"exports": {
7+
".": "./index.css"
8+
}
9+
}

playground/ssr-deps/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
"read-file-content": "file:./read-file-content",
2020
"require-absolute": "file:./require-absolute",
2121
"ts-transpiled-exports": "file:./ts-transpiled-exports",
22-
"no-external-cjs": "file:./no-external-cjs"
22+
"no-external-cjs": "file:./no-external-cjs",
23+
"no-external-css": "file:./no-external-css"
2324
},
2425
"devDependencies": {
2526
"cross-env": "^7.0.3",

playground/ssr-deps/server.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export async function createServer(root = process.cwd(), hmrPort) {
3535
},
3636
appType: 'custom',
3737
ssr: {
38-
noExternal: ['no-external-cjs']
38+
noExternal: ['no-external-cjs', 'no-external-css']
3939
}
4040
})
4141
// use vite's connect instance as middleware

pnpm-lock.yaml

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)