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

The esModuleInterop and module importing is inconsistent (and mandatory?) with tsx #388

Closed
3 of 5 tasks
anodynos opened this issue Nov 14, 2023 · 2 comments
Closed
3 of 5 tasks
Labels
bug Something isn't working outdated pending triage

Comments

@anodynos
Copy link

Precheck

  • I searched existing issues before opening this one to avoid duplicates
  • I'm able to reproduce this issue and prove it with a minimal reproduction
  • I understand this is not a place to ask for free debugging support

Problem

I have a file

// @ts-ignore
import deepmergeAsDefault from 'deepmerge'         // < ===  BREAKS IN TypeScript, I have esModuleInterop: false

import * as deepmerge from 'deepmerge'
import * as _ from 'lodash'

console.log(_.merge({ lodashA: 1 }, { lodashB: 2 }))              // < ===  WORKS
console.log(deepmergeAsDefault({ deepmergeAsDefaultA: 1 }, { deepmergeAsDefaultB: 2 })) // < ===  WORKS

console.log(deepmerge({ deepmergeA: 1 }, { deepmergeB: 2 }))        // < ===  BREAKS

I have a // @ts-ignore on top of the default import, since TypeScript complains, rightly so since my esModuleInterop: false (the default):

TS1259: Module
'//wsl.localhost/ubuntara/mnt/projects/devzen-tools/packages/speczen/node_modules/deepmerge/index'
can only be default-imported using the  esModuleInterop  flag
index.d.ts(20, 1): This module is declared with  export = , and can only be used with a default import when using the  esModuleInterop  flag

When I execute I get:

╰─$ tsx temp/import-without-default-import.ts                                                                                                                                                                
{
  detail: undefined,
  id: 'call-import-namespace',
  location: {
    column: 12,
    file: '/mnt/projects/devzen-tools/packages/speczen/temp/import-without-default-import.ts',
    length: 9,
    line: 8,
    lineText: 'console.log(deepmerge({ deepmergeA: 1 }, { deepmergeB: 2 }))',
    namespace: '',
    suggestion: ''
  },
  notes: [
    {
      location: [Object],
      text: 'Consider changing "deepmerge" to a default import instead:'
    },
    {
      location: null,
      text: `Make sure to enable TypeScript's "esModuleInterop" setting so that TypeScript's type checker generates an error when you try to do this. You can read more about this setting here: https://www.typescriptlang.org/tsconfig#esModuleInterop`
    }
  ],
  pluginName: '',
  text: `Calling "deepmerge" will crash at run-time because it's an import namespace object, not a function`
}
{ lodashA: 1, lodashB: 2 }
{ deepmergeAsDefaultA: 1, deepmergeAsDefaultB: 2 }
/mnt/projects/devzen-tools/packages/speczen/temp/import-without-default-import.ts:8
console.log(deepmerge({ deepmergeA: 1 }, { deepmergeB: 2 }))
            ^


TypeError: deepmerge is not a function
    at deepmergeAsDefault (/mnt/projects/devzen-tools/packages/speczen/temp/import-without-default-import.ts:8:13)
    at Object.<anonymous> (/mnt/projects/devzen-tools/packages/speczen/temp/import-without-default-import.ts:8:60)
    at Module._compile (node:internal/modules/cjs/loader:1241:14)
    at Object.j (/home/anodynos/.asdf/installs/nodejs/20.8.0/lib/node_modules/tsx/dist/cjs/index.cjs:1:1197)
    at Module.load (node:internal/modules/cjs/loader:1091:32)
    at Module._load (node:internal/modules/cjs/loader:938:12)
    at cjsLoader (node:internal/modules/esm/translators:284:17)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:234:7)
    at ModuleJob.run (node:internal/modules/esm/module_job:217:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:316:24)

Node.js v20.8.0

if you noticed,

{ lodashA: 1, lodashB: 2 }
{ deepmergeAsDefaultA: 1, deepmergeAsDefaultB: 2 }

worked, which is incosistent. I imported lodash normally (for my esModuleInterop: false) and deepmerge in an illegal way for my project, and these are the 2 that worked.

But the legit deepmerge({ deepmergeA: 1 }, { deepmergeB: 2 }) broke.

In ts-node

import * as deepmerge from 'deepmerge'
import * as _ from 'lodash'

work as expected (and the deepmergeAsDefault breaks as should).

All my project is using non-default imports, and I don't want to enable esModuleInterop cause some library was breaking some time ago, and all my project is using the non-esModuleInterop any way and works just fine with all other tooling, including ts-node, jest with ts-jest and as compiled js code.

Is there any flag I'm missing that can accommodate this?

Expected behavior

To work like it does with all other tooling and respecting my ts-config.

Minimal reproduction URL

https://stackblitz.com/edit/node-34fss6?file=index.ts

Version

v3.14.0

Node.js version

20.8.0

Package manager

npm

Operating system

Linux

Contributions

  • I plan to open a pull request for this issue
  • I plan to make a financial contribution to this project
@anodynos anodynos added bug Something isn't working pending triage labels Nov 14, 2023
@saltman424
Copy link

saltman424 commented Feb 21, 2024

@anodynos are you running as an ES module or CommonJS module?

I am encountering the same issue, and I believe in my case it is the same issue as #38. Specifically, with "type": "module" in my package.json, if I run a script that imports from helper.cts, I get the error mentioned in #38: SyntaxError: The requested module './helper.cjs' does not provide an export named 'x'. However, if I change the import to treat it similar to as if esModuleInterop was set to true, even though it is false in my tsconfig.json, then tsx works fine. Of course, TypeScript/VSCode complain since it is not valid code with esModuleInterop as false.

@privatenumber you would probably have a better sense if these issues are in fact linked.

Examples

Should Work

// helper.cts
export function myFunction() { return 'success' }

// script.ts
import { myFunction } from './helper.cjs'

console.log(myFunction())

Result:

import{myFunction}from"./helper.cjs";console.log(myFunction());
       ^^^^^^^^^^
SyntaxError: The requested module './helper.cjs' does not provide an export named 'myFunction'

Shouldn't Work

// helper.cts
export function myFunction() { return 'success' }

// script.ts
import { default as helper } from './helper.cjs'

console.log(helper.myFunction())

image

Result:

success

@privatenumber
Copy link
Owner

tsx doesn't look at esModuleInterop

Closing as a duplicate of #38

@privatenumber privatenumber closed this as not planned Won't fix, can't repro, duplicate, stale Feb 22, 2024
@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 26, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working outdated pending triage
Projects
None yet
Development

No branches or pull requests

3 participants