Skip to content

commonjs wildcard imports only expose enumerable properties of requirees' exports #1778

@dbjorge

Description

@dbjorge

Describe the bug

_interopRequireWildcard's implementation works by using a for(var key in obj) loop over the required module's export to build up a new object with the same keys. This strategy does not cooperate with modules whose exports expose other types of capabilities. For example, it does not support commonjs modules whose exports are:

  • Proxy objects which support get operations of dynamic, non-enumerable properties (eg, identity-obj-proxy)
  • Classes which support being constructed (eg, enzyme-adapter-react-16)

The specific motivating module in our case is identity-obj-proxy, which the Jest docs suggest as the recommended way to mock CSS modules. Its implementation is essentially:

idObj = new Proxy({}, {
  get: function getter(target, key) {
    if (key === '__esModule') {
      return false;
    }
    return key;
  }
});

module.exports = idObj;

We found this issue while experimenting with migrating an existing React codebase (microsoft/accessibility-insights-web) from ts-jest to @swc/jest. Our project uses identity-obj-proxy per the Jest docs' suggestion of how to mock CSS modules, like this:

// jest.config.js
{
    moduleNameMapper: {
        '\\.(scss)$': 'identity-obj-proxy',
    },
    transform: {
        // works
        // '^.+\\.(ts|tsx)$': 'ts-jest',

        // breaks
         '^.+\\.(ts|tsx)$': ['@swc/jest'],
    },
}
// my-component.tsx
import * as styles from './footer-section.scss';
export const MyComponent = () => {
    return <button className={styles.myCoolButton} />;
};

With ts-jest, this strategy results in the component seeing styles.myCoolButton as "myCoolButton"; with @swc/jest, this results in the component seeing styles.myCoolButton as undefined, because _interopRequireWildcard' doesn't know to add a myCoolButton property to the synthetic export it creates.

The second motivating case we ran into was with the enzyme-adapter-react-16 module, which exports a class and expects you to construct an instance of it. The synthetic export from _interopRequireWildcard does not support having new instances constructed from it.

Input code

repro-proxy-export.ts:

import * as identityObjProxy from 'identity-obj-proxy';
console.log(`identityObjProxy.testVar: ${identityObjProxy.testVar}`);

repro-class-export.ts:

import * as EnzymeAdapter from 'enzyme-adapter-react-16';
new EnzymeAdapter(); // shouldn't throw
console.log('Test passed!');

.swcrc:

{ "module": { "type": "commonjs" } }
> npm install @swc/core @swc/cli identity-obj-proxy enzyme-adapter-react-16 enzyme react@16 react-dom@16
> npx swc -d dist ./repro-class-export.ts ./repro-proxy-export.ts

> # should print identityObjProxy.testVar: testVar
> node ./dist/repro-proxy-export.js
identityObjProxy.testVar: undefined

> # should run without throwing
> node ./dist/repro-class-export.js
C:\repos\swc-repro\dist\repro-class-export.js:26
new EnzymeAdapter(); // shouldn't throw
^

TypeError: EnzymeAdapter is not a constructor
    at Object.<anonymous> (C:\repos\swc-repro\dist\repro-class-export.js:26:1)
    at Module._compile (internal/modules/cjs/loader.js:1068:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1097:10)
    at Module.load (internal/modules/cjs/loader.js:933:32)
    at Function.Module._load (internal/modules/cjs/loader.js:774:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)
    at internal/main/run_main_module.js:17:47

Config

With @swc/jest, default configuration (no .swcrc file) repros. For standalone repro:

{ "module": { "type": "commonjs" } }

Expected behavior

The two repros from the "Input code" section above should respectively:

  • print identityObjProxy.testVar: testVar
  • not emit an error

Version

  • @swc/core: 1.2.59
  • @swc/jest: 0.1.2

Additional context

n/a

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions