Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
44 changes: 8 additions & 36 deletions packages/vite/src/node/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ import {
type EnvironmentResolveOptions,
type InternalResolveOptions,
type ResolveOptions,
tryNodeResolve,
} from './plugins/resolve'
import type { LogLevel, Logger } from './logger'
import { createLogger } from './logger'
Expand All @@ -102,7 +101,7 @@ import { loadEnv, resolveEnvPrefix } from './env'
import type { ResolvedSSROptions, SSROptions } from './ssr'
import { resolveSSROptions, ssrConfigDefaults } from './ssr'
import { PartialEnvironment } from './baseEnvironment'
import { createIdResolver } from './idResolver'
import { createIdResolver, createNodeResolverWithVite } from './idResolver'
import { runnerImport } from './ssr/runnerImport'
import { getAdditionalAllowedHosts } from './server/middlewares/hostCheck'
import type { RequiredExceptFor } from './typeUtils'
Expand Down Expand Up @@ -1921,12 +1920,14 @@ async function bundleConfigFile(
fileName: string,
isESM: boolean,
): Promise<{ code: string; dependencies: string[] }> {
const isModuleSyncConditionEnabled = (await import('#module-sync-enabled'))
.default

const dirnameVarName = '__vite_injected_original_dirname'
const filenameVarName = '__vite_injected_original_filename'
const importMetaUrlVarName = '__vite_injected_original_import_meta_url'

const nodeResolveWithVite = await createNodeResolverWithVite(
path.dirname(fileName),
)

const result = await build({
absWorkingDir: process.cwd(),
entryPoints: [fileName],
Expand All @@ -1952,35 +1953,6 @@ async function bundleConfigFile(
{
name: 'externalize-deps',
setup(build) {
const packageCache = new Map()
const resolveByViteResolver = (
id: string,
importer: string,
isRequire: boolean,
) => {
return tryNodeResolve(id, importer, {
root: path.dirname(fileName),
isBuild: true,
isProduction: true,
preferRelative: false,
tryIndex: true,
mainFields: [],
conditions: [
'node',
...(isModuleSyncConditionEnabled ? ['module-sync'] : []),
],
externalConditions: [],
external: [],
noExternal: [],
dedupe: [],
extensions: configDefaults.resolve.extensions,
preserveSymlinks: false,
packageCache,
isRequire,
builtins: nodeLikeBuiltins,
})?.id
}

// externalize bare imports
build.onResolve(
{ filter: /^[^.#].*/ },
Expand All @@ -2003,12 +1975,12 @@ async function bundleConfigFile(
const isImport = isESM || kind === 'dynamic-import'
let idFsPath: string | undefined
try {
idFsPath = resolveByViteResolver(id, importer, !isImport)
idFsPath = nodeResolveWithVite(id, importer, !isImport)
} catch (e) {
if (!isImport) {
let canResolveWithImport = false
try {
canResolveWithImport = !!resolveByViteResolver(
canResolveWithImport = !!nodeResolveWithVite(
id,
importer,
false,
Expand Down
43 changes: 41 additions & 2 deletions packages/vite/src/node/idResolver.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import type { PartialResolvedId } from 'rollup'
import aliasPlugin from '@rollup/plugin-alias'
import type { ResolvedConfig } from './config'
import { type ResolvedConfig, configDefaults } from './config'
import type { EnvironmentPluginContainer } from './server/pluginContainer'
import { createEnvironmentPluginContainer } from './server/pluginContainer'
import { resolvePlugin } from './plugins/resolve'
import { resolvePlugin, tryNodeResolve } from './plugins/resolve'
import type { InternalResolveOptions } from './plugins/resolve'
import type { Environment } from './environment'
import type { PartialEnvironment } from './baseEnvironment'
import { nodeLikeBuiltins } from './utils'

export type ResolveIdFn = (
environment: PartialEnvironment,
Expand Down Expand Up @@ -110,3 +111,41 @@ export function createIdResolver(
return resolved?.id
}
}

export type NodeResolveWithViteFn = (
id: string,
importer?: string,
isRequire?: boolean,
) => string | undefined

export async function createNodeResolverWithVite(
root: string,
): Promise<NodeResolveWithViteFn> {
const isModuleSyncConditionEnabled = (await import('#module-sync-enabled'))
.default

return (id, importer, isRequire) => {
return tryNodeResolve(id, importer, {
root,
isBuild: true,
isProduction: true,
preferRelative: false,
tryIndex: true,
mainFields: [],
conditions: [
'node',
...(isModuleSyncConditionEnabled ? ['module-sync'] : []),
],
externalConditions: [],
external: [],
noExternal: [],
dedupe: [],
extensions: configDefaults.resolve.extensions,
preserveSymlinks: false,
// Intentionally disable package cache for now as consumers don't need it
packageCache: undefined,
isRequire,
builtins: nodeLikeBuiltins,
})?.id
}
}
107 changes: 55 additions & 52 deletions packages/vite/src/node/plugins/css.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import fs from 'node:fs'
import fsp from 'node:fs/promises'
import path from 'node:path'
import { createRequire } from 'node:module'
import { fileURLToPath, pathToFileURL } from 'node:url'
import postcssrc from 'postcss-load-config'
import type {
Expand Down Expand Up @@ -79,15 +78,17 @@ import {
processSrcSet,
removeDirectQuery,
removeUrlQuery,
requireResolveFromRootWithFallback,
stripBomTag,
urlRE,
} from '../utils'
import type { Logger } from '../logger'
import { cleanUrl, isWindows, slash } from '../../shared/utils'
import { NULL_BYTE_PLACEHOLDER } from '../../shared/constants'
import { createBackCompatIdResolver } from '../idResolver'
import type { ResolveIdFn } from '../idResolver'
import {
createBackCompatIdResolver,
createNodeResolverWithVite,
} from '../idResolver'
import type { NodeResolveWithViteFn, ResolveIdFn } from '../idResolver'
import { PartialEnvironment } from '../baseEnvironment'
import type { TransformPluginContext } from '../server/pluginContainer'
import { searchForWorkspaceRoot } from '../server/searchRoot'
Expand Down Expand Up @@ -1562,7 +1563,7 @@ async function compilePostCSS(

const postcssOptions = postcssConfig?.options ?? {}
const postcssParser =
lang === 'sss' ? loadSss(config.root) : postcssOptions.parser
lang === 'sss' ? await loadSss(config.root) : postcssOptions.parser

if (!postcssPlugins.length && !postcssParser) {
return
Expand All @@ -1588,11 +1589,12 @@ async function transformSugarSS(
const { config } = environment
const { devSourcemap } = config.css

const sssParser = await loadSss(config.root)
const result = await runPostCSS(
id,
code,
[],
{ parser: loadSss(config.root) },
{ parser: sssParser },
undefined,
environment.logger,
devSourcemap,
Expand Down Expand Up @@ -2312,60 +2314,68 @@ export interface StylePreprocessorResults {
}

const loadedPreprocessorPath: Partial<
Record<PreprocessLang | PostCssDialectLang | 'sass-embedded', string>
Record<
PreprocessLang | PostCssDialectLang | 'sass-embedded',
string | Promise<string>
>
> = {}
let nodeResolveWithVite: NodeResolveWithViteFn | undefined

function loadPreprocessorPath(
async function loadPreprocessorPath(
lang: PreprocessLang | PostCssDialectLang | 'sass-embedded',
root: string,
): string {
): Promise<string> {
const cached = loadedPreprocessorPath[lang]
if (cached) {
return cached
}
try {
const resolved = requireResolveFromRootWithFallback(root, lang)
return (loadedPreprocessorPath[lang] = resolved)
} catch (e) {
if (e.code === 'MODULE_NOT_FOUND') {
const installCommand = getPackageManagerCommand('install')
throw new Error(
`Preprocessor dependency "${lang}" not found. Did you install it? Try \`${installCommand} -D ${lang}\`.`,
)
} else {
const message = new Error(
`Preprocessor dependency "${lang}" failed to load:\n${e.message}`,
)
message.stack = e.stack + '\n' + message.stack
throw message
}
}
loadedPreprocessorPath[lang] = (async () => {
nodeResolveWithVite ??= await createNodeResolverWithVite(root)
const resolved = nodeResolveWithVite(lang, path.join(root, '*'))
if (resolved) return resolved

const installCommand = getPackageManagerCommand('install')
throw new Error(
`Preprocessor dependency "${lang}" not found. Did you install it? Try \`${installCommand} -D ${lang}\`.`,
)
})()

loadedPreprocessorPath[lang]
// Set the result directly to avoid needing to resolve the promise again
.then((p) => (loadedPreprocessorPath[lang] = p))
// Set undefined so next retry work if the package is installed later
.catch(() => (loadedPreprocessorPath[lang] = undefined))

return loadedPreprocessorPath[lang]
}

function loadSassPackage(root: string): {
async function loadSassPackage(root: string): Promise<{
name: 'sass' | 'sass-embedded'
path: string
} {
}> {
// try sass-embedded before sass
try {
const path = loadPreprocessorPath('sass-embedded', root)
const path = await loadPreprocessorPath('sass-embedded', root)
return { name: 'sass-embedded', path }
} catch (e1) {
try {
const path = loadPreprocessorPath(PreprocessLang.sass, root)
const path = await loadPreprocessorPath(PreprocessLang.sass, root)
return { name: 'sass', path }
} catch {
throw e1
}
}
}

let cachedSss: PostCSS.Syntax
function loadSss(root: string): PostCSS.Syntax {
if (cachedSss) return cachedSss

const sssPath = loadPreprocessorPath(PostCssDialectLang.sss, root)
cachedSss = createRequire(/** #__KEEP__ */ import.meta.url)(sssPath)
let cachedSss: PostCSS.Syntax | Promise<PostCSS.Syntax>
async function loadSss(root: string): Promise<PostCSS.Syntax> {
if (!cachedSss) {
cachedSss = (async () => {
const sssPath = await loadPreprocessorPath(PostCssDialectLang.sss, root)
const resolved = (await import(pathToFileURL(sssPath).href)).default
return (cachedSss = resolved)
})()
}
return cachedSss
}

Expand Down Expand Up @@ -2415,10 +2425,7 @@ const makeScssWorker = (

const worker: WorkerType = {
async run(sassPath, data, options) {
// need pathToFileURL for windows since import("D:...") fails
// https://github.com/nodejs/node/issues/31710
const sass: typeof Sass = (await import(pathToFileURL(sassPath).href))
.default
const sass: typeof Sass = await import(sassPath)
compilerPromise ??= sass.initAsyncCompiler()
const compiler = await compilerPromise

Expand Down Expand Up @@ -2519,7 +2526,7 @@ const scssProcessor = (
worker?.stop()
},
async process(environment, source, root, options, resolvers) {
const sassPackage = loadSassPackage(root)
const sassPackage = await loadSassPackage(root)
worker ??= makeScssWorker(environment, resolvers, maxWorkers)

const { content: data, map: additionalMap } = await getSource(
Expand All @@ -2535,7 +2542,7 @@ const scssProcessor = (
}
try {
const result = await worker.run(
sassPackage.path,
pathToFileURL(sassPackage.path).href,
data,
optionsWithoutAdditionalData,
)
Expand Down Expand Up @@ -2796,9 +2803,7 @@ const lessProcessor = (
worker?.stop()
},
async process(environment, source, root, options, resolvers) {
const lessPath = pathToFileURL(
loadPreprocessorPath(PreprocessLang.less, root),
).href
const lessPath = await loadPreprocessorPath(PreprocessLang.less, root)
worker ??= makeLessWorker(environment, resolvers, maxWorkers)

const { content, map: additionalMap } = await getSource(
Expand All @@ -2815,7 +2820,7 @@ const lessProcessor = (
}
try {
result = await worker.run(
lessPath,
pathToFileURL(lessPath).href,
content,
optionsWithoutAdditionalData,
)
Expand Down Expand Up @@ -2864,9 +2869,9 @@ const makeStylWorker = (maxWorkers: number | undefined) => {
additionalData: undefined
},
) => {
const nodeStylus: typeof Stylus = (await import(stylusPath)).default
const stylus: typeof Stylus = (await import(stylusPath)).default

const ref = nodeStylus(content, {
const ref = stylus(content, {
// support @import from node dependencies by default
paths: ['node_modules'],
...options,
Expand Down Expand Up @@ -2917,9 +2922,7 @@ const stylProcessor = (
worker?.stop()
},
async process(_environment, source, root, options, _resolvers) {
const stylusPath = pathToFileURL(
loadPreprocessorPath(PreprocessLang.stylus, root),
).href
const stylusPath = await loadPreprocessorPath(PreprocessLang.stylus, root)
worker ??= makeStylWorker(maxWorkers)

// Get source with preprocessor options.additionalData. Make sure a new line separator
Expand All @@ -2942,7 +2945,7 @@ const stylProcessor = (
}
try {
const { code, map, deps } = await worker.run(
stylusPath,
pathToFileURL(stylusPath).href,
content,
root,
optionsWithoutAdditionalData,
Expand Down
Loading