Skip to content

Commit

Permalink
feat: decouple DevEnvironment and server (#16460)
Browse files Browse the repository at this point in the history
  • Loading branch information
patak-dev authored Apr 19, 2024
1 parent b4fdfaf commit 319b8e0
Show file tree
Hide file tree
Showing 36 changed files with 1,154 additions and 722 deletions.
2 changes: 0 additions & 2 deletions packages/vite/src/node/__tests__/external.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { fileURLToPath } from 'node:url'
import { describe, expect, test } from 'vitest'
import type { SSROptions } from '../ssr'
import { resolveConfig } from '../config'
import { createIsConfiguredAsExternal } from '../external'
import { Environment } from '../environment'
Expand All @@ -27,6 +26,5 @@ async function createIsExternal(external?: true) {
'serve',
)
const environment = new Environment('ssr', resolvedConfig)
console.log(environment.options)
return createIsConfiguredAsExternal(environment)
}
25 changes: 11 additions & 14 deletions packages/vite/src/node/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ import {
import type {
EnvironmentOptions,
InlineConfig,
PluginOption,
ResolvedConfig,
ResolvedEnvironmentOptions,
} from './config'
import type { PluginOption } from './plugin'
import { getDefaultResolvedEnvironmentOptions, resolveConfig } from './config'
import { buildReporterPlugin } from './plugins/reporter'
import { buildEsbuildPlugin } from './plugins/esbuild'
Expand All @@ -55,7 +55,7 @@ import type { Logger } from './logger'
import { dataURIPlugin } from './plugins/dataUri'
import { buildImportAnalysisPlugin } from './plugins/importAnalysisBuild'
import { ssrManifestPlugin } from './ssr/ssrManifestPlugin'
import { loadFallbackPlugin } from './plugins/loadFallback'
import { buildLoadFallbackPlugin } from './plugins/loadFallback'
import { findNearestPackageData } from './packages'
import type { PackageCache } from './packages'
import { resolveChokidarOptions } from './watch'
Expand Down Expand Up @@ -256,8 +256,8 @@ export interface BuildEnvironmentOptions {
* create the Build Environment instance
*/
createEnvironment?: (
builder: ViteBuilder,
name: string,
config: ResolvedConfig,
) => Promise<BuildEnvironment> | BuildEnvironment
}

Expand Down Expand Up @@ -498,7 +498,7 @@ export async function resolveBuildPlugins(config: ResolvedConfig): Promise<{
buildReporterPlugin(config),
]
: []),
loadFallbackPlugin(),
buildLoadFallbackPlugin(),
],
}
}
Expand Down Expand Up @@ -1375,26 +1375,24 @@ function areSeparateFolders(a: string, b: string) {

export class BuildEnvironment extends Environment {
mode = 'build' as const
builder: ViteBuilder

constructor(
builder: ViteBuilder,
name: string,
config: ResolvedConfig,
setup?: {
options?: EnvironmentOptions
},
) {
// TODO: move this to the base Environment class?
let options =
builder.config.environments[name] ??
getDefaultResolvedEnvironmentOptions(builder.config)
config.environments[name] ?? getDefaultResolvedEnvironmentOptions(config)
if (setup?.options) {
options = mergeConfig(
options,
setup?.options,
) as ResolvedEnvironmentOptions
}
super(name, builder.config, options)
this.builder = builder
super(name, config, options)
}
}

Expand Down Expand Up @@ -1492,18 +1490,17 @@ export async function createViteBuilder(
const environmentOptions = defaultConfig.environments[name]
const createEnvironment =
environmentOptions.build?.createEnvironment ??
((builder: ViteBuilder, name: string) =>
new BuildEnvironment(builder, name))
((name: string, config: ResolvedConfig) =>
new BuildEnvironment(name, config))

// We need to resolve the config again so we can properly merge options
// and get a new set of plugins for each build environment. The ecosystem
// expects plugins to be run for the same environment once they are created
// and to process a single bundle at a time (contrary to dev mode where
// plugins are built to handle multiple environments concurrently).
const environmentConfig = await resolveConfig(environmentOptions)
const environmentBuilder = { ...builder, config: environmentConfig }

const environment = await createEnvironment(environmentBuilder, name)
const environment = await createEnvironment(name, environmentConfig)
environments[name] = environment
}

Expand Down
5 changes: 3 additions & 2 deletions packages/vite/src/node/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import type { ServerOptions } from './server'
import type { CLIShortcut } from './shortcuts'
import type { LogLevel } from './logger'
import { createLogger } from './logger'
import { resolveConfig } from './config'
import { Environment } from './environment'

const cli = cac('vite')

Expand Down Expand Up @@ -345,6 +343,8 @@ cli
)
.action(
async (root: string, options: { force?: boolean } & GlobalCLIOptions) => {
/* TODO: do we need this command?
filterDuplicateOptions(options)
const { optimizeDeps } = await import('./optimizer')
try {
Expand All @@ -367,6 +367,7 @@ cli
)
process.exit(1)
}
*/
},
)

Expand Down
131 changes: 83 additions & 48 deletions packages/vite/src/node/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ import {
ENV_ENTRY,
FS_PREFIX,
} from './constants'
import type { HookHandler, Plugin, PluginWithRequiredHook } from './plugin'
import type {
HookHandler,
Plugin,
PluginEnvironment,
PluginOption,
PluginWithRequiredHook,
} from './plugin'
import type {
BuildEnvironmentOptions,
BuildOptions,
Expand All @@ -35,12 +41,9 @@ import {
resolveBuildOptions,
resolveBuilderOptions,
} from './build'
import type {
ResolvedServerOptions,
ServerOptions,
ViteDevServer,
} from './server'
import type { ResolvedServerOptions, ServerOptions } from './server'
import { resolveServerOptions } from './server'
import { Environment } from './environment'
import type { DevEnvironment } from './server/environment'
import type { PreviewOptions, ResolvedPreviewOptions } from './preview'
import { resolvePreviewOptions } from './preview'
Expand Down Expand Up @@ -78,8 +81,8 @@ import type { LogLevel, Logger } from './logger'
import { createLogger } from './logger'
import type { DepOptimizationConfig, DepOptimizationOptions } from './optimizer'
import type { JsonOptions } from './plugins/json'
import type { PluginContainer } from './server/pluginContainer'
import { createPluginContainer } from './server/pluginContainer'
import type { BoundedPluginContainer } from './server/pluginContainer'
import { createBoundedPluginContainer } from './server/pluginContainer'
import type { PackageCache } from './packages'
import { findNearestPackageData } from './packages'
import { loadEnv, resolveEnvPrefix } from './env'
Expand Down Expand Up @@ -133,22 +136,14 @@ export function defineConfig(config: UserConfigExport): UserConfigExport {
return config
}

export type PluginOption =
| Plugin
| false
| null
| undefined
| PluginOption[]
| Promise<Plugin | false | null | undefined | PluginOption[]>

export interface DevEnvironmentOptions {
/**
* Files tßo be pre-transformed. Supports glob patterns.
*/
warmup?: string[]
/**ß
/**
* Pre-transform known direct imports
* @default true
* defaults to true for the client environment, false for the rest
*/
preTransformRequests?: boolean
/**
Expand Down Expand Up @@ -178,8 +173,8 @@ export interface DevEnvironmentOptions {
* create the Dev Environment instance
*/
createEnvironment?: (
server: ViteDevServer,
name: string,
config: ResolvedConfig,
) => Promise<DevEnvironment> | DevEnvironment

/**
Expand All @@ -190,6 +185,13 @@ export interface DevEnvironmentOptions {
*/
recoverable?: boolean

/**
* For environments associated with a module runner.
* By default it is true for the client environment and false for non-client environments.
* This option can also be used instead of the removed config.experimental.skipSsrTransform.
*/
moduleRunnerTransform?: boolean

/**
* Defaults to true for the client environment and false for others, following node permissive
* security model.
Expand All @@ -208,8 +210,8 @@ export type ResolvedDevEnvironmentOptions = Required<
// TODO: Should we set the default at config time? For now, it is defined on server init
createEnvironment:
| ((
server: ViteDevServer,
name: string,
config: ResolvedConfig,
) => Promise<DevEnvironment> | DevEnvironment)
| undefined
}
Expand Down Expand Up @@ -558,43 +560,54 @@ export function resolveDevEnvironmentOptions(
dev: DevEnvironmentOptions | undefined,
preserverSymlinks: boolean,
environmentName: string | undefined,
// Backward compatibility
skipSsrTransform?: boolean,
): ResolvedDevEnvironmentOptions {
return {
sourcemap: dev?.sourcemap ?? { js: true },
sourcemapIgnoreList:
dev?.sourcemapIgnoreList === false
? () => false
: dev?.sourcemapIgnoreList || isInNodeModules,
preTransformRequests: dev?.preTransformRequests ?? true,
preTransformRequests:
dev?.preTransformRequests ?? environmentName === 'client',
warmup: dev?.warmup ?? [],
optimizeDeps: resolveOptimizeDepsConfig(
dev?.optimizeDeps,
preserverSymlinks,
),
createEnvironment: dev?.createEnvironment,
recoverable: dev?.recoverable ?? environmentName === 'client',
moduleRunnerTransform:
dev?.moduleRunnerTransform ??
(skipSsrTransform !== undefined && environmentName === 'ssr'
? skipSsrTransform
: environmentName !== 'client'),
}
}

function resolveEnvironmentOptions(
config: EnvironmentOptions,
options: EnvironmentOptions,
resolvedRoot: string,
logger: Logger,
environmentName: string,
// Backward compatibility
skipSsrTransform?: boolean,
): ResolvedEnvironmentOptions {
const resolve = resolveEnvironmentResolveOptions(config.resolve, logger)
const resolve = resolveEnvironmentResolveOptions(options.resolve, logger)
return {
resolve,
nodeCompatible: config.nodeCompatible ?? environmentName !== 'client',
webCompatible: config.webCompatible ?? environmentName === 'client',
injectInvalidationTimestamp: config.injectInvalidationTimestamp ?? true,
nodeCompatible: options.nodeCompatible ?? environmentName !== 'client',
webCompatible: options.webCompatible ?? environmentName === 'client',
injectInvalidationTimestamp: options.injectInvalidationTimestamp ?? true,
dev: resolveDevEnvironmentOptions(
config.dev,
options.dev,
resolve.preserveSymlinks,
environmentName,
skipSsrTransform,
),
build: resolveBuildEnvironmentOptions(
config.build ?? {},
options.build ?? {},
logger,
resolvedRoot,
environmentName,
Expand Down Expand Up @@ -916,6 +929,7 @@ export async function resolveConfig(
resolvedRoot,
logger,
name,
config.experimental?.skipSsrTransform,
)
}

Expand Down Expand Up @@ -1186,29 +1200,52 @@ export async function resolveConfig(
// create an internal resolver to be used in special scenarios, e.g.
// optimizer & handling css @imports
createResolver(options) {
let aliasContainer: PluginContainer | undefined
let resolverContainer: PluginContainer | undefined
const alias: {
client?: BoundedPluginContainer
ssr?: BoundedPluginContainer
} = {}
const resolver: {
client?: BoundedPluginContainer
ssr?: BoundedPluginContainer
} = {}
const environments = this.environments ?? resolvedEnvironments
const createPluginContainer = async (
environmentName: string,
plugins: Plugin[],
) => {
// The used alias and resolve plugins only use configuration options from the
// environment so we can safely cast to a base Environment instance to a
// PluginEnvironment here
const environment = new Environment(environmentName, this)
const pluginContainer = await createBoundedPluginContainer(
environment as PluginEnvironment,
plugins,
)
await pluginContainer.buildStart({})
return pluginContainer
}
async function resolve(
id: string,
importer?: string,
aliasOnly?: boolean,
ssr?: boolean,
): Promise<PartialResolvedId | null> {
let container: PluginContainer
const environmentName = ssr ? 'ssr' : 'client'
let container: BoundedPluginContainer
if (aliasOnly) {
container =
aliasContainer ||
(aliasContainer = await createPluginContainer({
...resolved,
plugins: [aliasPlugin({ entries: resolved.resolve.alias })],
}))
let aliasContainer = alias[environmentName]
if (!aliasContainer) {
aliasContainer = alias[environmentName] =
await createPluginContainer(environmentName, [
aliasPlugin({ entries: resolved.resolve.alias }),
])
}
container = aliasContainer
} else {
container =
resolverContainer ||
(resolverContainer = await createPluginContainer({
...resolved,
plugins: [
let resolverContainer = resolver[environmentName]
if (!resolverContainer) {
resolverContainer = resolver[environmentName] =
await createPluginContainer(environmentName, [
aliasPlugin({ entries: resolved.resolve.alias }),
resolvePlugin(
{
Expand All @@ -1225,13 +1262,11 @@ export async function resolveConfig(
},
environments,
),
],
}))
])
}
container = resolverContainer
}
return await container.resolveId(id, importer, {
ssr,
scan: options?.scan,
})
return await container.resolveId(id, importer, { scan: options?.scan })
}
return async (id, importer, aliasOnly, ssr) =>
(await resolve(id, importer, aliasOnly, ssr))?.id
Expand Down
Loading

0 comments on commit 319b8e0

Please sign in to comment.