Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: respect optimize deps entries #8489

Merged
merged 3 commits into from Jun 8, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/vite/src/node/optimizer/index.ts
Expand Up @@ -45,11 +45,18 @@ export type ExportsData = {
export interface DepsOptimizer {
metadata: DepOptimizationMetadata
scanProcessing?: Promise<void>

registerMissingImport: (id: string, resolved: string) => OptimizedDepInfo
run: () => void

isOptimizedDepFile: (id: string) => boolean
isOptimizedDepUrl: (url: string) => boolean
getOptimizedDepId: (depInfo: OptimizedDepInfo) => string

delayDepsOptimizerUntil: (id: string, done: () => Promise<any>) => void
registerWorkersSource: (id: string) => void
resetRegisteredIds: () => void
bluwy marked this conversation as resolved.
Show resolved Hide resolved

options: DepOptimizationOptions
}

Expand Down
89 changes: 89 additions & 0 deletions packages/vite/src/node/optimizer/optimizer.ts
@@ -1,6 +1,8 @@
import colors from 'picocolors'
import _debug from 'debug'
import glob from 'fast-glob'
import { getHash } from '../utils'
import { transformRequest } from '../server/transformRequest'
import type { ResolvedConfig, ViteDevServer } from '..'
import {
addOptimizedDepInfo,
Expand Down Expand Up @@ -65,6 +67,9 @@ export async function initDepsOptimizer(
isOptimizedDepUrl: createIsOptimizedDepUrl(config),
getOptimizedDepId: (depInfo: OptimizedDepInfo) =>
isBuild ? depInfo.file : `${depInfo.file}?v=${depInfo.browserHash}`,
registerWorkersSource,
delayDepsOptimizerUntil,
resetRegisteredIds,
options: config.optimizeDeps
}

Expand Down Expand Up @@ -101,8 +106,12 @@ export async function initDepsOptimizer(
let enqueuedRerun: (() => void) | undefined
let currentlyProcessing = false

// Only pretransform optimizeDeps.entries on cold start
let optimizeDepsEntriesVisited = !!cachedMetadata

// If there wasn't a cache or it is outdated, we need to prepare a first run
let firstRunCalled = !!cachedMetadata

if (!cachedMetadata) {
if (!scan) {
// Initialize discovered deps with manually added optimizeDeps.include info
Expand Down Expand Up @@ -496,5 +505,85 @@ export async function initDepsOptimizer(
}, timeout)
}

const runOptimizerIfIdleAfterMs = 100

let registeredIds: { id: string; done: () => Promise<any> }[] = []
let seenIds = new Set<string>()
let workersSources = new Set<string>()
let waitingOn: string | undefined

function resetRegisteredIds() {
registeredIds = []
seenIds = new Set<string>()
workersSources = new Set<string>()
waitingOn = undefined
}

function registerWorkersSource(id: string): void {
workersSources.add(id)
if (waitingOn === id) {
waitingOn = undefined
}
}

function delayDepsOptimizerUntil(id: string, done: () => Promise<any>): void {
if (!depsOptimizer.isOptimizedDepFile(id) && !seenIds.has(id)) {
seenIds.add(id)
registeredIds.push({ id, done })
runOptimizerWhenIdle()
}
if (server && !optimizeDepsEntriesVisited) {
optimizeDepsEntriesVisited = true
pretransformOptimizeDepsEntries(server)
}
}

function runOptimizerWhenIdle() {
if (!waitingOn) {
const next = registeredIds.pop()
if (next) {
waitingOn = next.id
const afterLoad = () => {
waitingOn = undefined
if (registeredIds.length > 0) {
runOptimizerWhenIdle()
} else if (!workersSources.has(next.id)) {
getDepsOptimizer(config)?.run()
}
}
next
.done()
.then(() => {
setTimeout(
afterLoad,
registeredIds.length > 0 ? 0 : runOptimizerIfIdleAfterMs
)
})
.catch(afterLoad)
}
}
}

return depsOptimizer
}

export async function pretransformOptimizeDepsEntries(
patak-dev marked this conversation as resolved.
Show resolved Hide resolved
server: ViteDevServer
): Promise<void> {
const { config } = server
const { entries } = config.optimizeDeps
if (entries) {
const explicitEntries = await glob(entries, {
cwd: config.root,
ignore: ['**/node_modules/**', `**/${config.build.outDir}/**`],
absolute: true
})
// TODO: should we restrict the entries to JS and HTML like the
// scanner did? I think we can let the user chose any entry
for (const entry of explicitEntries) {
transformRequest(entry, server, { ssr: false }).catch((e) => {
config.logger.error(e.message)
})
}
}
}
13 changes: 6 additions & 7 deletions packages/vite/src/node/plugins/importAnalysis.ts
Expand Up @@ -53,10 +53,7 @@ import {
optimizedDepNeedsInterop
} from '../optimizer'
import { checkPublicFile } from './asset'
import {
ERR_OUTDATED_OPTIMIZED_DEP,
delayDepsOptimizerUntil
} from './optimizedDeps'
import { ERR_OUTDATED_OPTIMIZED_DEP } from './optimizedDeps'
import { isCSSRequest, isDirectCSSRequest } from './css'
import { browserExternalId } from './resolve'

Expand Down Expand Up @@ -599,7 +596,9 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
)

// pre-transform known direct imports
// TODO: we should also crawl dynamic imports
// TODO: should we also crawl dynamic imports? or the experience is good enough to allow
// users to chose their tradeoffs by explicitily setting optimizeDeps.entries for the
// most common dynamic imports
if (config.server.preTransformRequests && staticImportedUrls.size) {
staticImportedUrls.forEach(({ url, id }) => {
url = unwrapId(removeImportQuery(url)).replace(
Expand All @@ -614,8 +613,8 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
// Unexpected error, log the issue but avoid an unhandled exception
config.logger.error(e.message)
})
if (!config.optimizeDeps.devScan) {
delayDepsOptimizerUntil(config, id, () => request)
if (depsOptimizer && !config.optimizeDeps.devScan) {
depsOptimizer.delayDepsOptimizerUntil(id, () => request)
}
})
}
Expand Down
94 changes: 2 additions & 92 deletions packages/vite/src/node/plugins/optimizedDeps.ts
Expand Up @@ -13,100 +13,10 @@ export const ERR_OUTDATED_OPTIMIZED_DEP = 'ERR_OUTDATED_OPTIMIZED_DEP'
const isDebug = process.env.DEBUG
const debug = createDebugger('vite:optimize-deps')

const runOptimizerIfIdleAfterMs = 100

interface RunProcessingInfo {
ids: { id: string; done: () => Promise<any> }[]
seenIds: Set<string>
workersSources: Set<string>
waitingOn: string | undefined
}

const runProcessingInfoMap = new WeakMap<ResolvedConfig, RunProcessingInfo>()

function initRunProcessingInfo(config: ResolvedConfig) {
config = config.mainConfig || config
const runProcessingInfo = {
ids: [],
seenIds: new Set<string>(),
workersSources: new Set<string>(),
waitingOn: undefined
}
runProcessingInfoMap.set(config, runProcessingInfo)
return runProcessingInfo
}

function getRunProcessingInfo(config: ResolvedConfig): RunProcessingInfo {
return (
runProcessingInfoMap.get(config.mainConfig || config) ??
initRunProcessingInfo(config)
)
}

export function registerWorkersSource(
config: ResolvedConfig,
id: string
): void {
const info = getRunProcessingInfo(config)
info.workersSources.add(id)
if (info.waitingOn === id) {
info.waitingOn = undefined
}
}

export function delayDepsOptimizerUntil(
config: ResolvedConfig,
id: string,
done: () => Promise<any>
): void {
const info = getRunProcessingInfo(config)
if (
!getDepsOptimizer(config)?.isOptimizedDepFile(id) &&
!info.seenIds.has(id)
) {
info.seenIds.add(id)
info.ids.push({ id, done })
runOptimizerWhenIdle(config)
}
}

function runOptimizerWhenIdle(config: ResolvedConfig) {
const info = getRunProcessingInfo(config)
if (!info.waitingOn) {
const next = info.ids.pop()
if (next) {
info.waitingOn = next.id
const afterLoad = () => {
info.waitingOn = undefined
if (info.ids.length > 0) {
runOptimizerWhenIdle(config)
} else if (!info.workersSources.has(next.id)) {
getDepsOptimizer(config)?.run()
}
}
next
.done()
.then(() => {
setTimeout(
afterLoad,
info.ids.length > 0 ? 0 : runOptimizerIfIdleAfterMs
)
})
.catch(afterLoad)
}
}
}

export function optimizedDepsPlugin(config: ResolvedConfig): Plugin {
return {
name: 'vite:optimized-deps',

buildStart() {
if (!config.isWorker) {
initRunProcessingInfo(config)
}
},

async resolveId(id) {
if (getDepsOptimizer(config)?.isOptimizedDepFile(id)) {
return id
Expand Down Expand Up @@ -174,7 +84,7 @@ export function optimizedDepsBuildPlugin(config: ResolvedConfig): Plugin {

buildStart() {
if (!config.isWorker) {
initRunProcessingInfo(config)
getDepsOptimizer(config)?.resetRegisteredIds()
}
},

Expand All @@ -185,7 +95,7 @@ export function optimizedDepsBuildPlugin(config: ResolvedConfig): Plugin {
},

transform(_code, id) {
delayDepsOptimizerUntil(config, id, async () => {
getDepsOptimizer(config)?.delayDepsOptimizerUntil(id, async () => {
await this.load({ id })
})
},
Expand Down
4 changes: 2 additions & 2 deletions packages/vite/src/node/plugins/worker.ts
Expand Up @@ -13,8 +13,8 @@ import {
parseRequest
} from '../utils'
import { onRollupWarning } from '../build'
import { getDepsOptimizer } from '../optimizer'
import { fileToUrl } from './asset'
import { registerWorkersSource } from './optimizedDeps'

interface WorkerCache {
// save worker all emit chunk avoid rollup make the same asset unique.
Expand Down Expand Up @@ -269,7 +269,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
: 'module'
const workerOptions = workerType === 'classic' ? '' : ',{type: "module"}'
if (isBuild) {
registerWorkersSource(config, id)
getDepsOptimizer(config)?.registerWorkersSource(id)
if (query.inline != null) {
const chunk = await bundleWorkerEntry(config, id, query)
// inline as blob data url
Expand Down
4 changes: 2 additions & 2 deletions packages/vite/src/node/plugins/workerImportMetaUrl.ts
Expand Up @@ -12,10 +12,10 @@ import {
parseRequest,
transformResult
} from '../utils'
import { getDepsOptimizer } from '../optimizer'
import type { WorkerType } from './worker'
import { WORKER_FILE_ID, workerFileToUrl } from './worker'
import { fileToUrl } from './asset'
import { registerWorkersSource } from './optimizedDeps'

const ignoreFlagRE = /\/\*\s*@vite-ignore\s*\*\//

Expand Down Expand Up @@ -123,7 +123,7 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin {

let url: string
if (isBuild) {
registerWorkersSource(config, id)
getDepsOptimizer(config)?.registerWorkersSource(id)
url = await workerFileToUrl(config, file, query)
} else {
url = await fileToUrl(cleanUrl(file), config, this)
Expand Down
2 changes: 2 additions & 0 deletions playground/optimize-deps/added-in-entries/index.js
@@ -0,0 +1,2 @@
// written in cjs, optimization should convert this to esm
module.exports = 'added-in-entries'
6 changes: 6 additions & 0 deletions playground/optimize-deps/added-in-entries/package.json
@@ -0,0 +1,6 @@
{
"name": "added-in-entries",
"private": true,
"version": "1.0.0",
"main": "index.js"
}
11 changes: 11 additions & 0 deletions playground/optimize-deps/entry.js
@@ -0,0 +1,11 @@
import msg from 'added-in-entries'

// This is an entry file that is added to optimizeDeps.entries
// When the deps aren't cached, these entries are also processed
// to discover dependencies in them. This should only be needed
// for code splitted sections that are commonly visited after
// first load where a full-reload wants to be avoided at the expense
// of extra processing on cold start. Another option is to add
// the missing dependencies to optimizeDeps.include directly

console.log(msg)
1 change: 1 addition & 0 deletions playground/optimize-deps/package.json
Expand Up @@ -22,6 +22,7 @@
"dep-with-builtin-module-cjs": "file:./dep-with-builtin-module-cjs",
"dep-with-builtin-module-esm": "file:./dep-with-builtin-module-esm",
"dep-with-dynamic-import": "file:./dep-with-dynamic-import",
"added-in-entries": "file:./added-in-entries",
"lodash-es": "^4.17.21",
"nested-exclude": "file:./nested-exclude",
"phoenix": "^1.6.10",
Expand Down
3 changes: 2 additions & 1 deletion playground/optimize-deps/vite.config.js
Expand Up @@ -33,7 +33,8 @@ module.exports = {
}
}
]
}
},
entries: ['entry.js']
},

build: {
Expand Down