-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Support for ESM config files (with "type": "module") #3677
Comments
Yes, we would be happy to accept a PR adding support for the ESM config file. tschaub@99f1a92 is a good start, but it will need some more work. In particular, we'll need to mention this feature in the documentation and add e2e test to prevent regressions (I think we can use |
If I can free up some time, I will try to tackle this. I started a new job so it has kept me very busy! This seems like a natural extension of my previous 2 contributions. Notes: Both static The trick is, we would have to use On top of that, I don't know if The last time I checked, it didn't work because Node's API for intercepting modules isn't yet stable. I wouldn't hold your breath waiting for me until you see at least a draft PR. It may be a while before I can start. If you have a more immediate need, you could consume the JavaScript API immediately.
I have a proof of concept published, but it is more complicated than anything that would be implemented in this repo (I tend to over build things). It was written before Karma natively supported Promises and before exception handling was improved, so the end result would need to change before you use it. |
I really need to stop saying this prematurely. See: #3679 |
Is there a workaround for this? |
Karma accepts config files with the If the project package.json has |
~20 lines quick fix NODE_OPTIONS="$NODE_OPTIONS -r $PWD/hook.cjs" karma start karma.config.mjs // hook.cjs
const { addHook } = require('pirates')
const revert = addHook(
(code, _filename) =>
code
.replace(/\nfunction parseConfig/, 'async function parseConfig')
.replace('require(configFilePath)', '(await import(configFilePath)).default'),
{
exts: ['.js'],
ignoreNodeModules: false,
matcher: filename => filename.endsWith('node_modules/karma/lib/config.js')
}
);
void revert |
Above require hook, force So
|
It appears that, even though it's not in the documentation, Karma can already take a config function whose return value is a Promise. That allows you to simply write // karma.conf.js
module.exports = function(config) {
return import("./karma.conf.mjs").then(val => config.set(val));
}
// karma.conf.mjs
import fooPlugin from "foo-plugin";
export default function(config) {
config.set({
...,
plugins: [fooPlugin]
});
} I needed this when I updated to Angular 13; why I needed it is kind of an epic tale. I'm using Karma with I figured out the solution above, to use Node's own MJS support to dynamically import the Karma config file, which needs to be able to somehow import the Webpack config file. By writing my actual Karma config in MJS, I can simply write |
This won't quite work, or at least it shouldn't (I admit, I haven't tried it). You are close. The value that Long Version // karma.conf.js
const path = require('path')
const absoluteModulePath = path.resolve('./karma.conf.mjs')
module.exports = function(config) {
// The default module loader only supports File and HTTP(S) urls. File paths
// without a protocol is an error.
return import('file:' + absoluteModulePath).then( function(retrievedModule) {
let defaultExport
// We are only interested in the default export, so we can keep this simple.
if (typeof retrievedModule?.default !== 'undefined') {
// The expectation is that `import()` will be used for JavaScript
// modules and that the result of this will always have a property named
// "default" regardless of whether it was a CommonJS module or an
// ECMAScript module.
defaultExport = retrievedModule.default
} else {
// If module promise details are such that CommonJS modules are not
// assigned to a "default" property on the module object, then this will
// handle that.
defaultExport = retrievedModule
}
if (typeof defaultExport === 'function') {
return defaultExport(config)
}
})
} Short version: // karma.conf.js
const path = require('path')
const absoluteModulePath = path.resolve('./karma.conf.mjs')
module.exports = function(config) {
return import('file:' + absoluteModulePath).then( function(retrievedModule) {
const defaultExport = (typeof retrievedModule?.default !== 'undefined') ? retrievedModule.default : retrievedModule
if (typeof defaultExport === 'function') {
return defaultExport(config)
}
})
} If you want to be extra cautious, handle errors (such as if the default export isn't a function). For https://nodejs.org/docs/latest-v12.x/api/esm.html#esm_import_expressions |
The downside to You can work around this by checking the path's extension and using the normal https://nodejs.org/docs/latest-v12.x/api/module.html#module_module_createrequire_filename |
It is documented in the public API page: Though it may be worth adding a link to it from the config file page, in the Overview section, just after the line that describes the export:
|
PR: #3733 |
Sorry I missed all the activity, I was already off the clock for the weekend. I forgot to come back after tweaking my setup and post what I actually wound up using: module.exports = function(config) {
return import("./karma.conf.mjs").then(mod => {
mod.default(config);
});
} That let me convert my old karma.config.js from /** @type {(config: import("karma").Config) => void} */
export default function(config) {
/** @type {import("karma").ConfigOptions} */
const karmaOptions = { ... };
config.set(karmaOptions);
} Obviously this isn't production-ready to work with any arbitrary config-file syntax supported by Karma, but it allowed me to migrate from the way I was already writing my config (in CJS) to ESM syntax with a tiny bit of boilerplate and very few changes otherwise. I'm not saying this is the right solution for Karma going forward but it should get people watching this issue up and running until official support comes along. |
Is there a way to do this without |
I tried patching if (typeof configFilePath === 'function') {
configModule = configFilePath;
} and it works! |
You only need to use parseConfig if you are using the public API. If you are using the CLI, then returning a promise as illustrated above should be all that is needed. My examples were lengthy, @thw0rted boiled it down to the most minimal version possible. The only change I would make is to return the result of module.exports = function(config) {
return import("./karma.conf.mjs").then(mod => {
return mod.default(config);
});
} or, since it is a single statement, we can take advantage of syntax built into arrow functions: module.exports = function(config) {
return import("./karma.conf.mjs").then(mod => mod.default(config));
} |
@npetruzzelli I need to run a |
Since you are using the public API, I recommend checking out the public API docs: http://karma-runner.github.io/6.3/dev/public-api.html#karmaconfig When using the public API, you will still need an intermediate file like the one described above: // karma.conf.js
module.exports = function(config) {
return import("./karma.conf.mjs").then(mod => mod.default(config));
} This is because, even if the files for your config and |
…amples (#95) * Add engine-specific 'start' commands for running integration tests/examples. In the root, you may now run `start-core`, `start-three`, or `start-babylon`. These all run webpack dev server, but will open relevant tabs for the package you have selected. This is either integration tests, or both integration tests and examples. More work is still required to integrate the integration test code/pages into webpack so they support hot reload This change also enables source maps for non-production builds. I've moved demo-credentials.js to the root of the repo. It is onerous for devs to insert what is likely the same cognito pool many places. I've created a fake API in webpack at /devConfig.json where webpack serves up the cognito pool and it is fetched via the samples. Ultimately I do thing we should serve up everything via webpack and probably HtmlWebpackPlugin I tried to change this to a module with type: module in package.json. If you are reading this, do not attempt this until Karma 7! It doesn't seem like it can work without some really hacky stuff and I could not get it to work even then: Support for ESM config files (with "type": "module") karma-runner/karma#3677. My workaround is to change demo-credentials.js to be node-style instead of a ESM. A release build won't build any of the examples. These don't work without webpack server because of file location and our devConfig endpoint, so I think it is confusing/misleading to add these to the build output. They wouldn't work anyway without cognito so I don't think this is so bad, but we need to figure out how to test these in CI and how to pull a sample cognito Id from github secrets/etc
For me that works:
|
Modules are not yet supported: karma-runner/karma#3677
This reduces the need for variation in eg. linting rules by making all JS files modules where possible. Note that Karma does not support this. See karma-runner/karma#3677.
This reduces the need for variation in eg. linting rules by making all JS files modules where possible. Note that Karma does not support this. See karma-runner/karma#3677.
This reduces the need for variation in eg. linting rules by making all JS files modules where possible. Note that Karma does not support this. See karma-runner/karma#3677.
If a project's
package.json
has"type": "module"
, Karma fails to load the config file. I'm wondering if there would be interest in supporting ESM config files in addition to CJS (and the other currently supported flavors).While this might not be complete or the desired solution, it looks like something like this could add ESM config support: tschaub@99f1a92
The text was updated successfully, but these errors were encountered: