Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/babel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,24 @@ defineRolldownBabelPreset({

When running without Vite (pure Rolldown), `applyToEnvironmentHook` is ignored.

### `optimizeDeps`

A preset can declare dependencies that should be pre-bundled by Vite's dependency optimizer. The plugin automatically merges these into `optimizeDeps.include` in the Vite config.

```js
defineRolldownBabelPreset({
preset: ['@babel/preset-react'],
rolldown: {
filter: { id: /\.[jt]sx$/ },
optimizeDeps: {
include: ['react', 'react-dom'],
},
},
})
```

When running without Vite (pure Rolldown), `optimizeDeps` is ignored.

### How preset filters work

Preset filters operate at two levels:
Expand Down
69 changes: 68 additions & 1 deletion packages/babel/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as babel from '@babel/core'
import { rolldown, type OutputChunk } from 'rolldown'
import { build as viteBuild, createBuilder, type Rollup } from 'vite'
import path from 'node:path'
import type { PluginOptions } from './options.ts'
import { collectOptimizeDepsInclude, type PluginOptions } from './options.ts'
import type { RolldownBabelPreset } from './rolldownPreset.ts'

test('plugin works', async () => {
Expand Down Expand Up @@ -570,6 +570,73 @@ test('babel syntax error produces enhanced error message', async () => {
`)
})

describe('optimizeDeps.include', () => {
test('collectOptimizeDepsInclude merges from presets and overrides', () => {
const topPreset: RolldownBabelPreset = {
preset: '@babel/preset-react',
rolldown: {
optimizeDeps: { include: ['react'] },
},
}
const overridePreset: RolldownBabelPreset = {
preset: '@babel/preset-react',
rolldown: {
optimizeDeps: { include: ['react-dom'] },
},
}
const result = collectOptimizeDepsInclude({
presets: [topPreset],
overrides: [{ presets: [overridePreset] }],
})
expect(result).toEqual(['react', 'react-dom'])
})

test('collectOptimizeDepsInclude skips plain babel presets', () => {
const result = collectOptimizeDepsInclude({
presets: ['@babel/preset-env'],
})
expect(result).toEqual([])
})

test('collectOptimizeDepsInclude returns empty for no optimizeDeps', () => {
const preset: RolldownBabelPreset = {
preset: '@babel/preset-react',
rolldown: {},
}
const result = collectOptimizeDepsInclude({ presets: [preset] })
expect(result).toEqual([])
})

test('config hook returns optimizeDeps.include via Vite build', async () => {
const preset: RolldownBabelPreset = {
preset: (): babel.InputOptions => ({ plugins: [] }),
rolldown: {
optimizeDeps: { include: ['react', 'react-dom'] },
},
}
let receivedOptimizeDeps: any
await viteBuild({
configFile: false,
logLevel: 'silent',
plugins: [
{
name: 'capture-config',
configResolved(config) {
receivedOptimizeDeps = config.optimizeDeps
},
},
babelPlugin({ presets: [preset] }),
],
build: {
write: false,
minify: false,
rollupOptions: { input: 'foo.js' },
},
}).catch(() => {})
expect(receivedOptimizeDeps?.include).toEqual(expect.arrayContaining(['react', 'react-dom']))
})
})

async function buildWithVite(
filename: string,
code: string,
Expand Down
7 changes: 7 additions & 0 deletions packages/babel/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Plugin, type HookFilter, type SourceMapInput } from 'rolldown'
import {
collectOptimizeDepsInclude,
createBabelOptionsConverter,
filterPresetsWithConfigResolved,
filterPresetsWithEnvironment,
Expand All @@ -19,6 +20,12 @@ async function babelPlugin(rawOptions: PluginOptions): Promise<Plugin> {
name: '@rolldown/plugin-babel',
// this plugin should run before TS, JSX, TSX transformations are done
enforce: 'pre',
config() {
const include = collectOptimizeDepsInclude(rawOptions)
if (include.length > 0) {
return { optimizeDeps: { include } }
}
},
configResolved(config: ResolvedConfig) {
configFilteredOptions = filterPresetsWithConfigResolved(rawOptions, config)
const resolved = resolveOptions(configFilteredOptions)
Expand Down
17 changes: 17 additions & 0 deletions packages/babel/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,23 @@ export function filterPresetsWithConfigResolved(
}
}

export function collectOptimizeDepsInclude(options: PluginOptions): string[] {
const result: string[] = []
for (const preset of options.presets ?? []) {
if (typeof preset === 'object' && 'rolldown' in preset) {
result.push(...(preset.rolldown.optimizeDeps?.include ?? []))
}
}
for (const override of options.overrides ?? []) {
for (const preset of override.presets ?? []) {
if (typeof preset === 'object' && 'rolldown' in preset) {
result.push(...(preset.rolldown.optimizeDeps?.include ?? []))
}
}
}
return result
}

/**
* Pre-compile all preset filters and return a function that
* converts options to babel options for a given context.
Expand Down
3 changes: 3 additions & 0 deletions packages/babel/src/rolldownPreset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ export type RolldownBabelPreset = {
moduleType?: ModuleTypeFilter
code?: GeneralHookFilter
}
optimizeDeps?: {
include?: string[]
}
applyToEnvironmentHook?: (environment: PartialEnvironment) => boolean
configResolvedHook?: (config: ResolvedConfig) => boolean
}
Expand Down
Loading