Skip to content

Commit 1460824

Browse files
authored
fix: use esm entrypoint for css preprocessors and terser (#20918)
1 parent fc21af7 commit 1460824

File tree

5 files changed

+104
-123
lines changed

5 files changed

+104
-123
lines changed

packages/vite/src/node/config.ts

Lines changed: 10 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ import {
9090
type EnvironmentResolveOptions,
9191
type InternalResolveOptions,
9292
type ResolveOptions,
93-
tryNodeResolve,
9493
} from './plugins/resolve'
9594
import type { LogLevel, Logger } from './logger'
9695
import { createLogger } from './logger'
@@ -110,6 +109,7 @@ import {
110109
BasicMinimalPluginContext,
111110
basePluginContextMeta,
112111
} from './server/pluginContainer'
112+
import { nodeResolveWithVite } from './nodeResolve'
113113

114114
const debug = createDebugger('vite:config', { depth: 10 })
115115
const promisifiedRealpath = promisify(fs.realpath)
@@ -1921,12 +1921,11 @@ async function bundleConfigFile(
19211921
fileName: string,
19221922
isESM: boolean,
19231923
): Promise<{ code: string; dependencies: string[] }> {
1924-
const isModuleSyncConditionEnabled = (await import('#module-sync-enabled'))
1925-
.default
1926-
1924+
const root = path.dirname(fileName)
19271925
const dirnameVarName = '__vite_injected_original_dirname'
19281926
const filenameVarName = '__vite_injected_original_filename'
19291927
const importMetaUrlVarName = '__vite_injected_original_import_meta_url'
1928+
19301929
const result = await build({
19311930
absWorkingDir: process.cwd(),
19321931
entryPoints: [fileName],
@@ -1952,35 +1951,6 @@ async function bundleConfigFile(
19521951
{
19531952
name: 'externalize-deps',
19541953
setup(build) {
1955-
const packageCache = new Map()
1956-
const resolveByViteResolver = (
1957-
id: string,
1958-
importer: string,
1959-
isRequire: boolean,
1960-
) => {
1961-
return tryNodeResolve(id, importer, {
1962-
root: path.dirname(fileName),
1963-
isBuild: true,
1964-
isProduction: true,
1965-
preferRelative: false,
1966-
tryIndex: true,
1967-
mainFields: [],
1968-
conditions: [
1969-
'node',
1970-
...(isModuleSyncConditionEnabled ? ['module-sync'] : []),
1971-
],
1972-
externalConditions: [],
1973-
external: [],
1974-
noExternal: [],
1975-
dedupe: [],
1976-
extensions: configDefaults.resolve.extensions,
1977-
preserveSymlinks: false,
1978-
packageCache,
1979-
isRequire,
1980-
builtins: nodeLikeBuiltins,
1981-
})?.id
1982-
}
1983-
19841954
// externalize bare imports
19851955
build.onResolve(
19861956
{ filter: /^[^.#].*/ },
@@ -2003,16 +1973,17 @@ async function bundleConfigFile(
20031973
const isImport = isESM || kind === 'dynamic-import'
20041974
let idFsPath: string | undefined
20051975
try {
2006-
idFsPath = resolveByViteResolver(id, importer, !isImport)
1976+
idFsPath = nodeResolveWithVite(id, importer, {
1977+
root,
1978+
isRequire: !isImport,
1979+
})
20071980
} catch (e) {
20081981
if (!isImport) {
20091982
let canResolveWithImport = false
20101983
try {
2011-
canResolveWithImport = !!resolveByViteResolver(
2012-
id,
2013-
importer,
2014-
false,
2015-
)
1984+
canResolveWithImport = !!nodeResolveWithVite(id, importer, {
1985+
root,
1986+
})
20161987
} catch {}
20171988
if (canResolveWithImport) {
20181989
throw new Error(
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import isModuleSyncConditionEnabled from '#module-sync-enabled'
2+
import { configDefaults } from './config'
3+
import { tryNodeResolve } from './plugins/resolve'
4+
import { nodeLikeBuiltins } from './utils'
5+
6+
export interface NodeResolveWithViteOptions {
7+
root: string
8+
isRequire?: boolean
9+
}
10+
11+
/**
12+
* Resolve like Node.js using Vite's resolution algorithm with preconfigured options.
13+
*/
14+
export function nodeResolveWithVite(
15+
id: string,
16+
importer: string | undefined,
17+
options: NodeResolveWithViteOptions,
18+
): string | undefined {
19+
return tryNodeResolve(id, importer, {
20+
root: options.root,
21+
isBuild: true,
22+
isProduction: true,
23+
preferRelative: false,
24+
tryIndex: true,
25+
mainFields: [],
26+
conditions: [
27+
'node',
28+
...(isModuleSyncConditionEnabled ? ['module-sync'] : []),
29+
],
30+
externalConditions: [],
31+
external: [],
32+
noExternal: [],
33+
dedupe: [],
34+
extensions: configDefaults.resolve.extensions,
35+
preserveSymlinks: false,
36+
// Intentionally disable package cache for now as consumers don't need it
37+
packageCache: undefined,
38+
isRequire: options.isRequire,
39+
builtins: nodeLikeBuiltins,
40+
})?.id
41+
}

packages/vite/src/node/plugins/css.ts

Lines changed: 34 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import fs from 'node:fs'
22
import fsp from 'node:fs/promises'
33
import path from 'node:path'
4-
import { createRequire } from 'node:module'
54
import { fileURLToPath, pathToFileURL } from 'node:url'
65
import postcssrc from 'postcss-load-config'
76
import type {
@@ -57,6 +56,7 @@ import type { ResolvedConfig } from '../config'
5756
import type { Plugin } from '../plugin'
5857
import { checkPublicFile } from '../publicDir'
5958
import {
59+
_dirname,
6060
arraify,
6161
asyncReplace,
6262
combineSourcemaps,
@@ -79,7 +79,6 @@ import {
7979
processSrcSet,
8080
removeDirectQuery,
8181
removeUrlQuery,
82-
requireResolveFromRootWithFallback,
8382
stripBomTag,
8483
urlRE,
8584
} from '../utils'
@@ -94,6 +93,7 @@ import { searchForWorkspaceRoot } from '../server/searchRoot'
9493
import { type DevEnvironment } from '..'
9594
import type { PackageCache } from '../packages'
9695
import { findNearestMainPackageData } from '../packages'
96+
import { nodeResolveWithVite } from '../nodeResolve'
9797
import { addToHTMLProxyTransformResult } from './html'
9898
import {
9999
assetUrlRE,
@@ -1564,7 +1564,7 @@ async function compilePostCSS(
15641564

15651565
const postcssOptions = postcssConfig?.options ?? {}
15661566
const postcssParser =
1567-
lang === 'sss' ? loadSss(config.root) : postcssOptions.parser
1567+
lang === 'sss' ? await loadSss(config.root) : postcssOptions.parser
15681568

15691569
if (!postcssPlugins.length && !postcssParser) {
15701570
return
@@ -1590,11 +1590,12 @@ async function transformSugarSS(
15901590
const { config } = environment
15911591
const { devSourcemap } = config.css
15921592

1593+
const sssParser = await loadSss(config.root)
15931594
const result = await runPostCSS(
15941595
id,
15951596
code,
15961597
[],
1597-
{ parser: loadSss(config.root) },
1598+
{ parser: sssParser },
15981599
undefined,
15991600
environment.logger,
16001601
devSourcemap,
@@ -2325,23 +2326,18 @@ function loadPreprocessorPath(
23252326
if (cached) {
23262327
return cached
23272328
}
2328-
try {
2329-
const resolved = requireResolveFromRootWithFallback(root, lang)
2330-
return (loadedPreprocessorPath[lang] = resolved)
2331-
} catch (e) {
2332-
if (e.code === 'MODULE_NOT_FOUND') {
2333-
const installCommand = getPackageManagerCommand('install')
2334-
throw new Error(
2335-
`Preprocessor dependency "${lang}" not found. Did you install it? Try \`${installCommand} -D ${lang}\`.`,
2336-
)
2337-
} else {
2338-
const message = new Error(
2339-
`Preprocessor dependency "${lang}" failed to load:\n${e.message}`,
2340-
)
2341-
message.stack = e.stack + '\n' + message.stack
2342-
throw message
2343-
}
2344-
}
2329+
2330+
// Try resolve from project root first, then the current vite installation path
2331+
const resolved =
2332+
nodeResolveWithVite(lang, undefined, { root }) ??
2333+
nodeResolveWithVite(lang, _dirname, { root })
2334+
if (resolved) return (loadedPreprocessorPath[lang] = resolved)
2335+
2336+
// Error if we can't find the preprocessor
2337+
const installCommand = getPackageManagerCommand('install')
2338+
throw new Error(
2339+
`Preprocessor dependency "${lang}" not found. Did you install it? Try \`${installCommand} -D ${lang}\`.`,
2340+
)
23452341
}
23462342

23472343
function loadSassPackage(root: string): {
@@ -2362,12 +2358,15 @@ function loadSassPackage(root: string): {
23622358
}
23632359
}
23642360

2365-
let cachedSss: PostCSS.Syntax
2366-
function loadSss(root: string): PostCSS.Syntax {
2367-
if (cachedSss) return cachedSss
2368-
2369-
const sssPath = loadPreprocessorPath(PostCssDialectLang.sss, root)
2370-
cachedSss = createRequire(/** #__KEEP__ */ import.meta.url)(sssPath)
2361+
let cachedSss: PostCSS.Syntax | Promise<PostCSS.Syntax>
2362+
async function loadSss(root: string): Promise<PostCSS.Syntax> {
2363+
if (!cachedSss) {
2364+
cachedSss = (async () => {
2365+
const sssPath = loadPreprocessorPath(PostCssDialectLang.sss, root)
2366+
const resolved = (await import(pathToFileURL(sssPath).href)).default
2367+
return (cachedSss = resolved)
2368+
})()
2369+
}
23712370
return cachedSss
23722371
}
23732372

@@ -2417,10 +2416,7 @@ const makeScssWorker = (
24172416

24182417
const worker: WorkerType = {
24192418
async run(sassPath, data, options) {
2420-
// need pathToFileURL for windows since import("D:...") fails
2421-
// https://github.com/nodejs/node/issues/31710
2422-
const sass: typeof Sass = (await import(pathToFileURL(sassPath).href))
2423-
.default
2419+
const sass: typeof Sass = await import(sassPath)
24242420
compilerPromise ??= sass.initAsyncCompiler()
24252421
const compiler = await compilerPromise
24262422

@@ -2537,7 +2533,7 @@ const scssProcessor = (
25372533
}
25382534
try {
25392535
const result = await worker.run(
2540-
sassPackage.path,
2536+
pathToFileURL(sassPackage.path).href,
25412537
data,
25422538
optionsWithoutAdditionalData,
25432539
)
@@ -2798,9 +2794,7 @@ const lessProcessor = (
27982794
worker?.stop()
27992795
},
28002796
async process(environment, source, root, options, resolvers) {
2801-
const lessPath = pathToFileURL(
2802-
loadPreprocessorPath(PreprocessLang.less, root),
2803-
).href
2797+
const lessPath = loadPreprocessorPath(PreprocessLang.less, root)
28042798
worker ??= makeLessWorker(environment, resolvers, maxWorkers)
28052799

28062800
const { content, map: additionalMap } = await getSource(
@@ -2817,7 +2811,7 @@ const lessProcessor = (
28172811
}
28182812
try {
28192813
result = await worker.run(
2820-
lessPath,
2814+
pathToFileURL(lessPath).href,
28212815
content,
28222816
optionsWithoutAdditionalData,
28232817
)
@@ -2866,9 +2860,9 @@ const makeStylWorker = (maxWorkers: number | undefined) => {
28662860
additionalData: undefined
28672861
},
28682862
) => {
2869-
const nodeStylus: typeof Stylus = (await import(stylusPath)).default
2863+
const stylus: typeof Stylus = (await import(stylusPath)).default
28702864

2871-
const ref = nodeStylus(content, {
2865+
const ref = stylus(content, {
28722866
// support @import from node dependencies by default
28732867
paths: ['node_modules'],
28742868
...options,
@@ -2919,9 +2913,7 @@ const stylProcessor = (
29192913
worker?.stop()
29202914
},
29212915
async process(_environment, source, root, options, _resolvers) {
2922-
const stylusPath = pathToFileURL(
2923-
loadPreprocessorPath(PreprocessLang.stylus, root),
2924-
).href
2916+
const stylusPath = loadPreprocessorPath(PreprocessLang.stylus, root)
29252917
worker ??= makeStylWorker(maxWorkers)
29262918

29272919
// Get source with preprocessor options.additionalData. Make sure a new line separator
@@ -2944,7 +2936,7 @@ const stylProcessor = (
29442936
}
29452937
try {
29462938
const { code, map, deps } = await worker.run(
2947-
stylusPath,
2939+
pathToFileURL(stylusPath).href,
29482940
content,
29492941
root,
29502942
optionsWithoutAdditionalData,

packages/vite/src/node/plugins/terser.ts

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import type {
66
} from '#types/internal/terserOptions'
77
import type { Plugin } from '../plugin'
88
import type { ResolvedConfig } from '..'
9-
import { generateCodeFrame, requireResolveFromRootWithFallback } from '../utils'
9+
import { _dirname, generateCodeFrame } from '../utils'
10+
import { nodeResolveWithVite } from '../nodeResolve'
1011

1112
export interface TerserOptions extends TerserMinifyOptions {
1213
/**
@@ -19,22 +20,19 @@ export interface TerserOptions extends TerserMinifyOptions {
1920
}
2021

2122
let terserPath: string | undefined
22-
const loadTerserPath = (root: string) => {
23+
function loadTerserPath(root: string) {
2324
if (terserPath) return terserPath
24-
try {
25-
terserPath = requireResolveFromRootWithFallback(root, 'terser')
26-
} catch (e) {
27-
if (e.code === 'MODULE_NOT_FOUND') {
28-
throw new Error(
29-
'terser not found. Since Vite v3, terser has become an optional dependency. You need to install it.',
30-
)
31-
} else {
32-
const message = new Error(`terser failed to load:\n${e.message}`)
33-
message.stack = e.stack + '\n' + message.stack
34-
throw message
35-
}
36-
}
37-
return terserPath
25+
26+
// Try resolve from project root first, then the current vite installation path
27+
const resolved =
28+
nodeResolveWithVite('terser', undefined, { root }) ??
29+
nodeResolveWithVite('terser', _dirname, { root })
30+
if (resolved) return (terserPath = resolved)
31+
32+
// Error if we can't find the package
33+
throw new Error(
34+
'terser not found. Since Vite v3, terser has become an optional dependency. You need to install it.',
35+
)
3836
}
3937

4038
export function terserPlugin(config: ResolvedConfig): Plugin {
@@ -48,8 +46,7 @@ export function terserPlugin(config: ResolvedConfig): Plugin {
4846
code: string,
4947
options: TerserMinifyOptions,
5048
) => {
51-
const terser: typeof import('terser') = (await import(terserPath))
52-
.default
49+
const terser: typeof import('terser') = await import(terserPath)
5350
try {
5451
return (await terser.minify(code, options)) as TerserMinifyOutput
5552
} catch (e) {

0 commit comments

Comments
 (0)