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

Overriding a re-export with a local variable of same name results in runtime ReferenceError #78

Closed
wadjoh opened this issue Oct 11, 2023 · 3 comments

Comments

@wadjoh
Copy link
Contributor

wadjoh commented Oct 11, 2023

Version: 0.79.55

Issue

Given an input file of:

export * from './someModule'
export const foo = () => {}

swc_mut_cjs_exports will output:

export { };
Object.defineProperty(exports, "foo", {
    enumerable: true,
    get () {
        return foo;
    },
    set (v) {
        foo = v;
    },
    configurable: true
});
Object.keys(mod).forEach(function(key) {
    if (key === "default" || key === "__esModule") return;
    if (key in exports && exports[key] === mod[key]) return;
    exports[key] = mod[key];
});
import * as mod from './someModule';
const foo = ()=>{};

If someModule exports a member named foo a ReferenceError will be thrown at runtime:

ReferenceError: Cannot access 'foo' before initialization

This is because the forEach in the output file tries to access exports.foo which has a getter to return the local foo variable which hasn't been defined yet.

Expectation

I think, ideally, we'd want the transformed exports to follow the same order as the pre-transformed exports rather than hoisting local exports to the top.

Like:

export { };
Object.keys(mod).forEach(function(key) {
    if (key === "default" || key === "__esModule") return;
    if (key in exports && exports[key] === mod[key]) return;
    exports[key] = mod[key];
});
Object.defineProperty(exports, "foo", {
    enumerable: true,
    get () {
        return foo;
    },
    set (v) {
        foo = v;
    },
    configurable: true
});
import * as mod from './someModule';
const foo = ()=>{};
@magic-akari
Copy link
Owner

magic-akari commented Oct 11, 2023

I believe the key issue is not in the order, but in the line if (key in exports && exports[key] === mod[key]) return;.

The expression exports[key] === mod[key] triggers a getter, and I am unsure why this code was written in this manner.

In my opinion, if the key exists in the exports object, it should be returned directly, without checking for equality.

Furthermore, there is an issue with key in exports. It should be replaced with Object.prototype.hasOwnProperty.call(exports, key).

Otherwise, exporting functions with names like toString may cause problems.

I strongly suspect that this code originates from the loose mode of Babel compilation.

@magic-akari
Copy link
Owner

expected result:

Object.keys(mod).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(exports, key)) return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return mod[key];
    },
    configurable: true
  });
});

@wadjoh
Copy link
Contributor Author

wadjoh commented Oct 11, 2023

Did my best at getting it to output what you mentioned in #79. Seems to fix my issue.
Never written Rust before (let alone working with swc and ASTs), so I'm guessing there's some issues in my code 😅

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

2 participants