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: supports cts and mts config #8729

Merged
merged 5 commits into from Jun 24, 2022
Merged
Show file tree
Hide file tree
Changes from 4 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
74 changes: 29 additions & 45 deletions packages/vite/src/node/config.ts
Expand Up @@ -37,7 +37,12 @@ import {
} from './utils'
import { resolvePlugins } from './plugins'
import type { ESBuildOptions } from './plugins/esbuild'
import { CLIENT_ENTRY, DEFAULT_ASSETS_RE, ENV_ENTRY } from './constants'
import {
CLIENT_ENTRY,
DEFAULT_ASSETS_RE,
DEFAULT_CONFIG_FILES,
ENV_ENTRY
} from './constants'
import type { InternalResolveOptions, ResolveOptions } from './plugins/resolve'
import { resolvePlugin } from './plugins/resolve'
import type { LogLevel, Logger } from './logger'
Expand Down Expand Up @@ -825,56 +830,20 @@ export async function loadConfigFromFile(
const getTime = () => `${(performance.now() - start).toFixed(2)}ms`

let resolvedPath: string | undefined
let isTS = false
let isESM = false
let dependencies: string[] = []

// check package.json for type: "module" and set `isMjs` to true
try {
const pkg = lookupFile(configRoot, ['package.json'])
if (pkg && JSON.parse(pkg).type === 'module') {
isESM = true
}
} catch (e) {}

if (configFile) {
// explicit config path is always resolved from cwd
resolvedPath = path.resolve(configFile)
isTS = configFile.endsWith('.ts')

if (configFile.endsWith('.mjs')) {
isESM = true
}
} else {
// implicit config file loaded from inline root (if present)
// otherwise from cwd
const jsconfigFile = path.resolve(configRoot, 'vite.config.js')
if (fs.existsSync(jsconfigFile)) {
resolvedPath = jsconfigFile
}

if (!resolvedPath) {
const mjsconfigFile = path.resolve(configRoot, 'vite.config.mjs')
if (fs.existsSync(mjsconfigFile)) {
resolvedPath = mjsconfigFile
isESM = true
}
}
for (const filename of DEFAULT_CONFIG_FILES) {
const filePath = path.resolve(configRoot, filename)
if (!fs.existsSync(filePath)) continue

if (!resolvedPath) {
const tsconfigFile = path.resolve(configRoot, 'vite.config.ts')
if (fs.existsSync(tsconfigFile)) {
resolvedPath = tsconfigFile
isTS = true
}
}

if (!resolvedPath) {
const cjsConfigFile = path.resolve(configRoot, 'vite.config.cjs')
if (fs.existsSync(cjsConfigFile)) {
resolvedPath = cjsConfigFile
isESM = false
}
resolvedPath = filePath
break
}
}

Expand All @@ -883,22 +852,37 @@ export async function loadConfigFromFile(
return null
}

let isESM = false
if (/\.m[jt]s$/.test(resolvedPath)) {
isESM = true
} else if (/\.c[jt]s$/.test(resolvedPath)) {
isESM = false
} else {
// check package.json for type: "module" and set `isESM` to true
try {
const pkg = lookupFile(configRoot, ['package.json'])
isESM = !!pkg && JSON.parse(pkg).type === 'module'
} catch (e) {}
}

try {
let userConfig: UserConfigExport | undefined

if (isESM) {
const fileUrl = pathToFileURL(resolvedPath)
const bundled = await bundleConfigFile(resolvedPath, true)
dependencies = bundled.dependencies

const isTS = /\.[cm]?ts$/.test(resolvedPath)
if (isTS) {
// before we can register loaders without requiring users to run node
// with --experimental-loader themselves, we have to do a hack here:
// bundle the config file w/ ts transforms first, write it to disk,
// load it with native Node ESM, then delete the file.
fs.writeFileSync(resolvedPath + '.js', bundled.code)
userConfig = (await dynamicImport(`${fileUrl}.js?t=${Date.now()}`))
fs.writeFileSync(resolvedPath + '.mjs', bundled.code)
userConfig = (await dynamicImport(`${fileUrl}.mjs?t=${Date.now()}`))
.default
fs.unlinkSync(resolvedPath + '.js')
fs.unlinkSync(resolvedPath + '.mjs')
debug(`TS + native esm config loaded in ${getTime()}`, fileUrl)
} else {
// using Function to avoid this from being compiled away by TS/Rollup
Expand Down
9 changes: 9 additions & 0 deletions packages/vite/src/node/constants.ts
Expand Up @@ -20,6 +20,15 @@ export const DEFAULT_EXTENSIONS = [
'.json'
]

export const DEFAULT_CONFIG_FILES = [
'vite.config.js',
'vite.config.mjs',
'vite.config.ts',
'vite.config.cjs',
'vite.config.mts',
'vite.config.cts'
]

export const JS_TYPES_RE = /\.(?:j|t)sx?$|\.mjs$/

export const OPTIMIZABLE_ENTRY_RE = /\.(?:m?js|ts)$/
Expand Down
16 changes: 16 additions & 0 deletions playground/resolve-config/__tests__/resolve-config.spec.ts
Expand Up @@ -49,4 +49,20 @@ describe.runIf(isBuild)('build', () => {
build('ts-module')
expect(getDistFile('ts-module', 'js')).toContain('console.log(true)')
})
it('loads vite.config.mts', () => {
build('mts')
expect(getDistFile('mts', 'mjs')).toContain('console.log(true)')
})
it('loads vite.config.mts with package#type module', () => {
build('mts-module')
expect(getDistFile('mts-module', 'js')).toContain('console.log(true)')
})
it('loads vite.config.cts', () => {
build('cts')
expect(getDistFile('cts', 'mjs')).toContain('console.log(true)')
})
it('loads vite.config.cts with package#type module', () => {
build('cts-module')
expect(getDistFile('cts-module', 'js')).toContain('console.log(true)')
})
})
12 changes: 9 additions & 3 deletions playground/resolve-config/__tests__/serve.ts
Expand Up @@ -5,7 +5,7 @@ import path from 'node:path'
import fs from 'fs-extra'
import { isBuild, rootDir } from '~utils'

const configNames = ['js', 'cjs', 'mjs', 'ts']
const configNames = ['js', 'cjs', 'mjs', 'ts', 'mts', 'cts']

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

await fs.copy(fromTestDir('root'), fromTestDir(configName))
await fs.rename(fromTestDir(configName, 'vite.config.js'), pathToConf)
await fs.rename(fromTestDir(configName, 'vite.config.ts'), pathToConf)

if (configName === 'cjs') {
if (['cjs', 'cts'].includes(configName)) {
const conf = await fs.readFile(pathToConf, 'utf8')
await fs.writeFile(
pathToConf,
conf.replace('export default', 'module.exports = ')
)
}

// Remove TS annotation for plain JavaScript file.
if (configName.endsWith('js')) {
const conf = await fs.readFile(pathToConf, 'utf8')
await fs.writeFile(pathToConf, conf.replace(': boolean', ''))
}

// copy directory and add package.json with "type": "module"
await fs.copy(fromTestDir(configName), fromTestDir(`${configName}-module`))
await fs.writeJSON(fromTestDir(`${configName}-module`, 'package.json'), {
Expand Down
@@ -1,5 +1,6 @@
const __CONFIG_LOADED__: boolean = true
export default {
define: { __CONFIG_LOADED__: true },
define: { __CONFIG_LOADED__ },
logLevel: 'silent',
build: {
minify: false,
Expand Down