Skip to content

Commit

Permalink
feat: backport supports cts and mts config (#8729)
Browse files Browse the repository at this point in the history
PR for v3: #8729
  • Loading branch information
sxzz committed Jun 24, 2022
1 parent 1afc1c2 commit f6f2f44
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 52 deletions.
12 changes: 9 additions & 3 deletions packages/playground/resolve-config/__tests__/serve.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const { testDir } = require('../../testUtils')

const fromTestDir = (/** @type{string[]} */ ...p) => path.resolve(testDir, ...p)

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

/** @param {string} root @param {boolean} isProd */
exports.serve = async function serve(root, isProd) {
Expand All @@ -20,16 +20,22 @@ exports.serve = async function serve(root, isProd) {
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
80 changes: 32 additions & 48 deletions packages/vite/src/node/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,18 @@ import {
dynamicImport,
isExternalUrl,
isObject,
isTS,
lookupFile,
normalizePath
} 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 @@ -863,56 +869,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 @@ -921,22 +891,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 = require('url').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 @@ -1006,10 +990,10 @@ async function bundleConfigFile(
{
name: 'replace-import-meta',
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')
return {
loader: args.path.endsWith('.ts') ? 'ts' : 'js',
loader: isTS(args.path) ? 'ts' : 'js',
contents: contents
.replace(
/\bimport\.meta\.url\b/g,
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 @@ -15,6 +15,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 @@ -842,3 +842,5 @@ function gracefulRemoveDir(
export function emptyCssComments(raw: string) {
return raw.replace(multilineCommentsRE, (s) => ' '.repeat(s.length))
}

export const isTS = (filename: string): boolean => /\.[cm]?ts$/.test(filename)

0 comments on commit f6f2f44

Please sign in to comment.