`async import` of typescript file doesn't work from cjs
Environment
Node 18 jiti 1.21.0
Reproduction
- Create two files
test.cjs
(async function foo() {
await import('./typescript');
})();
typescript.ts
console.log('TYPESCRIPT FILE')
- Run
npx jiti@latest test.cjs
Describe the bug
Trying to import *.ts file using async import from cjs file fails with:
node:internal/errors:496
ErrorCaptureStackTrace(err);
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/.../typescript' imported from /.../test.cjs
It works if the test.cjs file is renamed to test.mjs
Additional context
Note that the actual case where I've noticed it is a lot less contrived. I found this issue by trying to configure @nuxtjs/sentry using a file that the module needed to import.
So I've set the serverConfig: '~/config/sentry.server' option which the Sentry module then tries to import using async import. The issue is that that sentry module is imported through the CJS export rather than MJS so then it fails. (note how exports are defined in the module: https://github.com/nuxt-community/sentry-module/blob/ad424f10763d5c08826e250f715ebde699d4edfa/package.json#L19-L27).
Logs
No response
Thanks for reporting.
Minimal reproduction: https://stackblitz.com/edit/stackblitz-starters-3fj5rs?file=package.json
It seems transformation is not happening because the file has an explicit .cjs extension and also no ESM syntax (dynamic import is valid in Node.js CJS environment) and therefore we use native. (src)
I think we can also enable jiti transforms when in cjs, dynamic import syntax is detected but it adds overhead for all current .cjs files that go through and don't require it. Perhaps we can enable this behavior behind a flag so you can enable for sentry module importer?
How would I enable the flag in that case, given that the code looks like this?
https://github.com/nuxt-community/sentry-module/blob/977a74e7b28f360116514258adcf8cbcec2b5be1/src/options.ts#L243-L245
Note that this code is triggered by Nuxt when it loads the Sentry module (and uses CJS export by default).
You need an explicit jiti instance in place of await import(). Also consider that it is only happening for stub mode when module is not transformed yet that uses implicit jiti for dynamic imports.
By "stub" do you mean the mode used during development only? Because that's not the case here.
Please have a look at dist/module.cjs at https://www.npmjs.com/package/@nuxtjs/sentry?activeTab=code . This is the file that Nuxt loads.
(Sorry if I'm saying obvious things. Just clarifying in case I misunderstood).
With all that said, I'm contemplating just forcing mjs for the module like:
diff --git a/package.json b/package.json
index 3fcd235..733a350 100755
--- a/package.json
+++ b/package.json
@@ -17,13 +17,7 @@
"registry": "https://registry.npmjs.org"
},
"type": "module",
- "exports": {
- ".": {
- "import": "./dist/module.mjs",
- "require": "./dist/module.cjs"
- }
- },
- "main": "./dist/module.cjs",
+ "main": "./dist/module.mjs",
"types": "./dist/module.d.ts",
"files": [
"dist"
which I feel like would be perfectly fine. At least for versions of Nuxt that use jiti but that's probably reasonable to expect everyone using.
Yep, I got it. Regardless I think it would be always much much safer if in this line you expect resolvedPath pointing to a possibly Typescript entry, to use an explicit jiti.import() and ensures doing transforms and i expect Nuxt uses native ESM for loading module (which in Node.js has no TS support)
Would love to hear your feedback about doing it :)
I've tried quickly and that seems to work too. Though I'm really tempted to just forcing Nuxt to load the module using MJS since that's so much cleaner...
If nuxt uses MJS/Native ESM, i think this issue is irrelevant no? Because mainly we want typescript support from what i understand and it is possible when jiti transform happens. (we do transform dynamic import in native ESM files when passed to jiti only because we have do transform anyway as currently jiti is based on CJS. Is is a coincidence that works with ESM!)
Depending on Nuxt behavior that might use jiti implicitly for ESM files, is not sure assumption and can be broken in the future.
(update: reading above seems confusing explanation haha feel free to DM me if you have questions!)
If I'd want to do everything by the book then how would I initialize jiti in an ESM context?
jiti documentation says to use __filename:
const jiti = require("jiti")(__filename);
which would technically work when running in Nuxt since it seems to "polyfill" that but if I'd assume that I'm running in a native ESM context then it wouldn't be available.
This should work in ESM context (PR welcome for docs!)
import initJiti from 'jiti'
const jiti = initJiti(import.meta.url)
console.log(jiti.resolve('.'))
(docs updated)