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

'this' keyword is equivalent to 'undefined' #1518

Closed
iwinux opened this issue Aug 7, 2017 · 7 comments
Closed

'this' keyword is equivalent to 'undefined' #1518

iwinux opened this issue Aug 7, 2017 · 7 comments

Comments

@iwinux
Copy link

iwinux commented Aug 7, 2017

Hi,

Seems that every definition of async function that gets transformed by Babel (transform-async-to-generator in particular) will trigger Rollup to emit this warning message:

⚠️ The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
https://github.com/rollup/rollup/wiki/Troubleshooting#this-is-undefined

Can this be safely ignored, or needs some fix? Demo repo here: https://github.com/iwinux/rollup-this-undefined

@cfnelson
Copy link

cfnelson commented Aug 8, 2017

@iwinux I had a similar issue. I was able to resolve the warnings by using the rollup-plugin-async before invoking thebabel plugin. Unsure if this is the best approach and would be interested to hear about alternatives.

@Rich-Harris
Copy link
Contributor

@cfnelson's approach sounds like the right one. It's a warning because sometimes you can ignore it and sometimes you can't. You can specify the behaviour you want with options.context and options.moduleContext, as per the troubleshooting guidelines linked in the error message.

You can also suppress the warning with a custom onwarn handler:

{
  onwarn(warning, warn) {
    if (warning.code === 'THIS_IS_UNDEFINED') return;
    warn(warning); // this requires Rollup 0.46
  }
}

@patrickheeney
Copy link

patrickheeney commented Sep 2, 2017

@iwinux @cfnelson I just ran into this as well. Using a rollup plugin first as you suggested. Thank you! It got rid of the error but it seems like we are masking an underlying problem.

@Rich-Harris I think this is the same:
rollup/rollup-plugin-babel#97

It comes from this part of the code:

babelHelpers;

var _this = undefined;

const prepost = () => fn => (() => {
  var _ref = asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee(request, response) {
    return regenerator.wrap(function _callee$(_context) {
      while (1) switch (_context.prev = _context.next) {
...
      }
    }, _callee, _this);
  }));

  return function (_x, _x2) {
    return _ref.apply(this, arguments);
  };
})();

While using the plugin compiles to this:

const prepost = () => fn => (request, response) => __async(function* () {
...
}());

In my case I was using babel-plugin-transform-regenerator, which uses regenerator-transform, or babel-preset-env which also uses it, while rollup-plugin-async uses async-to-gen.

So is rollup not compatible with babel's regenerator-transform since how they seem to wrap uses a global this? If so, maybe it can wrapped again like commonjs or the other async plugin handles it?

EDIT: Tested using http://facebook.github.io/regenerator/ and it uses a global this, try this example:

const test = async (request, response) => {
  return await 1;
};

@pjjanak
Copy link

pjjanak commented Mar 20, 2018

TLDR: an unbound arrow function will cause this issue to arise. Use a plain function or put your async arrow function in a bounding structure. It isn't async/awaits fault at all.

FWIW, and to anyone still trying to solve this issue who is using babel and does not want to add the (unnecessary) rollup-plugin-async, I figured out the underlying problem. Basically I had an async arrow function that I was transpiling which was causing the _this = undefined to pop up. Allow me to explain. Take the following code and assume it is at the top level of your module (ie, not in another function or class):

const asyncFunc = async () => {
    const data = await fetchMyData();
    return data;
}

This will get transpiled by rollup and produce the offending error. Why? Arrow functions use a lexically bound this keyword. Since this arrow function is not in another function or class or bounding structure, ES6 defines this as being meaningless in this context (eg, top-level this does not mean window, it means nothing).

You're probably saying, "But there is no this in that function!" Not one that you put there, at least. See, when babel-plugin-transform-regenerator kicks in it converts your async/await structure using the regenerator-runtime. Without going into the nitty gritty, your arrow function ends up looking something like:

var _this = undefined;

var asyncFunc = function() {
    var _ref = asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
        return regeneratorRuntime.wrap(function _callee$(_context) {
            ...
        }, _callee, _this);
    }));

    return function (_x3) {
        return _ref.apply(this, arguments);
    };
}

Note the _this at the end of .wrap call. The transpiler also outputs the _this declaration (var _this = this) above the function. But then rollup gets its turn and does a pass on the output from babel. Since this is nothing at the top-level and rollup follows this rule, it rewrites the code as var _this = undefined.

How to fix it? The easiest way is to just not use an arrow function. Instead of what you had above use:

const asyncFunction = async function() {
    const data = await getMyData();
    return data;
}

The babel transpiler will appropriately use the block scoped this from the function asyncFunc in this scenario. The output will look something like this:

var asyncFunction = function() {
    var _ref = asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
            ...
        }, _callee, this);
    }));

    return function dumFunc() {
        return _ref.apply(this, arguments);
    };
}

As it were, arrow functions aren't the be-all end-all for functions in all cases, especially where the block scoped this is important. Hope this helps someone.

@emmanuj
Copy link

emmanuj commented Sep 1, 2020

Saw this after adding typescript to my sapper project. Adding "target": "es2017" to compilerOptions in my tsconfig.json resolved it.

@dhlolo
Copy link

dhlolo commented Nov 16, 2022

TLDR: an unbound arrow function will cause this issue to arise. Use a plain function or put your async arrow function in a bounding structure. It isn't async/awaits fault at all.

FWIW, and to anyone still trying to solve this issue who is using babel and does not want to add the (unnecessary) rollup-plugin-async, I figured out the underlying problem. Basically I had an async arrow function that I was transpiling which was causing the _this = undefined to pop up. Allow me to explain. Take the following code and assume it is at the top level of your module (ie, not in another function or class):

const asyncFunc = async () => {
    const data = await fetchMyData();
    return data;
}

This will get transpiled by rollup and produce the offending error. Why? Arrow functions use a lexically bound this keyword. Since this arrow function is not in another function or class or bounding structure, ES6 defines this as being meaningless in this context (eg, top-level this does not mean window, it means nothing).

You're probably saying, "But there is no this in that function!" Not one that you put there, at least. See, when babel-plugin-transform-regenerator kicks in it converts your async/await structure using the regenerator-runtime. Without going into the nitty gritty, your arrow function ends up looking something like:

var _this = undefined;

var asyncFunc = function() {
    var _ref = asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
        return regeneratorRuntime.wrap(function _callee$(_context) {
            ...
        }, _callee, _this);
    }));

    return function (_x3) {
        return _ref.apply(this, arguments);
    };
}

Note the _this at the end of .wrap call. The transpiler also outputs the _this declaration (var _this = this) above the function. But then rollup gets its turn and does a pass on the output from babel. Since this is nothing at the top-level and rollup follows this rule, it rewrites the code as var _this = undefined.

How to fix it? The easiest way is to just not use an arrow function. Instead of what you had above use:

const asyncFunction = async function() {
    const data = await getMyData();
    return data;
}

The babel transpiler will appropriately use the block scoped this from the function asyncFunc in this scenario. The output will look something like this:

var asyncFunction = function() {
    var _ref = asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
            ...
        }, _callee, this);
    }));

    return function dumFunc() {
        return _ref.apply(this, arguments);
    };
}

As it were, arrow functions aren't the be-all end-all for functions in all cases, especially where the block scoped this is important. Hope this helps someone.

Thanks for sharing. I wonder if I just ignore the warning, it seems that my bundle should still work. How can i judge if it break the runtime logic when this warning show up.

@dslatkin
Copy link

dslatkin commented Apr 24, 2024

For anyone else that stumbles across this, I was having a similar error message when building a SvelteKit project for production. Specifically complaining about @opentelemetry/api which gets included in the bundle through our use of @azure/storage-blob:

$ npm ls @opentelemetry/api
[email protected] /workspace
├─┬ @azure/[email protected]
  └─┬ @azure/[email protected]
    └── @opentelemetry/[email protected]

These were the kind of errors being produced (the circular dependency error was also new thanks to the introduction of that Azure package):

Output
$ npm run build

> [email protected] build
> vite build

vite v5.2.10 building SSR bundle for production...
✓ 155 modules transformed.
[My app output...]
vite v5.2.10 building for production...
✓ 139 modules transformed.
[Client build output...]
✓ built in 1.99s
[Server build output...]
✓ built in 8.29s

Run npm run preview to preview your production build locally.

> Using @sveltejs/adapter-node
node_modules/@opentelemetry/api/build/esm/metrics/NoopMeter.js (16:17): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/metrics/NoopMeter.js (16:25): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/api/diag.js (16:14): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/api/diag.js (16:22): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/api/diag.js (32:21): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/api/diag.js (32:29): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/api/context.js (16:14): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/api/context.js (16:22): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/api/context.js (32:21): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/api/context.js (32:29): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/baggage/internal/baggage-impl.js (16:14): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/baggage/internal/baggage-impl.js (16:22): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/baggage/internal/baggage-impl.js (32:16): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/baggage/internal/baggage-impl.js (32:24): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/context/NoopContextManager.js (16:14): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/context/NoopContextManager.js (16:22): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/context/NoopContextManager.js (32:21): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/context/NoopContextManager.js (32:29): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/diag/ComponentLogger.js (16:14): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/diag/ComponentLogger.js (16:22): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/diag/ComponentLogger.js (32:21): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
node_modules/@opentelemetry/api/build/esm/diag/ComponentLogger.js (32:29): The 'this' keyword is equivalent to 'undefined' at the top level of an ES module, and has been rewritten
Circular dependency: node_modules/xmlbuilder/lib/XMLNode.js -> node_modules/xmlbuilder/lib/XMLElement.js -> node_modules/xmlbuilder/lib/XMLNode.js
Circular dependency: node_modules/xmlbuilder/lib/XMLNode.js -> node_modules/xmlbuilder/lib/XMLElement.js -> node_modules/xmlbuilder/lib/XMLAttribute.js -> node_modules/xmlbuilder/lib/XMLNode.js
Circular dependency: node_modules/xmlbuilder/lib/XMLNode.js -> node_modules/xmlbuilder/lib/XMLCData.js -> node_modules/xmlbuilder/lib/XMLCharacterData.js -> node_modules/xmlbuilder/lib/XMLNode.js
Circular dependency: node_modules/xmlbuilder/lib/XMLNode.js -> node_modules/xmlbuilder/lib/XMLDeclaration.js -> node_modules/xmlbuilder/lib/XMLNode.js
Circular dependency: node_modules/xmlbuilder/lib/XMLNode.js -> node_modules/xmlbuilder/lib/XMLDocType.js -> node_modules/xmlbuilder/lib/XMLNode.js
Circular dependency: node_modules/xmlbuilder/lib/XMLNode.js -> node_modules/xmlbuilder/lib/XMLDocType.js -> node_modules/xmlbuilder/lib/XMLDTDAttList.js -> node_modules/xmlbuilder/lib/XMLNode.js
Circular dependency: node_modules/xmlbuilder/lib/XMLNode.js -> node_modules/xmlbuilder/lib/XMLDocType.js -> node_modules/xmlbuilder/lib/XMLDTDEntity.js -> node_modules/xmlbuilder/lib/XMLNode.js
Circular dependency: node_modules/xmlbuilder/lib/XMLNode.js -> node_modules/xmlbuilder/lib/XMLDocType.js -> node_modules/xmlbuilder/lib/XMLDTDElement.js -> node_modules/xmlbuilder/lib/XMLNode.js
Circular dependency: node_modules/xmlbuilder/lib/XMLNode.js -> node_modules/xmlbuilder/lib/XMLDocType.js -> node_modules/xmlbuilder/lib/XMLDTDNotation.js -> node_modules/xmlbuilder/lib/XMLNode.js
Circular dependency: node_modules/xmlbuilder/lib/XMLNode.js -> node_modules/xmlbuilder/lib/XMLRaw.js -> node_modules/xmlbuilder/lib/XMLNode.js
Circular dependency: node_modules/xmlbuilder/lib/XMLNode.js -> node_modules/xmlbuilder/lib/XMLDummy.js -> node_modules/xmlbuilder/lib/XMLNode.js
  ✔ done

Moving @azure/storage-blob from a dev dependency to a prod dependency (excluding it from bundling) was how I addressed this.

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

8 participants