Reputation: 9133
I want to compile my TS project to both ESM and CJS. I've set up two tsconfig files to do that and so on.
One of the files tries to conditionally import packages, which is something that has to be done differently in each environment. This means I have to have two different versions of the file. But I want the rest of my code to be the same.
Is there a way I can make sure that when the ESM version imports the file, it will receive one version while the CJS version will receive another?
I'd like to minimize using bundlers and post-build tools and processes as much as possible.
To conditionally import module in CJS, you do the following:
// try-import-cjs.js
try {
var mod = require("something")
console.log("Imported successfully"
} catch (e) {
console.error("Package not found")
}
To reproduce the same behavior in ESM, I have to use a module-level await.
// try-import-esm.js
try {
var mod = await import("something")
console.log("Imported!")
} catch (e) {
console.error("Package not found")
}
try-import-esm.js
will only execute under ESM. A module-level await
is a syntax error in a CJS context, so two different files are needed.
I want the rest of my code to be the same. So only this file will be different. Unlike in the examples above, both would have the same name, e.g. try-import
.
In the TypeScript source code, I would write:
import {x} from "./try-import.js"
And this would import a different version of the file, depending on the compilation result being used.
The purpose of this whole thing is to automatically import and use any popular test frameworks the user has installed.
By saying 'conditional,' I mean that the package should be imported if it exists, but if it doesn't exist the program shouldn't crash. This should happen during the import itself.
However, I'm not asking about importing packages. I'm asking about TypeScript compilation.
Upvotes: 1
Views: 1636
Reputation: 135396
You can use conditional exports in your package.json
-
{
"exports": {
"import": "./index-module.mjs",
"require": "./index-require.cjs"
},
"type": "module"
}
When your package is included in ESM -
import * as MyPackage from "my-package" // index-module.mjs
When your package is include with CommonJS -
const MyPackage = require("my-package") // index-require.cjs
Make sure to read the docs, there's a lot of flexibility -
{
"exports": {
".": {
"import": "./index-module.mjs",
"require": "./index-require.cjs"
},
"./something": "./src/something.js",
"./components/*": {
"require": "./src/components/*.cjs",
"default": "./src/components/*.mjs"
}
},
"type": "module"
}
import * as MyPackage from "my-package"
// index-module.mjs
import something from "my-package/something"
// src/something.js
import LoginForm from "my-package/components/LoginForm"
// src/components/LoginForm.mjs
const MyPackage = require("my-package") // index-require.cjs
const something = require("my-package/something")
// src/something.js
const LoginForm = require("my-package/components/LoginForm")
// src/components/LoginForm.cjs
Upvotes: 2