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

Error when using fully-qualified ESM imports with TypeScript #26827

Closed
nwalters512 opened this issue May 22, 2023 · 6 comments
Closed

Error when using fully-qualified ESM imports with TypeScript #26827

nwalters512 opened this issue May 22, 2023 · 6 comments
Assignees

Comments

@nwalters512
Copy link

Current behavior

I have a fully-ESM project. I have a file boolean.ts like the following:

export const TRUE = true;

I try to import a value into one of my Cypress test files with an import like the following:

import { TRUE } from '../lib/boolean.js';

This fails with the following error:

Module not found: Error: Can't resolve '../lib/boolean.js' in '/Users/nathan/git/cypress-esm-fully-qualified-import-repro/cypress/e2e'
Looked for and couldn't find the file at the following paths:
[/Users/nathan/git/cypress-esm-fully-qualified-import-repro/cypress/lib/boolean.js]
[/Users/nathan/git/cypress-esm-fully-qualified-import-repro/cypress/lib/boolean.js.js]
[/Users/nathan/git/cypress-esm-fully-qualified-import-repro/cypress/lib/boolean.js.json]
[/Users/nathan/git/cypress-esm-fully-qualified-import-repro/cypress/lib/boolean.js.jsx]
[/Users/nathan/git/cypress-esm-fully-qualified-import-repro/cypress/lib/boolean.js.mjs]
[/Users/nathan/git/cypress-esm-fully-qualified-import-repro/cypress/lib/boolean.js.coffee]
[/Users/nathan/git/cypress-esm-fully-qualified-import-repro/cypress/lib/boolean.js.ts]
[/Users/nathan/git/cypress-esm-fully-qualified-import-repro/cypress/lib/boolean.js.tsx]

Desired behavior

Cypress should support TypeScript-style fully-qualified ESM imports where one can import a .ts file by using a .js extension. In the context of this particular example, import ... from './modules.js should import module.ts.

Test code to reproduce

https://github.com/nwalters512/cypress-esm-fully-qualified-import-repro. Ignore the fact that defining and importing a TRUE constant isn't a real-world scenario 😉

Cypress Version

12.12.0

Node version

18.12.0

Operating System

macOS 13.3.1

Debug Logs

N/A

Other

I believe webpack's resolve.extensionAlias will be useful here. The example from their docs is probably pretty close to what you want:

module.exports = {
  //...
  resolve: {
    extensionAlias: {
      '.js': ['.ts', '.js'],
      '.mjs': ['.mts', '.mjs'],
    },
  },
};
@mike-plummer
Copy link
Contributor

Interesting - just for my curiosity is there a particular advantage/reason to prefer using a *.js import over the *.ts?

It looks like the resolve.extensionAlias was added fairly recently (v5.74.0 I think) but we still need to support Webpack 4 which makes this a bit tricky. There's more context in #19555 around the status of that upgrade.

There are a couple things you could try:

  1. If you can find an equivalent of resolve.extensionAlias that works in v4 we could look at introducing that
  2. You could use @cypress/webpack-preprocessor to force Cypress to resolve Webpack 5 and provide an appropriate config for that version - a search should turn up a couple examples of projects doing that including ours.
  3. There are plugins that try to use alternative bundlers like esbuild which may have baked-in support for your use case

@lmiller1990
Copy link
Contributor

If he is using fully ESM you need to type .js in the import, ESM does not understanding extensionless modules.

@nwalters512
Copy link
Author

@mike-plummer Using .js is generally necessary for ESM code that's transpiled with TypeScript. ESM at runtime must have extensions for imports, and at runtime, files will have .js extensions. This is documented and recommended by TypeScript itself: https://www.typescriptlang.org/docs/handbook/esm-node.html

Regarding your suggestions:

  1. https://www.npmjs.com/package/resolve-typescript-plugin appears to offer equivalent functionality to resolve.extensionAlias. Its README also offers a nice overview about why this kind of thing is necessary.
  2. I'm 99% sure that overwriting Module._load doesn't work in ESM.
  3. This is intriguing! I may give it a try.

@lmiller1990
Copy link
Contributor

lmiller1990 commented Jun 28, 2023

I think this is probably your best option:

I You could use @cypress/webpack-preprocessor to force Cypress to resolve Webpack 5 and provide an appropriate config for that version - a search should turn up a couple examples of projects doing that including ours.

You could grab the latest one of @cypress/webpack-preprocessor and configure it. Our defaults are here. If you use ts-loader which uses ts-node under the hood, it should detect your tsconfig.json and the version TS you've got and it'll work as expected. Basically, Cypress doesn't do anything specific around imports / transpiles, we just grab your webpack / config and run all the files through that. If the config understands the js / ts extension interop, it will work.

When we update to webpack 5, this will likely "just work", but that isn't something that'll be landing prior to a major, since it's a pretty large and potentially breaking change.

@jennifer-shehane
Copy link
Member

Closing since we upgraded to Webpack 5 and this should be resolved.

@mukulgupta21
Copy link

This still doesn't work with cypress 13. Here is a test code to reproduce: https://github.com/mukulgupta21/cypress-ts-issue
What are my options? Shall I open a new ticket?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants