Skip to content

Importing CommonJS module fails due to exports object having a null prototype #2761

@rimunroe

Description

@rimunroe

Describe the bug

I've been running into a problem which results in certain module imports failing with an error of "exports.hasOwnProperty is not a function". From what I can tell, this happens when the following conditions are met:

  • The evaluated code was generated by a version of TypeScript older than 4 (technically older than 4.0.0-dev.20200711) and used CommonJS as the target format
  • The evaluated code was generated from source which contained an export * from ... statement to re-export all the named exports from another module
  • The module is included in the deps.inline array of the Vitest config

I've learned the following things:

  • The error does not appear if the module is evaluated using Node directly. I think this is what Vitest essentially does by default, and is why using deps.inline causes the issue, but please correct me if I'm wrong
  • During the development of TypeScript 4, a PR, fix(helpers): Use hasOwnProperty.call microsoft/TypeScript#39537, was merge which altered the way TypeScript compiles import * from statements so that the generated code would use Object.prototype.hasOwnProperty.call to invoke the method rather than relying on exports having Object in its prototype chain. This fix made it into the 4.0.0-dev-20200711 nightly, and appears to be present in all subsequent versions
  • Vitest appears to use Object.create(null) to create its exports object

This bug took a fair amount of digging, and I only ran into it because I was dealing with another, much stranger bug where a CommonJS module from (an internal company) package generated with TypeScript 3.7 had all the exports (which were re-exported from other files) it was supposed to when evaluated in a browser (after being bundled by Vite), but when used in Vitest only had a subset of those exports available. (I'm still trying to figure out the reason for that difference so I can either figure out the right thing to do or file an issue.) That smelled like a resolution issue, so I tried using deps.inline, which lead me to the exports.hasOwnProperty problem. I edited the problematic code to use call to invoke the hasOwnProperty method on the exports object, and that made my tests pass.

As I mentioned earlier, newer versions of TypeScript shouldn't produce this issue. Sadly, I've run into code which can't be migrated to a newer version at the moment. Presumably there's other code out there written for Node which expected exports to have Object in its prototype chain. Both of these seem like good reasons to me that this is a bug which should be fixed. If others agree, I'd be happy to try and put together a PR to do so.

Reproduction

I created rimunroe/vitest-commonjs-exports-prototype-issue to demonstrate the issue. A summary of this issue is included in the readme along with steps to reproduce it.

System Info

System:
  OS: macOS 13.1
  CPU: (16) x64 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
  Memory: 26.67 GB / 64.00 GB
  Shell: 5.8.1 - /bin/zsh
Binaries:
  Node: 16.17.0 - ~/.nvm/versions/node/v16.17.0/bin/node
  npm: 8.15.0 - ~/.nvm/versions/node/v16.17.0/bin/npm
Browsers:
  Chrome: 109.0.5414.119
  Firefox: 109.0
  Safari: 16.2
npmPackages:
  vitest: 0.28.1 => 0.28.1

Used Package Manager

npm

Validations

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions