Skip to content

Commit

Permalink
feat: ssr.external/noExternal -> resolve.external/noExternal
Browse files Browse the repository at this point in the history
  • Loading branch information
patak-dev committed Apr 5, 2024
1 parent 1a7d290 commit 2a0b524
Show file tree
Hide file tree
Showing 18 changed files with 162 additions and 115 deletions.
32 changes: 32 additions & 0 deletions packages/vite/src/node/__tests__/external.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
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'

describe('createIsConfiguredAsExternal', () => {
test('default', async () => {
const isExternal = await createIsExternal()
expect(isExternal('@vitejs/cjs-ssr-dep')).toBe(false)
})

test('force external', async () => {
const isExternal = await createIsExternal(true)
expect(isExternal('@vitejs/cjs-ssr-dep')).toBe(true)
})
})

async function createIsExternal(external?: true) {
const resolvedConfig = await resolveConfig(
{
configFile: false,
root: fileURLToPath(new URL('./', import.meta.url)),
resolve: { external },
},
'serve',
)
const environment = new Environment('ssr', resolvedConfig)
console.log(environment.options)
return createIsConfiguredAsExternal(environment)
}
26 changes: 15 additions & 11 deletions packages/vite/src/node/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,8 @@ function resolveEnvironmentResolveOptions(
mainFields: resolve?.mainFields ?? DEFAULT_MAIN_FIELDS,
conditions: resolve?.conditions ?? [],
externalConditions: resolve?.externalConditions ?? [],
external: resolve?.external ?? [],
noExternal: resolve?.noExternal ?? [],
extensions: resolve?.extensions ?? DEFAULT_EXTENSIONS,
dedupe: resolve?.dedupe ?? [],
preserveSymlinks: resolve?.preserveSymlinks ?? false,
Expand Down Expand Up @@ -804,8 +806,6 @@ export async function resolveConfig(
)

// Backward compatibility: merge ssr into environments.ssr.config as defaults
// Done: ssr.optimizeDeps, ssr.resolve.conditions, ssr.resolve.externalConditions,
// TODO: ssr.external, ssr.noExternal
const deprecatedSsrOptimizeDepsConfig = config.ssr?.optimizeDeps ?? {}
const configEnvironmentsSsr = config.environments!.ssr
if (configEnvironmentsSsr) {
Expand All @@ -814,13 +814,13 @@ export async function resolveConfig(
configEnvironmentsSsr.dev.optimizeDeps ?? {},
deprecatedSsrOptimizeDepsConfig,
)
// TODO: should we merge here?
configEnvironmentsSsr.resolve ??= {}
const deprecatedSsrResolveConditions = config.ssr?.resolve?.conditions
configEnvironmentsSsr.resolve.conditions ??= deprecatedSsrResolveConditions // TODO: should we merge?
const deprecatedSsrResolveExternalConditions =
config.ssr?.resolve?.externalConditions
configEnvironmentsSsr.resolve.conditions ??= config.ssr?.resolve?.conditions
configEnvironmentsSsr.resolve.externalConditions ??=
deprecatedSsrResolveExternalConditions // TODO: should we merge?
config.ssr?.resolve?.externalConditions
configEnvironmentsSsr.resolve.external ??= config.ssr?.external
configEnvironmentsSsr.resolve.noExternal ??= config.ssr?.noExternal

if (config.ssr?.target === 'webworker') {
configEnvironmentsSsr.webCompatible = true
Expand Down Expand Up @@ -891,17 +891,20 @@ export async function resolveConfig(
undefined, // default environment
)

// Backward compatibility: merge environments.ssr.dev.optimizeDeps back into ssr.optimizeDeps
// Backward compatibility: merge config.environments.ssr back into config.ssr
// so ecosystem SSR plugins continue to work if only environments.ssr is configured
const patchedConfigSsr = {
...config.ssr,
external: resolvedEnvironments.ssr?.resolve.external,
noExternal: resolvedEnvironments.ssr?.resolve.noExternal,
optimizeDeps: mergeConfig(
resolvedEnvironments.ssr?.dev?.optimizeDeps ?? {},
config.ssr?.optimizeDeps ?? {},
resolvedEnvironments.ssr?.dev?.optimizeDeps ?? {},
),
resolve: {
...config.ssr?.resolve,
conditions: resolvedEnvironments.ssr?.resolve.conditions,
externalConditions: resolvedEnvironments.ssr?.resolve.externalConditions,
...config.ssr?.resolve,
},
}
const ssr = resolveSSROptions(
Expand Down Expand Up @@ -1150,7 +1153,6 @@ export async function resolveConfig(
root: resolvedRoot,
isProduction,
isBuild: command === 'build',
ssrConfig: resolved.ssr,
asSrc: true,
preferRelative: false,
tryIndex: true,
Expand Down Expand Up @@ -1439,6 +1441,8 @@ async function bundleConfigFile(
mainFields: [],
conditions: [],
externalConditions: [],
external: [],
noExternal: [],
overrideConditions: ['node'],
dedupe: [],
extensions: DEFAULT_EXTENSIONS,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,54 +1,73 @@
import path from 'node:path'
import type { InternalResolveOptions } from '../plugins/resolve'
import { tryNodeResolve } from '../plugins/resolve'
import type { InternalResolveOptions } from './plugins/resolve'
import { tryNodeResolve } from './plugins/resolve'
import {
bareImportRE,
createDebugger,
createFilter,
getNpmPackageName,
isBuiltin,
} from '../utils'
import type { ResolvedConfig } from '..'
} from './utils'
import type { Environment } from './environment'

const debug = createDebugger('vite:ssr-external')
const debug = createDebugger('vite:external')

const isSsrExternalCache = new WeakMap<
ResolvedConfig,
const isExternalCache = new WeakMap<
Environment,
(id: string, importer?: string) => boolean | undefined
>()

export function shouldExternalizeForSSR(
export function shouldExternalize(
environment: Environment,
id: string,
importer: string | undefined,
config: ResolvedConfig,
): boolean | undefined {
let isSsrExternal = isSsrExternalCache.get(config)
if (!isSsrExternal) {
isSsrExternal = createIsSsrExternal(config)
isSsrExternalCache.set(config, isSsrExternal)
let isExternal = isExternalCache.get(environment)
if (!isExternal) {
isExternal = createIsExternal(environment)
isExternalCache.set(environment, isExternal)
}
return isSsrExternal(id, importer)
return isExternal(id, importer)
}

export function createIsConfiguredAsSsrExternal(
config: ResolvedConfig,
const isConfiguredAsExternalCache = new WeakMap<
Environment,
(id: string, importer?: string) => boolean
>()

export function isConfiguredAsExternal(
environment: Environment,
id: string,
importer?: string,
): boolean {
let isExternal = isConfiguredAsExternalCache.get(environment)
if (!isExternal) {
isExternal = createIsConfiguredAsExternal(environment)
isConfiguredAsExternalCache.set(environment, isExternal)
}
return isExternal(id, importer)
}

export function createIsConfiguredAsExternal(
environment: Environment,
): (id: string, importer?: string) => boolean {
const { ssr, root } = config
const noExternal = ssr?.noExternal
const { config, options } = environment
const { root } = config
const { external, noExternal } = options.resolve
const noExternalFilter =
noExternal !== 'undefined' &&
typeof noExternal !== 'boolean' &&
!(Array.isArray(noExternal) && noExternal.length === 0) &&
createFilter(undefined, noExternal, { resolve: false })

const targetConditions = config.ssr.resolve?.externalConditions || []
const targetConditions = options.resolve?.externalConditions || []

const resolveOptions: InternalResolveOptions = {
...config.resolve,
...options.resolve,
root,
isProduction: false,
isBuild: true,
conditions: targetConditions,
webCompatible: ssr.target === 'webworker', // TODO: back compat
webCompatible: config.ssr.target === 'webworker', // TODO: back compat
nodeCompatible: true,
}

Expand Down Expand Up @@ -90,9 +109,9 @@ export function createIsConfiguredAsSsrExternal(
return (id: string, importer?: string) => {
if (
// If this id is defined as external, force it as external
// Note that individual package entries are allowed in ssr.external
ssr.external !== true &&
ssr.external?.includes(id)
// Note that individual package entries are allowed in `external`
external !== true &&
external.includes(id)
) {
return true
}
Expand All @@ -103,8 +122,8 @@ export function createIsConfiguredAsSsrExternal(
if (
// A package name in ssr.external externalizes every
// externalizable package entry
ssr.external !== true &&
ssr.external?.includes(pkgName)
external !== true &&
external.includes(pkgName)
) {
return isExternalizable(id, importer, true)
}
Expand All @@ -114,28 +133,28 @@ export function createIsConfiguredAsSsrExternal(
if (noExternalFilter && !noExternalFilter(pkgName)) {
return false
}
// If `ssr.external: true`, all will be externalized by default, regardless if
// If external is true, all will be externalized by default, regardless if
// it's a linked package
return isExternalizable(id, importer, ssr.external === true)
return isExternalizable(id, importer, external === true)
}
}

function createIsSsrExternal(
config: ResolvedConfig,
function createIsExternal(
environment: Environment,
): (id: string, importer?: string) => boolean | undefined {
const processedIds = new Map<string, boolean | undefined>()

const isConfiguredAsExternal = createIsConfiguredAsSsrExternal(config)
const isConfiguredAsExternal = createIsConfiguredAsExternal(environment)

return (id: string, importer?: string) => {
if (processedIds.has(id)) {
return processedIds.get(id)
}
let external = false
let isExternal = false
if (id[0] !== '.' && !path.isAbsolute(id)) {
external = isBuiltin(id) || isConfiguredAsExternal(id, importer)
isExternal = isBuiltin(id) || isConfiguredAsExternal(id, importer)
}
processedIds.set(id, external)
return external
processedIds.set(id, isExternal)
return isExternal
}
}
1 change: 0 additions & 1 deletion packages/vite/src/node/idResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ export function createIdResolver(
root: config.root,
isProduction: config.isProduction,
isBuild: config.command === 'build',
ssrConfig: config.ssr,
asSrc: true,
preferRelative: false,
tryIndex: true,
Expand Down
4 changes: 2 additions & 2 deletions packages/vite/src/node/plugins/importAnalysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ import { getDepOptimizationConfig } from '../config'
import type { ResolvedConfig } from '../config'
import type { Plugin } from '../plugin'
import type { DevEnvironment } from '../server/environment'
import { shouldExternalizeForSSR } from '../ssr/ssrExternal'
import { shouldExternalize } from '../external'
import { optimizedDepNeedsInterop } from '../optimizer'
import {
cleanUrl,
Expand Down Expand Up @@ -493,7 +493,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
}
// skip ssr external
if (ssr && !matchAlias(specifier)) {
if (shouldExternalizeForSSR(specifier, importer, config)) {
if (shouldExternalize(environment, specifier, importer)) {
return
}
if (isBuiltin(specifier)) {
Expand Down
6 changes: 1 addition & 5 deletions packages/vite/src/node/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type { ObjectHook } from 'rollup'
import type { PluginHookUtils, ResolvedConfig } from '../config'
import { isDepsOptimizerEnabled } from '../config'
import type { HookHandler, Plugin, PluginWithRequiredHook } from '../plugin'
import { shouldExternalizeForSSR } from '../ssr/ssrExternal'
import { watchPackageDataPlugin } from '../packages'
import { getFsUtils } from '../fsUtils'
import { jsonPlugin } from './json'
Expand Down Expand Up @@ -65,10 +64,7 @@ export async function resolvePlugins(
asSrc: true,
fsUtils: getFsUtils(config),
optimizeDeps: true,
shouldExternalize:
isBuild && config.build.ssr
? (id, importer) => shouldExternalizeForSSR(id, importer, config)
: undefined,
externalize: isBuild && !!config.build.ssr, // TODO: should we do this for all environments?
},
config.environments,
),
Expand Down
15 changes: 9 additions & 6 deletions packages/vite/src/node/plugins/preAlias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type {
ResolvedConfig,
} from '..'
import type { Plugin } from '../plugin'
import { createIsConfiguredAsSsrExternal } from '../ssr/ssrExternal'
import { isConfiguredAsExternal } from '../external'
import {
bareImportRE,
isInNodeModules,
Expand All @@ -22,18 +22,17 @@ import { tryOptimizedResolve } from './resolve'
*/
export function preAliasPlugin(config: ResolvedConfig): Plugin {
const findPatterns = getAliasPatterns(config.resolve.alias)
const isConfiguredAsExternal = createIsConfiguredAsSsrExternal(config)
const isBuild = config.command === 'build'
const fsUtils = getFsUtils(config)
return {
name: 'vite:pre-alias',
async resolveId(id, importer, options) {
const { environment } = this
const ssr = options?.ssr === true
const depsOptimizer =
this.environment?.mode === 'dev'
? this.environment.depsOptimizer
: undefined
environment?.mode === 'dev' ? environment.depsOptimizer : undefined
if (
environment &&
importer &&
depsOptimizer &&
bareImportRE.test(id) &&
Expand Down Expand Up @@ -71,7 +70,11 @@ export function preAliasPlugin(config: ResolvedConfig): Plugin {
(isInNodeModules(resolvedId) ||
optimizeDeps.include?.includes(id)) &&
isOptimizable(resolvedId, optimizeDeps) &&
!(isBuild && ssr && isConfiguredAsExternal(id, importer)) &&
!(
isBuild &&
ssr &&
isConfiguredAsExternal(environment, id, importer)
) &&
(!ssr || optimizeAliasReplacementForSSR(resolvedId, optimizeDeps))
) {
// aliased dep has not yet been optimized
Expand Down
Loading

0 comments on commit 2a0b524

Please sign in to comment.