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

ESM Config File Support #3679

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Changes from all 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
69 changes: 61 additions & 8 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,12 @@ const CONFIG_SYNTAX_HELP = ' module.exports = function(config) {\n' +
' });\n' +
' };\n'

const ESM_CONFIG_SYNTAX_HELP = ' export default function(config) {\n' +
' config.set({\n' +
' // your config\n' +
' });\n' +
' };\n'

/**
* Retrieve a parsed and finalized Karma `Config` instance. This `karmaConfig`
* object may be used to configure public API methods such a `Server`,
Expand Down Expand Up @@ -429,9 +435,42 @@ function parseConfig (configFilePath, cliOptions, parseOptions) {
if (path.extname(configFilePath) === '.ts' && TYPE_SCRIPT_AVAILABLE) {
require('ts-node').register()
}
configModule = require(configFilePath)
if (typeof configModule === 'object' && typeof configModule.default !== 'undefined') {
configModule = configModule.default
try {
configModule = require(configFilePath)
if (typeof configModule === 'object' && typeof configModule.default !== 'undefined') {
configModule = configModule.default
}
} catch (requireException) {
if (requireException.code === 'ERR_REQUIRE_ESM') {
if (promiseConfig === true) {
configModule = import(configFilePath).then(
function onRetrieveKarmaConfigFulfilled (retrievedModule) {
if (typeof retrievedModule.default === 'undefined') {
// Use an error similar to what you'd get for trying to import
// default from within another ES Module.
const err = new SyntaxError(`The requested module '${configFilePath}' does not provide an export named 'default'`)
return fail('Error in config file!\n ' + err.stack || err)
}
if (!helper.isFunction(retrievedModule.default)) {
return fail("Config file's default export must be a function!\n" + ESM_CONFIG_SYNTAX_HELP)
}
return retrievedModule.default
},
function onRetrieveKarmaConfigRejected (reason) {
return fail('Error in config file!\n ' + reason.stack || reason)
})
} else {
return fail(
// 'You must enable promises and error throwing to use ES Modules' +
'You must enable promises to use ES Modules' +
' as config files.\n' +
'Example: parseConfig(path, cliOptions, { promiseConfig: true,' +
' throwErrors: true })'
)
}
} else {
throw requireException
}
}
} catch (e) {
const extension = path.extname(configFilePath)
Expand All @@ -444,7 +483,9 @@ function parseConfig (configFilePath, cliOptions, parseOptions) {
}
return fail('Error in config file!\n ' + e.stack || e)
}
if (!helper.isFunction(configModule)) {
// The config module will only be a promise if it passed the earlier check
// for promiseConfig === true.
if (!helper.isFunction(configModule) && !(configModule instanceof Promise)) {
return fail('Config file must export a function!\n' + CONFIG_SYNTAX_HELP)
}
} else {
Expand All @@ -464,10 +505,22 @@ function parseConfig (configFilePath, cliOptions, parseOptions) {
config.set(cliOptions)

let configModuleReturn
try {
configModuleReturn = configModule(config)
} catch (e) {
return fail('Error in config file!\n', e)
if (configModule instanceof Promise) {
configModuleReturn = configModule.then((esmConfigModule) => {
let esmConfigModuleReturn
try {
esmConfigModuleReturn = esmConfigModule(config)
} catch (esmConfigModuleException) {
return fail('Error in config file!\n', esmConfigModuleException)
}
return esmConfigModuleReturn
})
} else {
try {
configModuleReturn = configModule(config)
} catch (e) {
return fail('Error in config file!\n', e)
}
}
function finalizeConfig (config) {
// merge the config from config file and cliOptions (precedence)
Expand Down