From 02508649f1c9b73991e9a5e490969bf90ffced04 Mon Sep 17 00:00:00 2001
From: patak <583075+patak-dev@users.noreply.github.com>
Date: Wed, 4 Sep 2024 10:54:01 +0200
Subject: [PATCH] feat: global only alias (#18005)

---
 .../src/node/__tests__/environment.spec.ts    | 101 ------------------
 packages/vite/src/node/config.ts              |  83 ++++++++------
 packages/vite/src/node/plugins/resolve.ts     |  21 ++--
 3 files changed, 64 insertions(+), 141 deletions(-)
 delete mode 100644 packages/vite/src/node/__tests__/environment.spec.ts

diff --git a/packages/vite/src/node/__tests__/environment.spec.ts b/packages/vite/src/node/__tests__/environment.spec.ts
deleted file mode 100644
index cae672513be806..00000000000000
--- a/packages/vite/src/node/__tests__/environment.spec.ts
+++ /dev/null
@@ -1,101 +0,0 @@
-import { fileURLToPath } from 'node:url'
-import path from 'node:path'
-import { expect, test } from 'vitest'
-import { createServer } from '../server'
-import { createServerModuleRunner } from '../ssr/runtime/serverModuleRunner'
-import type { ResolveIdFn } from '../idResolver'
-import { createBackCompatIdResolver } from '../idResolver'
-import { normalizePath } from '../utils'
-
-const root = fileURLToPath(new URL('./', import.meta.url))
-
-async function createDevServer() {
-  const server = await createServer({
-    configFile: false,
-    root,
-    logLevel: 'silent',
-    plugins: [
-      (() => {
-        let idResolver: ResolveIdFn
-        return {
-          name: 'environment-alias-test-plugin',
-          configResolved(config) {
-            idResolver = createBackCompatIdResolver(config)
-          },
-          async resolveId(id) {
-            return await idResolver(this.environment, id)
-          },
-        }
-      })(),
-    ],
-    environments: {
-      client: {
-        resolve: {
-          alias: [
-            {
-              find: 'mod',
-              replacement: '/fixtures/environment-alias/test.client.js',
-            },
-          ],
-        },
-      },
-      ssr: {
-        resolve: {
-          alias: [
-            {
-              find: 'mod',
-              replacement: '/fixtures/environment-alias/test.ssr.js',
-            },
-          ],
-        },
-      },
-      rsc: {
-        resolve: {
-          alias: [
-            {
-              find: 'mod',
-              replacement: '/fixtures/environment-alias/test.rsc.js',
-            },
-          ],
-        },
-      },
-    },
-  })
-
-  const moduleRunner = createServerModuleRunner(server.environments.rsc)
-  return { server, moduleRunner }
-}
-
-test('alias', async () => {
-  expect.assertions(7)
-  const { server, moduleRunner } = await createDevServer()
-
-  const [clientId, ssrId, rscId, clientReq, ssrReq, rscReq, rscMod] =
-    await Promise.all([
-      server.environments.client.pluginContainer.resolveId('mod'),
-      server.environments.ssr.pluginContainer.resolveId('mod'),
-      server.environments.rsc.pluginContainer.resolveId('mod'),
-      server.environments.client.transformRequest('mod'),
-      server.environments.ssr.transformRequest('mod'),
-      server.environments.rsc.transformRequest('mod'),
-      moduleRunner.import('mod'),
-    ])
-
-  expect(clientId?.id).toEqual(
-    normalizePath(
-      path.join(root, '/fixtures/environment-alias/test.client.js'),
-    ),
-  )
-  expect(ssrId?.id).toEqual(
-    normalizePath(path.join(root, '/fixtures/environment-alias/test.ssr.js')),
-  )
-  expect(rscId?.id).toEqual(
-    normalizePath(path.join(root, '/fixtures/environment-alias/test.rsc.js')),
-  )
-
-  expect(clientReq?.code ?? '').toContain('(client)')
-  expect(ssrReq?.code ?? '').toContain('(ssr)')
-  expect(rscReq?.code ?? '').toContain('(rsc)')
-
-  expect(rscMod?.msg).toContain('(rsc)')
-})
diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts
index 382d3e701985bb..a96c6a179b1b01 100644
--- a/packages/vite/src/node/config.ts
+++ b/packages/vite/src/node/config.ts
@@ -29,7 +29,6 @@ import type {
 } from './plugin'
 import type {
   BuildEnvironmentOptions,
-  BuildOptions,
   BuilderOptions,
   RenderBuiltAssetUrl,
   ResolvedBuildEnvironmentOptions,
@@ -73,7 +72,11 @@ import {
   resolvePlugins,
 } from './plugins'
 import type { ESBuildOptions } from './plugins/esbuild'
-import type { InternalResolveOptions, ResolveOptions } from './plugins/resolve'
+import type {
+  EnvironmentResolveOptions,
+  InternalResolveOptions,
+  ResolveOptions,
+} from './plugins/resolve'
 import { tryNodeResolve } from './plugins/resolve'
 import type { LogLevel, Logger } from './logger'
 import { createLogger } from './logger'
@@ -223,10 +226,12 @@ function defaultCreateDevEnvironment(name: string, config: ResolvedConfig) {
 
 export type ResolvedDevEnvironmentOptions = Required<DevEnvironmentOptions>
 
-type EnvironmentResolveOptions = ResolveOptions & {
+type AllResolveOptions = ResolveOptions & {
   alias?: AliasOptions
 }
 
+type ResolvedAllResolveOptions = Required<ResolveOptions> & { alias: Alias[] }
+
 export interface SharedEnvironmentOptions {
   /**
    * Define global variable replacements.
@@ -260,12 +265,11 @@ export interface EnvironmentOptions extends SharedEnvironmentOptions {
   build?: BuildEnvironmentOptions
 }
 
-export type ResolvedEnvironmentResolveOptions =
-  Required<EnvironmentResolveOptions>
+export type ResolvedResolveOptions = Required<ResolveOptions>
 
 export type ResolvedEnvironmentOptions = {
   define?: Record<string, any>
-  resolve: ResolvedEnvironmentResolveOptions
+  resolve: ResolvedResolveOptions
   consumer: 'client' | 'server'
   webCompatible: boolean
   dev: ResolvedDevEnvironmentOptions
@@ -274,10 +278,9 @@ export type ResolvedEnvironmentOptions = {
 
 export type DefaultEnvironmentOptions = Omit<
   EnvironmentOptions,
-  'build' | 'consumer' | 'webCompatible'
+  'consumer' | 'webCompatible' | 'resolve'
 > & {
-  // Includes lib mode support
-  build?: BuildOptions
+  resolve?: AllResolveOptions
 }
 
 export interface UserConfig extends DefaultEnvironmentOptions {
@@ -623,12 +626,19 @@ export function resolveDevEnvironmentOptions(
 function resolveEnvironmentOptions(
   options: EnvironmentOptions,
   resolvedRoot: string,
+  alias: Alias[],
+  preserveSymlinks: boolean,
   logger: Logger,
   environmentName: string,
   // Backward compatibility
   skipSsrTransform?: boolean,
 ): ResolvedEnvironmentOptions {
-  const resolve = resolveEnvironmentResolveOptions(options.resolve, logger)
+  const resolve = resolveEnvironmentResolveOptions(
+    options.resolve,
+    alias,
+    preserveSymlinks,
+    logger,
+  )
   const isClientEnvironment = environmentName === 'client'
   const consumer =
     (options.consumer ?? isClientEnvironment) ? 'client' : 'server'
@@ -731,16 +741,17 @@ const clientAlias = [
   },
 ]
 
+/**
+ * alias and preserveSymlinks are not per-environment options, but they are
+ * included in the resolved environment options for convenience.
+ */
 function resolveEnvironmentResolveOptions(
   resolve: EnvironmentResolveOptions | undefined,
+  alias: Alias[],
+  preserveSymlinks: boolean,
   logger: Logger,
-): ResolvedConfig['resolve'] {
-  // resolve alias with internal client alias
-  const resolvedAlias = normalizeAlias(
-    mergeAlias(clientAlias, resolve?.alias || []),
-  )
-
-  const resolvedResolve: ResolvedConfig['resolve'] = {
+): ResolvedAllResolveOptions {
+  const resolvedResolve: ResolvedAllResolveOptions = {
     mainFields: resolve?.mainFields ?? DEFAULT_MAIN_FIELDS,
     conditions: resolve?.conditions ?? [],
     externalConditions: resolve?.externalConditions ?? [],
@@ -748,8 +759,8 @@ function resolveEnvironmentResolveOptions(
     noExternal: resolve?.noExternal ?? [],
     extensions: resolve?.extensions ?? DEFAULT_EXTENSIONS,
     dedupe: resolve?.dedupe ?? [],
-    preserveSymlinks: resolve?.preserveSymlinks ?? false,
-    alias: resolvedAlias,
+    preserveSymlinks,
+    alias,
   }
 
   if (
@@ -768,6 +779,22 @@ function resolveEnvironmentResolveOptions(
   return resolvedResolve
 }
 
+function resolveResolveOptions(
+  resolve: AllResolveOptions | undefined,
+  logger: Logger,
+): ResolvedAllResolveOptions {
+  // resolve alias with internal client alias
+  const alias = normalizeAlias(mergeAlias(clientAlias, resolve?.alias || []))
+  const preserveSymlinks = resolve?.preserveSymlinks ?? false
+  return resolveEnvironmentResolveOptions(
+    resolve,
+    alias,
+    preserveSymlinks,
+    logger,
+  )
+}
+
+// TODO: Introduce ResolvedDepOptimizationOptions
 function resolveDepOptimizationOptions(
   optimizeDeps: DepOptimizationOptions | undefined,
   preserveSymlinks: boolean,
@@ -968,22 +995,21 @@ export async function resolveConfig(
 
   await runConfigEnvironmentHook(config.environments, userPlugins, configEnv)
 
+  const resolvedDefaultResolve = resolveResolveOptions(config.resolve, logger)
+
   const resolvedEnvironments: Record<string, ResolvedEnvironmentOptions> = {}
   for (const environmentName of Object.keys(config.environments)) {
     resolvedEnvironments[environmentName] = resolveEnvironmentOptions(
       config.environments[environmentName],
       resolvedRoot,
+      resolvedDefaultResolve.alias,
+      resolvedDefaultResolve.preserveSymlinks,
       logger,
       environmentName,
       config.experimental?.skipSsrTransform,
     )
   }
 
-  const resolvedDefaultEnvironmentResolve = resolveEnvironmentResolveOptions(
-    config.resolve,
-    logger,
-  )
-
   // Backward compatibility: merge environments.client.dev.optimizeDeps back into optimizeDeps
   // The same object is assigned back for backward compatibility. The ecosystem is modifying
   // optimizeDeps in the ResolvedConfig hook, so these changes will be reflected on the
@@ -991,11 +1017,9 @@ export async function resolveConfig(
   const backwardCompatibleOptimizeDeps =
     resolvedEnvironments.client.dev.optimizeDeps
 
-  // TODO: Deprecate and remove resolve, dev and build options at the root level of the resolved config
-
   const resolvedDevEnvironmentOptions = resolveDevEnvironmentOptions(
     config.dev,
-    resolvedDefaultEnvironmentResolve.preserveSymlinks,
+    resolvedDefaultResolve.preserveSymlinks,
     // default environment options
     undefined,
     undefined,
@@ -1023,7 +1047,7 @@ export async function resolveConfig(
   }
   const ssr = resolveSSROptions(
     patchedConfigSsr,
-    resolvedDefaultEnvironmentResolve.preserveSymlinks,
+    resolvedDefaultResolve.preserveSymlinks,
   )
 
   // load .env files
@@ -1226,8 +1250,7 @@ export async function resolveConfig(
     optimizeDeps: backwardCompatibleOptimizeDeps,
     ssr,
 
-    // TODO: deprecate and later remove from ResolvedConfig?
-    resolve: resolvedDefaultEnvironmentResolve,
+    resolve: resolvedDefaultResolve,
     dev: resolvedDevEnvironmentOptions,
     build: resolvedBuildOptions,
 
diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts
index edf07f81dc377e..2abacbf9d2caea 100644
--- a/packages/vite/src/node/plugins/resolve.ts
+++ b/packages/vite/src/node/plugins/resolve.ts
@@ -75,7 +75,7 @@ const debug = createDebugger('vite:resolve-details', {
   onlyWhenFocused: true,
 })
 
-export interface ResolveOptions {
+export interface EnvironmentResolveOptions {
   /**
    * @default ['browser', 'module', 'jsnext:main', 'jsnext']
    */
@@ -87,10 +87,6 @@ export interface ResolveOptions {
    */
   extensions?: string[]
   dedupe?: string[]
-  /**
-   * @default false
-   */
-  preserveSymlinks?: boolean
   /**
    * external/noExternal logic, this only works for certain environments
    * Previously this was ssr.external/ssr.noExternal
@@ -100,6 +96,13 @@ export interface ResolveOptions {
   external?: string[] | true
 }
 
+export interface ResolveOptions extends EnvironmentResolveOptions {
+  /**
+   * @default false
+   */
+  preserveSymlinks?: boolean
+}
+
 interface ResolvePluginOptions {
   root: string
   isBuild: boolean
@@ -173,8 +176,6 @@ export interface InternalResolveOptions
 
 // Defined ResolveOptions are used to overwrite the values for all environments
 // It is used when creating custom resolvers (for CSS, scanning, etc)
-// TODO: It could be more clear to make the plugin constructor be:
-// resolvePlugin(pluginOptions: ResolvePluginOptions, overrideResolveOptions?: ResolveOptions)
 export interface ResolvePluginOptionsWithOverrides
   extends ResolveOptions,
     ResolvePluginOptions {}
@@ -183,9 +184,9 @@ export function resolvePlugin(
   resolveOptions: ResolvePluginOptionsWithOverrides,
   /**
    * @internal
-   * The deprecated config.createResolver creates a pluginContainer before
-   * environments are created. The resolve plugin is especial as it works without
-   * environments to enable this use case. It only needs access to the resolve options.
+   * config.createResolver creates a pluginContainer before environments are created.
+   * The resolve plugin is especial as it works without environments to enable this use case.
+   * It only needs access to the resolve options.
    */
   environmentsOptions?: Record<string, ResolvedEnvironmentOptions>,
 ): Plugin {