Skip to content

Commit c2b09db

Browse files
authored
feat: supports cts and mts config (#8729)
1 parent 3271266 commit c2b09db

File tree

6 files changed

+70
-52
lines changed

6 files changed

+70
-52
lines changed

packages/vite/src/node/config.ts

+32-48
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
dynamicImport,
3030
isExternalUrl,
3131
isObject,
32+
isTS,
3233
lookupFile,
3334
mergeAlias,
3435
mergeConfig,
@@ -37,7 +38,12 @@ import {
3738
} from './utils'
3839
import { resolvePlugins } from './plugins'
3940
import type { ESBuildOptions } from './plugins/esbuild'
40-
import { CLIENT_ENTRY, DEFAULT_ASSETS_RE, ENV_ENTRY } from './constants'
41+
import {
42+
CLIENT_ENTRY,
43+
DEFAULT_ASSETS_RE,
44+
DEFAULT_CONFIG_FILES,
45+
ENV_ENTRY
46+
} from './constants'
4147
import type { InternalResolveOptions, ResolveOptions } from './plugins/resolve'
4248
import { resolvePlugin } from './plugins/resolve'
4349
import type { LogLevel, Logger } from './logger'
@@ -825,56 +831,20 @@ export async function loadConfigFromFile(
825831
const getTime = () => `${(performance.now() - start).toFixed(2)}ms`
826832

827833
let resolvedPath: string | undefined
828-
let isTS = false
829-
let isESM = false
830834
let dependencies: string[] = []
831835

832-
// check package.json for type: "module" and set `isMjs` to true
833-
try {
834-
const pkg = lookupFile(configRoot, ['package.json'])
835-
if (pkg && JSON.parse(pkg).type === 'module') {
836-
isESM = true
837-
}
838-
} catch (e) {}
839-
840836
if (configFile) {
841837
// explicit config path is always resolved from cwd
842838
resolvedPath = path.resolve(configFile)
843-
isTS = configFile.endsWith('.ts')
844-
845-
if (configFile.endsWith('.mjs')) {
846-
isESM = true
847-
}
848839
} else {
849840
// implicit config file loaded from inline root (if present)
850841
// otherwise from cwd
851-
const jsconfigFile = path.resolve(configRoot, 'vite.config.js')
852-
if (fs.existsSync(jsconfigFile)) {
853-
resolvedPath = jsconfigFile
854-
}
855-
856-
if (!resolvedPath) {
857-
const mjsconfigFile = path.resolve(configRoot, 'vite.config.mjs')
858-
if (fs.existsSync(mjsconfigFile)) {
859-
resolvedPath = mjsconfigFile
860-
isESM = true
861-
}
862-
}
842+
for (const filename of DEFAULT_CONFIG_FILES) {
843+
const filePath = path.resolve(configRoot, filename)
844+
if (!fs.existsSync(filePath)) continue
863845

864-
if (!resolvedPath) {
865-
const tsconfigFile = path.resolve(configRoot, 'vite.config.ts')
866-
if (fs.existsSync(tsconfigFile)) {
867-
resolvedPath = tsconfigFile
868-
isTS = true
869-
}
870-
}
871-
872-
if (!resolvedPath) {
873-
const cjsConfigFile = path.resolve(configRoot, 'vite.config.cjs')
874-
if (fs.existsSync(cjsConfigFile)) {
875-
resolvedPath = cjsConfigFile
876-
isESM = false
877-
}
846+
resolvedPath = filePath
847+
break
878848
}
879849
}
880850

@@ -883,22 +853,36 @@ export async function loadConfigFromFile(
883853
return null
884854
}
885855

856+
let isESM = false
857+
if (/\.m[jt]s$/.test(resolvedPath)) {
858+
isESM = true
859+
} else if (/\.c[jt]s$/.test(resolvedPath)) {
860+
isESM = false
861+
} else {
862+
// check package.json for type: "module" and set `isESM` to true
863+
try {
864+
const pkg = lookupFile(configRoot, ['package.json'])
865+
isESM = !!pkg && JSON.parse(pkg).type === 'module'
866+
} catch (e) {}
867+
}
868+
886869
try {
887870
let userConfig: UserConfigExport | undefined
888871

889872
if (isESM) {
890873
const fileUrl = pathToFileURL(resolvedPath)
891874
const bundled = await bundleConfigFile(resolvedPath, true)
892875
dependencies = bundled.dependencies
893-
if (isTS) {
876+
877+
if (isTS(resolvedPath)) {
894878
// before we can register loaders without requiring users to run node
895879
// with --experimental-loader themselves, we have to do a hack here:
896880
// bundle the config file w/ ts transforms first, write it to disk,
897881
// load it with native Node ESM, then delete the file.
898-
fs.writeFileSync(resolvedPath + '.js', bundled.code)
899-
userConfig = (await dynamicImport(`${fileUrl}.js?t=${Date.now()}`))
882+
fs.writeFileSync(resolvedPath + '.mjs', bundled.code)
883+
userConfig = (await dynamicImport(`${fileUrl}.mjs?t=${Date.now()}`))
900884
.default
901-
fs.unlinkSync(resolvedPath + '.js')
885+
fs.unlinkSync(resolvedPath + '.mjs')
902886
debug(`TS + native esm config loaded in ${getTime()}`, fileUrl)
903887
} else {
904888
// using Function to avoid this from being compiled away by TS/Rollup
@@ -972,7 +956,7 @@ async function bundleConfigFile(
972956
{
973957
name: 'inject-file-scope-variables',
974958
setup(build) {
975-
build.onLoad({ filter: /\.[jt]s$/ }, async (args) => {
959+
build.onLoad({ filter: /\.[cm]?[jt]s$/ }, async (args) => {
976960
const contents = await fs.promises.readFile(args.path, 'utf8')
977961
const injectValues =
978962
`const __dirname = ${JSON.stringify(path.dirname(args.path))};` +
@@ -982,7 +966,7 @@ async function bundleConfigFile(
982966
)};`
983967

984968
return {
985-
loader: args.path.endsWith('.ts') ? 'ts' : 'js',
969+
loader: isTS(args.path) ? 'ts' : 'js',
986970
contents: injectValues + contents
987971
}
988972
})

packages/vite/src/node/constants.ts

+9
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ export const DEFAULT_EXTENSIONS = [
2020
'.json'
2121
]
2222

23+
export const DEFAULT_CONFIG_FILES = [
24+
'vite.config.js',
25+
'vite.config.mjs',
26+
'vite.config.ts',
27+
'vite.config.cjs',
28+
'vite.config.mts',
29+
'vite.config.cts'
30+
]
31+
2332
export const JS_TYPES_RE = /\.(?:j|t)sx?$|\.mjs$/
2433

2534
export const OPTIMIZABLE_ENTRY_RE = /\.(?:m?js|ts)$/

packages/vite/src/node/utils.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1056,3 +1056,5 @@ export function stripBomTag(content: string): string {
10561056

10571057
return content
10581058
}
1059+
1060+
export const isTS = (filename: string): boolean => /\.[cm]?ts$/.test(filename)

playground/resolve-config/__tests__/resolve-config.spec.ts

+16
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,20 @@ describe.runIf(isBuild)('build', () => {
4949
build('ts-module')
5050
expect(getDistFile('ts-module', 'js')).toContain('console.log(true)')
5151
})
52+
it('loads vite.config.mts', () => {
53+
build('mts')
54+
expect(getDistFile('mts', 'mjs')).toContain('console.log(true)')
55+
})
56+
it('loads vite.config.mts with package#type module', () => {
57+
build('mts-module')
58+
expect(getDistFile('mts-module', 'js')).toContain('console.log(true)')
59+
})
60+
it('loads vite.config.cts', () => {
61+
build('cts')
62+
expect(getDistFile('cts', 'mjs')).toContain('console.log(true)')
63+
})
64+
it('loads vite.config.cts with package#type module', () => {
65+
build('cts-module')
66+
expect(getDistFile('cts-module', 'js')).toContain('console.log(true)')
67+
})
5268
})

playground/resolve-config/__tests__/serve.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import path from 'node:path'
55
import fs from 'fs-extra'
66
import { isBuild, rootDir } from '~utils'
77

8-
const configNames = ['js', 'cjs', 'mjs', 'ts']
8+
const configNames = ['js', 'cjs', 'mjs', 'ts', 'mts', 'cts']
99

1010
export async function serve() {
1111
if (!isBuild) return
@@ -18,16 +18,22 @@ export async function serve() {
1818
const pathToConf = fromTestDir(configName, `vite.config.${configName}`)
1919

2020
await fs.copy(fromTestDir('root'), fromTestDir(configName))
21-
await fs.rename(fromTestDir(configName, 'vite.config.js'), pathToConf)
21+
await fs.rename(fromTestDir(configName, 'vite.config.ts'), pathToConf)
2222

23-
if (configName === 'cjs') {
23+
if (['cjs', 'cts'].includes(configName)) {
2424
const conf = await fs.readFile(pathToConf, 'utf8')
2525
await fs.writeFile(
2626
pathToConf,
2727
conf.replace('export default', 'module.exports = ')
2828
)
2929
}
3030

31+
// Remove TS annotation for plain JavaScript file.
32+
if (configName.endsWith('js')) {
33+
const conf = await fs.readFile(pathToConf, 'utf8')
34+
await fs.writeFile(pathToConf, conf.replace(': boolean', ''))
35+
}
36+
3137
// copy directory and add package.json with "type": "module"
3238
await fs.copy(fromTestDir(configName), fromTestDir(`${configName}-module`))
3339
await fs.writeJSON(fromTestDir(`${configName}-module`, 'package.json'), {

playground/resolve-config/root/vite.config.js playground/resolve-config/root/vite.config.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
const __CONFIG_LOADED__: boolean = true
12
export default {
2-
define: { __CONFIG_LOADED__: true },
3+
define: { __CONFIG_LOADED__ },
34
logLevel: 'silent',
45
build: {
56
minify: false,

0 commit comments

Comments
 (0)