Skip to content

Commit

Permalink
feat: supports cts and mts config (#8729)
Browse files Browse the repository at this point in the history
  • Loading branch information
sxzz authored Jun 24, 2022
1 parent 3271266 commit c2b09db
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 52 deletions.
80 changes: 32 additions & 48 deletions packages/vite/src/node/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
dynamicImport,
isExternalUrl,
isObject,
isTS,
lookupFile,
mergeAlias,
mergeConfig,
Expand All @@ -37,7 +38,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 +831,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 +853,36 @@ 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
if (isTS) {

if (isTS(resolvedPath)) {
// 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 Expand Up @@ -972,7 +956,7 @@ async function bundleConfigFile(
{
name: 'inject-file-scope-variables',
setup(build) {
build.onLoad({ filter: /\.[jt]s$/ }, async (args) => {
build.onLoad({ filter: /\.[cm]?[jt]s$/ }, async (args) => {
const contents = await fs.promises.readFile(args.path, 'utf8')
const injectValues =
`const __dirname = ${JSON.stringify(path.dirname(args.path))};` +
Expand All @@ -982,7 +966,7 @@ async function bundleConfigFile(
)};`

return {
loader: args.path.endsWith('.ts') ? 'ts' : 'js',
loader: isTS(args.path) ? 'ts' : 'js',
contents: injectValues + contents
}
})
Expand Down
9 changes: 9 additions & 0 deletions packages/vite/src/node/constants.ts
Original file line number Diff line number Diff line change
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
2 changes: 2 additions & 0 deletions packages/vite/src/node/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1056,3 +1056,5 @@ export function stripBomTag(content: string): string {

return content
}

export const isTS = (filename: string): boolean => /\.[cm]?ts$/.test(filename)
16 changes: 16 additions & 0 deletions playground/resolve-config/__tests__/resolve-config.spec.ts
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit c2b09db

Please sign in to comment.