Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
fix: optimize deps on dev SSR, builtin imports in node (#8854)
  • Loading branch information
patak-dev committed Jun 29, 2022
1 parent cd8d63b commit d49856c
Show file tree
Hide file tree
Showing 21 changed files with 226 additions and 129 deletions.
9 changes: 7 additions & 2 deletions packages/vite/src/node/optimizer/esbuildDepPlugin.ts
Expand Up @@ -5,6 +5,7 @@ import { KNOWN_ASSET_TYPES } from '../constants'
import type { ResolvedConfig } from '..'
import {
flattenId,
isBuiltin,
isExternalUrl,
isRunningWithYarnPnp,
moduleListContains,
Expand Down Expand Up @@ -42,7 +43,8 @@ const externalTypes = [
export function esbuildDepPlugin(
qualified: Record<string, string>,
exportsData: Record<string, ExportsData>,
config: ResolvedConfig
config: ResolvedConfig,
ssr: boolean
): Plugin {
// remove optimizable extensions from `externalTypes` list
const allExternalTypes = config.optimizeDeps.extensions
Expand Down Expand Up @@ -77,7 +79,7 @@ export function esbuildDepPlugin(
_importer = importer in qualified ? qualified[importer] : importer
}
const resolver = kind.startsWith('require') ? _resolveRequire : _resolve
return resolver(id, _importer, undefined)
return resolver(id, _importer, undefined, ssr)
}

const resolveResult = (id: string, resolved: string) => {
Expand All @@ -87,6 +89,9 @@ export function esbuildDepPlugin(
namespace: 'browser-external'
}
}
if (ssr && isBuiltin(resolved)) {
return
}
if (isExternalUrl(resolved)) {
return {
path: resolved,
Expand Down
22 changes: 18 additions & 4 deletions packages/vite/src/node/optimizer/index.ts
Expand Up @@ -26,7 +26,11 @@ import { transformWithEsbuild } from '../plugins/esbuild'
import { ESBUILD_MODULES_TARGET } from '../constants'
import { esbuildDepPlugin } from './esbuildDepPlugin'
import { scanImports } from './scan'
export { initDepsOptimizer, getDepsOptimizer } from './optimizer'
export {
initDepsOptimizer,
initDevSsrDepsOptimizer,
getDepsOptimizer
} from './optimizer'

export const debuggerViteDeps = createDebugger('vite:deps')
const debug = debuggerViteDeps
Expand All @@ -47,7 +51,7 @@ export type ExportsData = {
}

export interface DepsOptimizer {
metadata: (options: { ssr: boolean }) => DepOptimizationMetadata
metadata: DepOptimizationMetadata
scanProcessing?: Promise<void>
registerMissingImport: (
id: string,
Expand Down Expand Up @@ -546,6 +550,9 @@ export async function runOptimizeDeps(
: JSON.stringify(process.env.NODE_ENV || config.mode)
}

const platform =
ssr && config.ssr?.target !== 'webworker' ? 'node' : 'browser'

const start = performance.now()

const result = await build({
Expand All @@ -555,9 +562,16 @@ export async function runOptimizeDeps(
// We can't use platform 'neutral', as esbuild has custom handling
// when the platform is 'node' or 'browser' that can't be emulated
// by using mainFields and conditions
platform: ssr && config.ssr?.target !== 'webworker' ? 'node' : 'browser',
platform,
define,
format: 'esm',
// See https://github.com/evanw/esbuild/issues/1921#issuecomment-1152991694
banner:
platform === 'node'
? {
js: `import { createRequire } from 'module';const require = createRequire(import.meta.url);`
}
: undefined,
target: isBuild ? config.build.target || undefined : ESBUILD_MODULES_TARGET,
external: config.optimizeDeps?.exclude,
logLevel: 'error',
Expand All @@ -568,7 +582,7 @@ export async function runOptimizeDeps(
metafile: true,
plugins: [
...plugins,
esbuildDepPlugin(flatIdDeps, flatIdToExports, config)
esbuildDepPlugin(flatIdDeps, flatIdToExports, config, ssr)
],
...esbuildOptions,
supported: {
Expand Down
84 changes: 55 additions & 29 deletions packages/vite/src/node/optimizer/optimizer.ts
Expand Up @@ -24,7 +24,6 @@ import {
runOptimizeDeps
} from '.'
import type {
DepOptimizationMetadata,
DepOptimizationProcessing,
DepsOptimizer,
OptimizedDepInfo
Expand All @@ -39,18 +38,36 @@ const isDebugEnabled = _debug('vite:deps').enabled
const debounceMs = 100

const depsOptimizerMap = new WeakMap<ResolvedConfig, DepsOptimizer>()
const devSsrDepsOptimizerMap = new WeakMap<ResolvedConfig, DepsOptimizer>()

export function getDepsOptimizer(
config: ResolvedConfig
config: ResolvedConfig,
type: { ssr?: boolean }
): DepsOptimizer | undefined {
// Workers compilation shares the DepsOptimizer from the main build
return depsOptimizerMap.get(config.mainConfig || config)
const isDevSsr = type.ssr && config.command !== 'build'
return (isDevSsr ? devSsrDepsOptimizerMap : depsOptimizerMap).get(
config.mainConfig || config
)
}

export async function initDepsOptimizer(
config: ResolvedConfig,
server?: ViteDevServer
): Promise<DepsOptimizer> {
): Promise<void> {
await createDepsOptimizer(config, server)
}

export async function initDevSsrDepsOptimizer(
config: ResolvedConfig
): Promise<void> {
await createDevSsrDepsOptimizer(config)
}

async function createDepsOptimizer(
config: ResolvedConfig,
server?: ViteDevServer
): Promise<void> {
const { logger } = config
const isBuild = config.command === 'build'

Expand All @@ -62,18 +79,11 @@ export async function initDepsOptimizer(

let handle: NodeJS.Timeout | undefined

let ssrServerDepsMetadata: DepOptimizationMetadata
let _metadata =
let metadata =
cachedMetadata || initDepsOptimizerMetadata(config, sessionTimestamp)

const depsOptimizer: DepsOptimizer = {
metadata: (options: { ssr: boolean }) => {
if (isBuild || !options.ssr) {
return _metadata
} else {
return ssrServerDepsMetadata
}
},
metadata,
registerMissingImport,
run: () => debouncedProcessing(0),
isOptimizedDepFile: (id: string) => isOptimizedDepFile(id, config),
Expand All @@ -89,10 +99,6 @@ export async function initDepsOptimizer(

depsOptimizerMap.set(config, depsOptimizer)

if (!isBuild && config.ssr) {
ssrServerDepsMetadata = await optimizeServerSsrDeps(config)
}

let newDepsDiscovered = false

let newDepsToLog: string[] = []
Expand Down Expand Up @@ -137,7 +143,6 @@ export async function initDepsOptimizer(
config,
sessionTimestamp
)
const metadata = _metadata
for (const depInfo of Object.values(discovered)) {
addOptimizedDepInfo(metadata, 'discovered', {
...depInfo,
Expand All @@ -155,8 +160,6 @@ export async function initDepsOptimizer(
try {
debug(colors.green(`scanning for dependencies...`))

const metadata = _metadata

const discovered = await discoverProjectDependencies(
config,
sessionTimestamp
Expand Down Expand Up @@ -201,7 +204,7 @@ export async function initDepsOptimizer(
// Ensure that a rerun will not be issued for current discovered deps
if (handle) clearTimeout(handle)

if (Object.keys(_metadata.discovered).length === 0) {
if (Object.keys(metadata.discovered).length === 0) {
currentlyProcessing = false
return
}
Expand All @@ -217,8 +220,6 @@ export async function initDepsOptimizer(
// if the rerun fails, _metadata remains untouched, current discovered
// deps are cleaned, and a fullReload is issued

let metadata = _metadata

// All deps, previous known and newly discovered are rebundled,
// respect insertion order to keep the metadata file stable

Expand Down Expand Up @@ -324,7 +325,7 @@ export async function initDepsOptimizer(
)
}

metadata = _metadata = newData
metadata = depsOptimizer.metadata = newData
resolveEnqueuedProcessingPromises()
}

Expand Down Expand Up @@ -418,7 +419,7 @@ export async function initDepsOptimizer(
// debounce time to wait for new missing deps finished, issue a new
// optimization of deps (both old and newly found) once the previous
// optimizeDeps processing is finished
const deps = Object.keys(_metadata.discovered)
const deps = Object.keys(metadata.discovered)
const depsString = depsLogString(deps)
debug(colors.green(`new dependencies found: ${depsString}`))
runOptimizer()
Expand Down Expand Up @@ -449,7 +450,6 @@ export async function initDepsOptimizer(
`Error: ${id} is a missing dependency in SSR dev server, it needs to be added to optimizeDeps.include`
)
}
const metadata = _metadata
const optimized = metadata.optimized[id]
if (optimized) {
return optimized
Expand Down Expand Up @@ -539,7 +539,7 @@ export async function initDepsOptimizer(
if (!firstRunEnsured && !firstRunCalled && registeredIds.length === 0) {
setTimeout(() => {
if (!firstRunCalled && registeredIds.length === 0) {
getDepsOptimizer(config)?.run()
debouncedProcessing(0) // queue the optimizer run
}
}, runOptimizerIfIdleAfterMs)
}
Expand Down Expand Up @@ -580,7 +580,7 @@ export async function initDepsOptimizer(
if (registeredIds.length > 0) {
runOptimizerWhenIdle()
} else {
getDepsOptimizer(config)?.run()
debouncedProcessing(0) // queue the optimizer run
}
}
}
Expand All @@ -596,8 +596,34 @@ export async function initDepsOptimizer(
}
}
}
}

return depsOptimizer
async function createDevSsrDepsOptimizer(
config: ResolvedConfig
): Promise<void> {
const metadata = await optimizeServerSsrDeps(config)
const depsOptimizer = {
metadata,
isOptimizedDepFile: (id: string) => isOptimizedDepFile(id, config),
isOptimizedDepUrl: createIsOptimizedDepUrl(config),
getOptimizedDepId: (depInfo: OptimizedDepInfo) =>
`${depInfo.file}?v=${depInfo.browserHash}`,

registerMissingImport: () => {
throw new Error(
'Vite Internal Error: registerMissingImport is not supported in dev SSR'
)
},
// noop, there is no scanning during dev SSR
// the optimizer blocks the server start
run: () => {},
registerWorkersSource: (id: string) => {},
delayDepsOptimizerUntil: (id: string, done: () => Promise<any>) => {},
resetRegisteredIds: () => {},
ensureFirstRun: () => {},
options: config.optimizeDeps
}
devSsrDepsOptimizerMap.set(config, depsOptimizer)
}

export async function preTransformOptimizeDepsEntries(
Expand Down
7 changes: 3 additions & 4 deletions packages/vite/src/node/plugins/importAnalysis.ts
Expand Up @@ -214,7 +214,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
)
}

const depsOptimizer = getDepsOptimizer(config)
const depsOptimizer = getDepsOptimizer(config, { ssr })

const { moduleGraph } = server
// since we are already in the transform phase of the importer, it must
Expand Down Expand Up @@ -275,8 +275,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
// the dependency needs to be resolved starting from the original source location of the optimized file
// because starting from node_modules/.vite will not find the dependency if it was not hoisted
// (that is, if it is under node_modules directory in the package source of the optimized file)
for (const optimizedModule of depsOptimizer.metadata({ ssr })
.depInfoList) {
for (const optimizedModule of depsOptimizer.metadata.depInfoList) {
if (!optimizedModule.src) continue // Ignore chunks
if (optimizedModule.file === importerModule.file) {
importerFile = optimizedModule.src
Expand Down Expand Up @@ -479,7 +478,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
const file = cleanUrl(resolvedId) // Remove ?v={hash}

const needsInterop = await optimizedDepNeedsInterop(
depsOptimizer.metadata({ ssr }),
depsOptimizer.metadata,
file,
config
)
Expand Down
7 changes: 3 additions & 4 deletions packages/vite/src/node/plugins/importAnalysisBuild.ts
Expand Up @@ -154,7 +154,7 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin {
}

const { root } = config
const depsOptimizer = getDepsOptimizer(config)
const depsOptimizer = getDepsOptimizer(config, { ssr })

const normalizeUrl = async (
url: string,
Expand All @@ -170,8 +170,7 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin {
// the dependency needs to be resolved starting from the original source location of the optimized file
// because starting from node_modules/.vite will not find the dependency if it was not hoisted
// (that is, if it is under node_modules directory in the package source of the optimized file)
for (const optimizedModule of depsOptimizer.metadata({ ssr })
.depInfoList) {
for (const optimizedModule of depsOptimizer.metadata.depInfoList) {
if (!optimizedModule.src) continue // Ignore chunks
if (optimizedModule.file === importer) {
importerFile = optimizedModule.src
Expand Down Expand Up @@ -262,7 +261,7 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin {
const file = cleanUrl(resolvedId) // Remove ?v={hash}

const needsInterop = await optimizedDepNeedsInterop(
depsOptimizer.metadata({ ssr }),
depsOptimizer.metadata,
file,
config
)
Expand Down
3 changes: 2 additions & 1 deletion packages/vite/src/node/plugins/index.ts
Expand Up @@ -62,7 +62,8 @@ export async function resolvePlugins(
packageCache: config.packageCache,
ssrConfig: config.ssr,
asSrc: true,
getDepsOptimizer: () => getDepsOptimizer(config),
getDepsOptimizer: (type: { ssr?: boolean }) =>
getDepsOptimizer(config, type),
shouldExternalize:
isBuild && config.build.ssr && config.ssr?.format !== 'cjs'
? (id) => shouldExternalizeForSSR(id, config)
Expand Down

0 comments on commit d49856c

Please sign in to comment.