diff --git a/docs/config/index.md b/docs/config/index.md index 3dac90dc386d06..bc78daeae653d5 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -22,8 +22,10 @@ You can also explicitly specify a config file to use with the `--config` CLI opt vite --config my-config.js ``` -::: tip BUNDLING THE CONFIG -By default, Vite uses `esbuild` to bundle the config into a temporary file. This can cause issues when importing TypeScript files in a monorepo. If you encounter any issues with this approach, you can specify `--configLoader=runner` to use the module runner instead - it will not create a temporary config and will transform any files on the fly. Note that module runner doesn't support CJS in config files, but external CJS packages should work as usual. +::: tip CONFIG LOADING +By default, Vite uses `esbuild` to bundle the config into a temporary file and load it. This may cause issues when importing TypeScript files in a monorepo. If you encounter any issues with this approach, you can specify `--configLoader runner` to use the [module runner](/guide/api-environment-runtimes.html#modulerunner) instead, which will not create a temporary config and will transform any files on the fly. Note that module runner doesn't support CJS in config files, but external CJS packages should work as usual. + +Alternatively, if you're using an environment that supports TypeScript (e.g. `node --experimental-strip-types`), or if you're only writing plain JavaScript, you can specify `--configLoader native` to use the environment's native runtime to load the config file. Note that updates to modules imported by the config file are not detected and hence would not auto-restart the Vite server. ::: ## Config Intellisense diff --git a/docs/guide/cli.md b/docs/guide/cli.md index faaeefa18cb20f..55555f3b6bfacd 100644 --- a/docs/guide/cli.md +++ b/docs/guide/cli.md @@ -14,25 +14,25 @@ vite [root] #### Options -| Options | | -| ------------------------- | ---------------------------------------------------------------------------------------------------------------------- | -| `--host [host]` | Specify hostname (`string`) | -| `--port ` | Specify port (`number`) | -| `--open [path]` | Open browser on startup (`boolean \| string`) | -| `--cors` | Enable CORS (`boolean`) | -| `--strictPort` | Exit if specified port is already in use (`boolean`) | -| `--force` | Force the optimizer to ignore the cache and re-bundle (`boolean`) | -| `-c, --config ` | Use specified config file (`string`) | -| `--base ` | Public base path (default: `/`) (`string`) | -| `-l, --logLevel ` | info \| warn \| error \| silent (`string`) | -| `--clearScreen` | Allow/disable clear screen when logging (`boolean`) | -| `--configLoader ` | Use `bundle` to bundle the config with esbuild or `runner` (experimental) to process it on the fly (default: `bundle`) | -| `--profile` | Start built-in Node.js inspector (check [Performance bottlenecks](/guide/troubleshooting#performance-bottlenecks)) | -| `-d, --debug [feat]` | Show debug logs (`string \| boolean`) | -| `-f, --filter ` | Filter debug logs (`string`) | -| `-m, --mode ` | Set env mode (`string`) | -| `-h, --help` | Display available CLI options | -| `-v, --version` | Display version number | +| Options | | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `--host [host]` | Specify hostname (`string`) | +| `--port ` | Specify port (`number`) | +| `--open [path]` | Open browser on startup (`boolean \| string`) | +| `--cors` | Enable CORS (`boolean`) | +| `--strictPort` | Exit if specified port is already in use (`boolean`) | +| `--force` | Force the optimizer to ignore the cache and re-bundle (`boolean`) | +| `-c, --config ` | Use specified config file (`string`) | +| `--base ` | Public base path (default: `/`) (`string`) | +| `-l, --logLevel ` | info \| warn \| error \| silent (`string`) | +| `--clearScreen` | Allow/disable clear screen when logging (`boolean`) | +| `--configLoader ` | Use `bundle` to bundle the config with esbuild, or `runner` (experimental) to process it on the fly, or `native` (experimental) to load using the native runtime (default: `bundle`) | +| `--profile` | Start built-in Node.js inspector (check [Performance bottlenecks](/guide/troubleshooting#performance-bottlenecks)) | +| `-d, --debug [feat]` | Show debug logs (`string \| boolean`) | +| `-f, --filter ` | Filter debug logs (`string`) | +| `-m, --mode ` | Set env mode (`string`) | +| `-h, --help` | Display available CLI options | +| `-v, --version` | Display version number | ## Build diff --git a/packages/vite/src/node/cli.ts b/packages/vite/src/node/cli.ts index aeaaafb697b3b3..c9aa1202d43ca9 100644 --- a/packages/vite/src/node/cli.ts +++ b/packages/vite/src/node/cli.ts @@ -23,7 +23,7 @@ interface GlobalCLIOptions { l?: LogLevel logLevel?: LogLevel clearScreen?: boolean - configLoader?: 'bundle' | 'runner' + configLoader?: 'bundle' | 'runner' | 'native' d?: boolean | string debug?: boolean | string f?: string @@ -155,7 +155,7 @@ cli .option('--clearScreen', `[boolean] allow/disable clear screen when logging`) .option( '--configLoader ', - `[string] use 'bundle' to bundle the config with esbuild or 'runner' (experimental) to process it on the fly (default: bundle)`, + `[string] use 'bundle' to bundle the config with esbuild, or 'runner' (experimental) to process it on the fly, or 'native' (experimental) to load using the native runtime (default: bundle)`, ) .option('-d, --debug [feat]', `[string | boolean] show debug logs`) .option('-f, --filter ', `[string] filter debug logs`) diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 00444570cbfdea..74e4015b689bf1 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -546,7 +546,7 @@ export interface ResolvedWorkerOptions { export interface InlineConfig extends UserConfig { configFile?: string | false /** @experimental */ - configLoader?: 'bundle' | 'runner' + configLoader?: 'bundle' | 'runner' | 'native' envFile?: false forceOptimizeDeps?: boolean } @@ -1696,15 +1696,19 @@ export async function loadConfigFromFile( configRoot: string = process.cwd(), logLevel?: LogLevel, customLogger?: Logger, - configLoader: 'bundle' | 'runner' = 'bundle', + configLoader: 'bundle' | 'runner' | 'native' = 'bundle', ): Promise<{ path: string config: UserConfig dependencies: string[] } | null> { - if (configLoader !== 'bundle' && configLoader !== 'runner') { + if ( + configLoader !== 'bundle' && + configLoader !== 'runner' && + configLoader !== 'native' + ) { throw new Error( - `Unsupported configLoader: ${configLoader}. Accepted values are 'bundle' and 'runner'.`, + `Unsupported configLoader: ${configLoader}. Accepted values are 'bundle', 'runner', and 'native'.`, ) } @@ -1735,7 +1739,11 @@ export async function loadConfigFromFile( try { const resolver = - configLoader === 'bundle' ? bundleAndLoadConfigFile : importConfigFile + configLoader === 'bundle' + ? bundleAndLoadConfigFile + : configLoader === 'runner' + ? runnerImportConfigFile + : nativeImportConfigFile const { configExport, dependencies } = await resolver(resolvedPath) debug?.(`config file loaded in ${getTime()}`) @@ -1762,7 +1770,17 @@ export async function loadConfigFromFile( } } -async function importConfigFile(resolvedPath: string) { +async function nativeImportConfigFile(resolvedPath: string) { + const module = await import( + pathToFileURL(resolvedPath).href + '?t=' + Date.now() + ) + return { + configExport: module.default, + dependencies: [], + } +} + +async function runnerImportConfigFile(resolvedPath: string) { const { module, dependencies } = await runnerImport<{ default: UserConfigExport }>(resolvedPath) diff --git a/playground/config/__tests__/config.spec.ts b/playground/config/__tests__/config.spec.ts index 0addd3929f5244..ff8d4a29136a9c 100644 --- a/playground/config/__tests__/config.spec.ts +++ b/playground/config/__tests__/config.spec.ts @@ -1,6 +1,6 @@ import { resolve } from 'node:path' import { loadConfigFromFile } from 'vite' -import { expect, it } from 'vitest' +import { describe, expect, it } from 'vitest' const [nvMajor, nvMinor] = process.versions.node.split('.').map(Number) const isImportAttributesSupported = @@ -48,3 +48,24 @@ it.runIf(isImportAttributesSupported)( `) }, ) + +describe('loadConfigFromFile with configLoader: native', () => { + const fixtureRoot = resolve(import.meta.dirname, '../packages/native-import') + + it('imports a basic js config', async () => { + const result = await loadConfigFromFile( + {} as any, + resolve(fixtureRoot, 'basic.js'), + fixtureRoot, + undefined, + undefined, + 'native', + ) + expect(result.config).toMatchInlineSnapshot(` + { + "value": "works", + } + `) + expect(result.dependencies.length).toBe(0) + }) +}) diff --git a/playground/config/packages/native-import/basic.js b/playground/config/packages/native-import/basic.js new file mode 100644 index 00000000000000..1ebdccdb9aa628 --- /dev/null +++ b/playground/config/packages/native-import/basic.js @@ -0,0 +1,3 @@ +export default { + value: 'works', +}