Skip to content

Commit

Permalink
feat: auto re-run dep optimization on discovery of new imports
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Jan 27, 2021
1 parent 5fe9001 commit 470b4e4
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 28 deletions.
38 changes: 26 additions & 12 deletions packages/vite/src/node/optimizer/index.ts
Expand Up @@ -47,6 +47,7 @@ export interface DepOptimizationMetadata {
string,
{
file: string
src: string
needsInterop: boolean
}
>
Expand All @@ -55,8 +56,9 @@ export interface DepOptimizationMetadata {
export async function optimizeDeps(
config: ResolvedConfig,
force = config.server.force,
asCommand = false
) {
asCommand = false,
newDeps?: Record<string, string> // missing imports encountered after server has started
): Promise<DepOptimizationMetadata | null> {
config = {
...config,
command: 'build'
Expand All @@ -67,7 +69,7 @@ export async function optimizeDeps(

if (!cacheDir) {
log(`No package.json. Skipping.`)
return
return null
}

const dataPath = path.join(cacheDir, 'metadata.json')
Expand All @@ -84,7 +86,7 @@ export async function optimizeDeps(
// hash is consistent, no need to re-bundle
if (prevData && prevData.hash === data.hash) {
log('Hash is consistent. Skipping. Use --force to override.')
return
return prevData
}
}

Expand All @@ -94,7 +96,13 @@ export async function optimizeDeps(
fs.mkdirSync(cacheDir, { recursive: true })
}

const { deps, missing } = await scanImports(config)
let deps: Record<string, string>, missing: Record<string, string>
if (!newDeps) {
;({ deps, missing } = await scanImports(config))
} else {
deps = newDeps
missing = {}
}

const missingIds = Object.keys(missing)
if (missingIds.length) {
Expand Down Expand Up @@ -132,17 +140,21 @@ export async function optimizeDeps(
if (!qualifiedIds.length) {
writeFile(dataPath, JSON.stringify(data, null, 2))
log(`No dependencies to bundle. Skipping.\n\n\n`)
return
return data
}

const depsString = qualifiedIds.map((id) => chalk.yellow(id)).join(`, `)
if (!asCommand) {
// This is auto run on server start - let the user know that we are
// pre-optimizing deps
logger.info(chalk.greenBright(`Pre-bundling dependencies:\n${depsString}`))
logger.info(
`(this will be run only when your dependencies or config have changed)`
)
if (!newDeps) {
// This is auto run on server start - let the user know that we are
// pre-optimizing deps
logger.info(
chalk.greenBright(`Pre-bundling dependencies:\n${depsString}`)
)
logger.info(
`(this will be run only when your dependencies or config have changed)`
)
}
} else {
logger.info(chalk.greenBright(`Optimizing dependencies:\n${depsString}`))
}
Expand Down Expand Up @@ -193,13 +205,15 @@ export async function optimizeDeps(
}
data.optimized[id] = {
file: normalizePath(path.resolve(output)),
src: entry,
needsInterop: needsInterop(id, entry, exports)
}
break
}
}

writeFile(dataPath, JSON.stringify(data, null, 2))
return data
}

// https://github.com/vitejs/vite/issues/1724#issuecomment-767619642
Expand Down
62 changes: 62 additions & 0 deletions packages/vite/src/node/optimizer/registerMissing.ts
@@ -0,0 +1,62 @@
import chalk from 'chalk'
import { optimizeDeps } from '.'
import { ViteDevServer } from '..'

const debounceMs = 100

export function createMissingImpoterRegisterFn(server: ViteDevServer) {
const { logger } = server.config
let knownOptimized = server._optimizeDepsMetadata!.optimized
let currentMissing: Record<string, string> = {}
let handle: NodeJS.Timeout

async function rerun() {
const newDeps = currentMissing
currentMissing = {}

for (const id in knownOptimized) {
newDeps[id] = knownOptimized[id].src
}

logger.info(
chalk.yellow(`new imports encountered, updating dependencies...`),
{
timestamp: true
}
)

try {
const newData = (server._optimizeDepsMetadata = await optimizeDeps(
server.config,
true,
false,
newDeps
))
knownOptimized = newData!.optimized
server.ws.send({
type: 'full-reload',
path: '*'
})
} catch (e) {
logger.error(
chalk.red(`error while updating dependencies:\n${e.stack}`),
{ timestamp: true }
)
} finally {
server._hasPendingReload = false
}

logger.info(chalk.greenBright(`✨ dependencies updated.`), {
timestamp: true
})
}

return function registerMissingImport(id: string, resolved: string) {
if (!knownOptimized[id]) {
currentMissing[id] = resolved
if (handle) clearTimeout(handle)
handle = setTimeout(rerun, debounceMs)
server._hasPendingReload = true
}
}
}
27 changes: 21 additions & 6 deletions packages/vite/src/node/plugins/resolve.ts
Expand Up @@ -23,6 +23,7 @@ import slash from 'slash'
import { createFilter } from '@rollup/pluginutils'
import { PartialResolvedId } from 'rollup'
import { resolve as _resolveExports } from 'resolve.exports'
import { isCSSRequest } from './css'

const altMainFields = [
'module',
Expand Down Expand Up @@ -335,15 +336,29 @@ export function tryNodeResolve(
moduleSideEffects: pkg.hasSideEffects(resolved)
}
} else {
// During serve, inject a version query to npm deps so that the browser
// can cache it without revalidation. Make sure to apply this only to
// files actually inside node_modules so that locally linked packages
// in monorepos are not cached this way.
if (resolved.includes('node_modules')) {
const versionHash = server?._optimizeDepsMetadata?.hash
if (!resolved.includes('node_modules') || !server) {
// linked
return { id: resolved }
}
// if we reach here, it's a valid dep import that hasn't been optimzied.
const exclude = server.config.optimizeDeps?.exclude
if (
exclude?.includes(pkgId) ||
exclude?.includes(id) ||
isCSSRequest(resolved) ||
server.config.assetsInclude(resolved)
) {
// excluded from optimization
// Inject a version query to npm deps so that the browser
// can cache it without revalidation.
const versionHash = server._optimizeDepsMetadata?.hash
if (versionHash) {
resolved = injectQuery(resolved, `v=${versionHash}`)
}
} else {
// this is a missing import.
// queue optimize-deps re-run.
server._registerMissingImport?.(id, resolved)
}
return { id: resolved }
}
Expand Down
24 changes: 14 additions & 10 deletions packages/vite/src/node/server/index.ts
Expand Up @@ -46,6 +46,7 @@ import { DepOptimizationMetadata, optimizeDeps } from '../optimizer'
import { ssrLoadModule } from '../ssr/ssrModuleLoader'
import { resolveSSRExternal } from '../ssr/ssrExternal'
import { ssrRewriteStacktrace } from '../ssr/ssrStacktrace'
import { createMissingImpoterRegisterFn } from '../optimizer/registerMissing'

export interface ServerOptions {
host?: string
Expand Down Expand Up @@ -239,6 +240,14 @@ export interface ViteDevServer {
module: ModuleNode
}
>
/**
* @internal
*/
_registerMissingImport: ((id: string, resolved: string) => void) | null
/**
* @internal
*/
_hasPendingReload: boolean
}

export async function createServer(
Expand Down Expand Up @@ -310,7 +319,9 @@ export async function createServer(
},
_optimizeDepsMetadata: null,
_ssrExternals: null,
_globImporters: {}
_globImporters: {},
_registerMissingImport: null,
_hasPendingReload: false
}

process.once('SIGTERM', async () => {
Expand Down Expand Up @@ -435,15 +446,8 @@ export async function createServer(

const runOptimize = async () => {
if (config.optimizeCacheDir) {
// run optimizer
await optimizeDeps(config)
// after optimization, read updated optimization metadata
const dataPath = path.resolve(config.optimizeCacheDir, 'metadata.json')
if (fs.existsSync(dataPath)) {
server._optimizeDepsMetadata = JSON.parse(
fs.readFileSync(dataPath, 'utf-8')
)
}
server._optimizeDepsMetadata = await optimizeDeps(config)
server._registerMissingImport = createMissingImpoterRegisterFn(server)
}
}

Expand Down

0 comments on commit 470b4e4

Please sign in to comment.