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

feat: non-blocking esbuild optimization at build time #8280

Merged
merged 58 commits into from May 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
305eefc
feat: non-blocking needs interop
patak-dev Apr 1, 2022
bfff06a
chore: update jsxLoader hint
patak-dev Apr 2, 2022
19e7168
fix: avoid the breaking change
patak-dev Apr 2, 2022
5837ce9
fix: handle edge case and simplify
patak-dev Apr 3, 2022
5291442
chore: merge main
patak-dev May 9, 2022
628399e
chore: merge main
patak-dev May 9, 2022
29ee41e
chore: merge main
patak-dev May 11, 2022
9199c63
chore: merge main
patak-dev May 12, 2022
2530ea6
chore: merge main
patak-dev May 13, 2022
3b58a43
feat: build time esbuild based deps optimization
patak-dev May 21, 2022
5f3cd98
chore: lint, remove unused import
patak-dev May 21, 2022
92b0609
chore: merge main
patak-dev May 22, 2022
fbd4c0f
feat: scan free one pass optimization during build
patak-dev May 22, 2022
c7f4485
chore: merge main
patak-dev May 22, 2022
027b50f
fix: handle dynamic import
patak-dev May 22, 2022
40e0a26
chore: merge main
patak-dev May 22, 2022
a635b41
chore: merge feat/non-blocking-need-es-interop
patak-dev May 22, 2022
f9d8e41
refactor: simplify for non-blocking needInterop
patak-dev May 22, 2022
386391c
test: skip three non-supported tests
patak-dev May 22, 2022
dcd3b5e
chore: merge main
patak-dev May 23, 2022
14b70f7
fix: optimizeDeps.include support
patak-dev May 23, 2022
f7488f6
chore: merge main
patak-dev May 23, 2022
8dc6def
chore: format
patak-dev May 23, 2022
ff76fbb
chore: merge main
patak-dev May 23, 2022
8093499
test: try to exclude external component
patak-dev May 23, 2022
0872fe4
chore: fix ssr-vue test
bluwy May 24, 2022
1c38395
chore: update ssr-react tests
bluwy May 24, 2022
fc47ae8
feat: improve run optimizer when iddle logic
patak-dev May 24, 2022
fd4c815
test: revert skip for resolve test
patak-dev May 24, 2022
51e4c97
feat: shared optimizedDeps for worker builds
patak-dev May 24, 2022
d5fdaf9
feat: don't block deps processing in workers sources
patak-dev May 24, 2022
63b62bb
chore: merge feat/non-blocking-need-es-interop
patak-dev May 24, 2022
58cbd2c
test: don't skip ssr-vue /external test
patak-dev May 24, 2022
7216c98
fix: build
patak-dev May 24, 2022
46f5d8b
test: add nested-deps, but skip test-package-e-included
patak-dev May 24, 2022
ec453fb
chore: don't use commonjs plugins in workers
patak-dev May 24, 2022
9f2f6e9
test: update nested-deps include/exclude config
patak-dev May 24, 2022
04c2a65
test: exclude doesn't support nested, thanks @bluwy
patak-dev May 24, 2022
3e6e54e
chore: merge main
patak-dev May 24, 2022
7ed333e
chore: TODO, skip nested exclude test
patak-dev May 24, 2022
9cdc833
feat: support watch mode
patak-dev May 24, 2022
216290e
chore: format
patak-dev May 24, 2022
d08b766
Merge branch 'main' into feat/non-blocking-need-es-interop
patak-dev May 24, 2022
b88a120
chore: clean up
patak-dev May 25, 2022
8ba5f2e
fix: optimize-missing-deps, remove skip for darwin
patak-dev May 25, 2022
95aac5e
chore: lint
patak-dev May 25, 2022
76a2015
test: skip test again in darwin, seems Vitest has an issue here in CI
patak-dev May 25, 2022
ef14106
Merge branch 'feat/non-blocking-need-es-interop' into feat/non-blocki…
antfu May 25, 2022
bbe6903
chore: fix typo
patak-dev May 25, 2022
72b1f04
chore: clean up
patak-dev May 25, 2022
889257e
chore: runOptimizerWhenIdle clean up
patak-dev May 25, 2022
2a9801e
chore: name constant delay time
patak-dev May 25, 2022
809fdb8
chore: clean up
patak-dev May 25, 2022
8133edc
chore: merge main
patak-dev May 25, 2022
e7cac66
refactor: optimizedDeps -> depsOptimizer
patak-dev May 26, 2022
ec1a639
chore: lint
patak-dev May 26, 2022
6bdd7e1
refactor: avoid config and server dep in resolvePlugin
patak-dev May 26, 2022
af443a9
refactor: optimizeDeps.disabled: boolean | 'dev' | 'build'
patak-dev May 26, 2022
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
13 changes: 8 additions & 5 deletions packages/plugin-react/src/index.ts
Expand Up @@ -2,7 +2,6 @@ import path from 'path'
import type { ParserOptions, TransformOptions, types as t } from '@babel/core'
import * as babel from '@babel/core'
import { createFilter } from '@rollup/pluginutils'
import resolve from 'resolve'
import { normalizePath } from 'vite'
import type { Plugin, PluginOption, ResolvedConfig } from 'vite'
import {
Expand Down Expand Up @@ -362,7 +361,7 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
}
}

const runtimeId = 'react/jsx-runtime'
// const runtimeId = 'react/jsx-runtime'
// Adapted from https://github.com/alloc/vite-react-jsx
const viteReactJsx: Plugin = {
name: 'vite:react-jsx',
Expand All @@ -373,10 +372,14 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
include: ['react/jsx-dev-runtime']
}
}
},
}
// TODO: this optimization may not be necesary and it is breacking esbuild+rollup compat,
// see https://github.com/vitejs/vite/pull/7246#discussion_r861552185
// We could still do the same trick and resolve to the optimized dependency here
/*
resolveId(id: string) {
return id === runtimeId ? id : null
},
},
load(id: string) {
if (id === runtimeId) {
const runtimePath = resolve.sync(runtimeId, {
Expand All @@ -391,7 +394,7 @@ export default function viteReact(opts: Options = {}): PluginOption[] {
...exports.map((name) => `export const ${name} = jsxRuntime.${name}`)
].join('\n')
}
}
} */
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aleclarson we plan to merge this PR leaving this as a TODO, I think it is a good idea to revisit it and try to add back the optimization. I would prefer we could do something more generic here but I see the appeal to at least doing it for the jsx runtime that is used everywhere in the app

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did you try merging #7246 into this PR to see if your issue can be avoided?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, but I was looking into that PR while checking this code. If you have time to try it out, it would be good. If not, I prefer we move forward without the optimization to be able to merge these PRs and we can add it back with #7246 or a similar PR 👍🏼

}

return [viteBabel, viteReactRefresh, useAutomaticRuntime && viteReactJsx]
Expand Down
16 changes: 13 additions & 3 deletions packages/vite/src/node/build.ts
Expand Up @@ -22,7 +22,7 @@ import type { RollupCommonJSOptions } from 'types/commonjs'
import type { RollupDynamicImportVarsOptions } from 'types/dynamicImportVars'
import type { TransformOptions } from 'esbuild'
import type { InlineConfig, ResolvedConfig } from './config'
import { resolveConfig } from './config'
import { isDepsOptimizerEnabled, resolveConfig } from './config'
import { buildReporterPlugin } from './plugins/reporter'
import { buildEsbuildPlugin } from './plugins/esbuild'
import { terserPlugin } from './plugins/terser'
Expand All @@ -34,7 +34,11 @@ import { buildImportAnalysisPlugin } from './plugins/importAnalysisBuild'
import { resolveSSRExternal, shouldExternalizeForSSR } from './ssr/ssrExternal'
import { ssrManifestPlugin } from './ssr/ssrManifestPlugin'
import type { DepOptimizationMetadata } from './optimizer'
import { findKnownImports, getDepsCacheDir } from './optimizer'
import {
findKnownImports,
getDepsCacheDir,
initDepsOptimizer
} from './optimizer'
import { assetImportMetaUrlPlugin } from './plugins/assetImportMetaUrl'
import { loadFallbackPlugin } from './plugins/loadFallback'
import type { PackageData } from './packages'
Expand Down Expand Up @@ -283,7 +287,9 @@ export function resolveBuildPlugins(config: ResolvedConfig): {
pre: [
...(options.watch ? [ensureWatchPlugin()] : []),
watchPackageDataPlugin(config),
commonjsPlugin(options.commonjsOptions),
...(!isDepsOptimizerEnabled(config) || options.ssr
? [commonjsPlugin(options.commonjsOptions)]
: []),
dataURIPlugin(),
assetImportMetaUrlPlugin(config),
...(options.rollupOptions.plugins
Expand Down Expand Up @@ -390,6 +396,10 @@ async function doBuild(
)
}

if (isDepsOptimizerEnabled(config) && !ssr) {
await initDepsOptimizer(config)
}

const rollupOptions: RollupOptions = {
input,
context: 'globalThis',
Expand Down
6 changes: 6 additions & 0 deletions packages/vite/src/node/cli.ts
Expand Up @@ -25,6 +25,7 @@ interface GlobalCLIOptions {
filter?: string
m?: string
mode?: string
force?: boolean
}

/**
Expand Down Expand Up @@ -152,6 +153,10 @@ cli
)
.option('--manifest [name]', `[boolean | string] emit build manifest json`)
.option('--ssrManifest [name]', `[boolean | string] emit ssr manifest json`)
.option(
'--force',
`[boolean] force the optimizer to ignore the cache and re-bundle (experimental)`
)
.option(
'--emptyOutDir',
`[boolean] force empty outDir when it's outside of root`
Expand All @@ -169,6 +174,7 @@ cli
configFile: options.config,
logLevel: options.logLevel,
clearScreen: options.clearScreen,
force: options.force,
build: buildOptions
})
} catch (e) {
Expand Down
15 changes: 15 additions & 0 deletions packages/vite/src/node/config.ts
Expand Up @@ -144,6 +144,11 @@ export interface UserConfig {
* Preview specific options, e.g. host, port, https...
*/
preview?: PreviewOptions
/**
* Force dep pre-optimization regardless of whether deps have changed.
* @experimental
*/
force?: boolean
bluwy marked this conversation as resolved.
Show resolved Hide resolved
/**
* Dep optimization options
*/
Expand Down Expand Up @@ -855,3 +860,13 @@ async function loadConfigFromBundledFile(
_require.extensions[extension] = defaultLoader
return config
}

export function isDepsOptimizerEnabled(config: ResolvedConfig) {
const { command, optimizeDeps } = config
const { disabled } = optimizeDeps
return !(
disabled === true ||
(command === 'build' && disabled === 'build') ||
(command === 'serve' && optimizeDeps.disabled === 'dev')
)
}
2 changes: 1 addition & 1 deletion packages/vite/src/node/index.ts
Expand Up @@ -36,7 +36,7 @@ export type {
DepOptimizationResult,
DepOptimizationProcessing,
OptimizedDepInfo,
OptimizedDeps,
DepsOptimizer,
ExportsData
} from './optimizer'
export type { Plugin } from './plugin'
Expand Down
65 changes: 45 additions & 20 deletions packages/vite/src/node/optimizer/index.ts
Expand Up @@ -22,6 +22,7 @@ import {
import { transformWithEsbuild } from '../plugins/esbuild'
import { esbuildDepPlugin } from './esbuildDepPlugin'
import { scanImports } from './scan'
export { initDepsOptimizer, getDepsOptimizer } from './optimizer'

export const debuggerViteDeps = createDebugger('vite:deps')
const debug = debuggerViteDeps
Expand All @@ -38,10 +39,15 @@ export type ExportsData = ReturnType<typeof parse> & {
jsxLoader?: true
}

export interface OptimizedDeps {
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
options: DepOptimizationOptions
}

export interface DepOptimizationOptions {
Expand Down Expand Up @@ -107,11 +113,13 @@ export interface DepOptimizationOptions {
*/
extensions?: string[]
/**
* Disables dependencies optimizations
* Disables dependencies optimizations, true disables the optimizer during
* build and dev. Pass 'build' or 'dev' to only disable the optimizer in
* one of the modes. Deps optimization is enabled by default in both
* @default false
* @experimental
*/
disabled?: boolean
disabled?: boolean | 'build' | 'dev'
}

export interface DepOptimizationResult {
Expand Down Expand Up @@ -184,7 +192,7 @@ export interface DepOptimizationMetadata {
*/
export async function optimizeDeps(
config: ResolvedConfig,
force = config.server.force,
force = config.force,
asCommand = false
): Promise<DepOptimizationMetadata> {
const log = asCommand ? config.logger.info : debug
Expand All @@ -209,7 +217,7 @@ export async function optimizeDeps(
return result.metadata
}

export function createOptimizedDepsMetadata(
export function initDepsOptimizerMetadata(
config: ResolvedConfig,
timestamp?: string
): DepOptimizationMetadata {
Expand Down Expand Up @@ -240,7 +248,7 @@ export function addOptimizedDepInfo(
*/
export function loadCachedDepOptimizationMetadata(
config: ResolvedConfig,
force = config.server.force,
force = config.force,
asCommand = false
): DepOptimizationMetadata | undefined {
const log = asCommand ? config.logger.info : debug
Expand All @@ -257,7 +265,7 @@ export function loadCachedDepOptimizationMetadata(
let cachedMetadata: DepOptimizationMetadata | undefined
try {
const cachedMetadataPath = path.join(depsCacheDir, '_metadata.json')
cachedMetadata = parseOptimizedDepsMetadata(
cachedMetadata = parseDepsOptimizerMetadata(
fs.readFileSync(cachedMetadataPath, 'utf-8'),
depsCacheDir
)
Expand Down Expand Up @@ -301,6 +309,21 @@ export async function discoverProjectDependencies(
)
}

return initialProjectDependencies(config, timestamp, deps)
}

/**
* Create the initial discovered deps list. At build time we only
* have the manually included deps. During dev, a scan phase is
* performed and knownDeps is the list of discovered deps
*/
export async function initialProjectDependencies(
config: ResolvedConfig,
timestamp?: string,
knownDeps?: Record<string, string>
): Promise<Record<string, OptimizedDepInfo>> {
const deps: Record<string, string> = knownDeps ?? {}

await addManuallyIncludedOptimizeDeps(deps, config)

const browserHash = getOptimizedBrowserHash(
Expand Down Expand Up @@ -342,16 +365,16 @@ export function depsLogString(qualifiedIds: string[]): string {
* the metadata and start the server without waiting for the optimizeDeps processing to be completed
*/
export async function runOptimizeDeps(
config: ResolvedConfig,
resolvedConfig: ResolvedConfig,
depsInfo: Record<string, OptimizedDepInfo>
): Promise<DepOptimizationResult> {
config = {
...config,
const config: ResolvedConfig = {
...resolvedConfig,
command: 'build'
}

const depsCacheDir = getDepsCacheDir(config)
const processingCacheDir = getProcessingDepsCacheDir(config)
const depsCacheDir = getDepsCacheDir(resolvedConfig)
const processingCacheDir = getProcessingDepsCacheDir(resolvedConfig)

// Create a temporal directory so we don't need to delete optimized deps
// until they have been processed. This also avoids leaving the deps cache
Expand All @@ -369,7 +392,7 @@ export async function runOptimizeDeps(
JSON.stringify({ type: 'module' })
)

const metadata = createOptimizedDepsMetadata(config)
const metadata = initDepsOptimizerMetadata(config)

metadata.browserHash = getOptimizedBrowserHash(
metadata.hash,
Expand Down Expand Up @@ -493,7 +516,7 @@ export async function runOptimizeDeps(
const id = path
.relative(processingCacheDirOutputPath, o)
.replace(jsExtensionRE, '')
const file = getOptimizedDepPath(id, config)
const file = getOptimizedDepPath(id, resolvedConfig)
if (
!findOptimizedDepInfoInRecord(
metadata.optimized,
Expand All @@ -511,7 +534,7 @@ export async function runOptimizeDeps(
}

const dataPath = path.join(processingCacheDir, '_metadata.json')
writeFile(dataPath, stringifyOptimizedDepsMetadata(metadata, depsCacheDir))
writeFile(dataPath, stringifyDepsOptimizerMetadata(metadata, depsCacheDir))

debug(`deps bundled in ${(performance.now() - start).toFixed(2)}ms`)

Expand All @@ -532,7 +555,7 @@ async function addManuallyIncludedOptimizeDeps(
): Promise<void> {
const include = config.optimizeDeps?.include
if (include) {
const resolve = config.createResolver({ asSrc: false })
const resolve = config.createResolver({ asSrc: false, scan: true })
for (const id of include) {
// normalize 'foo >bar` as 'foo > bar' to prevent same id being added
// and for pretty printing
Expand Down Expand Up @@ -575,11 +598,13 @@ export function getOptimizedDepPath(id: string, config: ResolvedConfig) {
}

export function getDepsCacheDir(config: ResolvedConfig) {
return normalizePath(path.resolve(config.cacheDir, 'deps'))
const dirName = config.command === 'build' ? 'depsBuild' : 'deps'
return normalizePath(path.resolve(config.cacheDir, dirName))
}

function getProcessingDepsCacheDir(config: ResolvedConfig) {
return normalizePath(path.resolve(config.cacheDir, 'processing'))
const dirName = config.command === 'build' ? 'processingBuild' : 'processing'
return normalizePath(path.resolve(config.cacheDir, dirName))
}

export function isOptimizedDepFile(id: string, config: ResolvedConfig) {
Expand All @@ -605,7 +630,7 @@ export function createIsOptimizedDepUrl(config: ResolvedConfig) {
}
}

function parseOptimizedDepsMetadata(
function parseDepsOptimizerMetadata(
jsonMetadata: string,
depsCacheDir: string
): DepOptimizationMetadata | undefined {
Expand Down Expand Up @@ -659,7 +684,7 @@ function parseOptimizedDepsMetadata(
* the next time the server start we need to use the global
* browserHash to allow long term caching
*/
function stringifyOptimizedDepsMetadata(
function stringifyDepsOptimizerMetadata(
metadata: DepOptimizationMetadata,
depsCacheDir: string
) {
Expand Down