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

umd-compliance of babel-plugin-transform-modules-umd #10696

Open
fuweichin opened this issue Nov 11, 2019 · 10 comments
Open

umd-compliance of babel-plugin-transform-modules-umd #10696

fuweichin opened this issue Nov 11, 2019 · 10 comments

Comments

@fuweichin
Copy link

Bug Report

Current Behavior
With babel-plugin-transform-modules-umd(npm @babel/plugin-transform-modules-umd version@7.7.0), there are lots of limitations to transform mjs to js:

  • doesn't support export default xxx
  • is not compatible with amdjs since output code FACTORY lacks of return xxx
  • is not compatible with commonjs since output code IFFE lacks of module.exports=xxx

Input Code

foo.mjs

export default {
    name: "foo"
};

Output Code

foo.js

(function (global, factory) {
  if (typeof define === "function" && define.amd) {
    define(["exports"], factory);
  } else if (typeof exports !== "undefined") {
    factory(exports);
  } else {
    var mod = {
      exports: {}
    };
    factory(mod.exports);
    global.foo = mod.exports;
  }
})(typeof globalThis === "object" ? globalThis : typeof self === "object" ? self : this, function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  var _default = {
    name: "foo"
  };
  _exports.default = _default;
});

Expected behavior/code

(function (global, factory) {
  if (typeof define === "function" && define.amd) {
    define(["exports"], factory);
  //DEL } else if (typeof exports !== "undefined") {
  } else if (typeof module === "object" && module.exports) { //INS
    //DEL factory(exports);
    module.exports = factory(module.exports); //INS
  } else {
    var mod = {
      exports: {}
    };
    //DEL factory(mod.exports);
    //DEL global.foo = mod.exports;
    global.foo = factory(mod.exports); //INS
  }
})(typeof globalThis === "object" ? globalThis : typeof self === "object" ? self : this, function (_exports) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.default = void 0;
  var _default = {
    name: "foo"
  };
  _exports.default = _default;
  return "default" in _exports ? _exports["default"] : _exports; //INS
});

Note with such behavior, you shouldn't use export {xxx} and export default xxx both at once, since ES6 module has features which AMD module and CommonJS module don't support.

Babel Configuration (.babelrc, package.json, cli command)

module.exprots={
  "presets": [
    ["@babel/preset-env", {
      "useBuiltIns": false,
      "targets": {
        "chrome": "60",
        "safari": "12",
      }
    }]
  ],
  "plugins": [
    "@babel/plugin-transform-modules-umd"
  ]
}

Environment

  • Babel version(s): 7.7.0
  • Node/npm version: Node 10.16.3/npm 6.12.1
  • OS: Windows 10
  • Monorepo: yes
  • How you are using Babel: cli

Possible Solution
Now that the plugin targets umd, why not see umdjs - returnExports for some umd considerations?

Additional context/Screenshots

Is my babel configuration mistaken, or I have used a wrong plugin?

I have once forked and published babel-plugin-transform-modules-simple-amd for babel 7.x, or maybe I need fork and publish another plugin, e.g. babel-plugin-transform-modules-simple-umd, to do that kind of job?

@babel-bot
Copy link
Collaborator

Hey @fuweichin! We really appreciate you taking the time to report an issue. The collaborators on this project attempt to help as many people as possible, but we're a limited number of volunteers, so it's possible this won't be addressed swiftly.

If you need any help, or just have general Babel or JavaScript questions, we have a vibrant Slack community that typically always has someone willing to help. You can sign-up here for an invite."

@JLHwung
Copy link
Contributor

JLHwung commented Nov 11, 2019

There are three changes on the proposed expected code.

  1. apply factory to module.exports instead of exports

Actually it is not compliant to CommonJS spec which only defined exports. module.exports is defined in Node.js Module system. Other than Node.js, there are many other CommonJS implementation.

  1. return a value in factory

Either use "return value;" or the CommonJS "exports" idiom, which can be useful for circular dependencies.
-- quoted from https://requirejs.org/docs/whyamd.html#amd

Since we have defined the exports of the module, I don't think we still need to return a value.

  1. define exports.default instead of exports in global object

By doing so the global object would not provide access to other exported properties. For example

export default React;
export { Component };

Now if the user uses React.Component from global object, it will throw since only default export is available.

@fuweichin
Copy link
Author

fuweichin commented Nov 12, 2019

Ask in reply:

  1. It seems that current implementation of the plugin follows CommonJS Strict variant of UMD. and that means I cannot use export default in ES module.
  2. The AMD API says:

If the factory function returns a value (an object, function, or any value that coerces to true), then that value should be assigned as the exported value for the module.

  1. Like what I said before, ES6 module has features which AMD module and CommonJS module don't support. I do understand the plugin is trying to transform losslessly, which is at the cost of incompatibility/inconvenience with existing runtimes(NodeJS, RequireJS and IE).

Possible solution:

How about adding an property in babel config to specify which umd template the plugin is going to use? then let the plugin prepare several templates for users to choose from? like this

module.exports={
  "plugins": [
    ["@babel/plugin-transform-modules-umd", {
        "umdTemplate": "amdWebGlobal",// or "commonjsStrictGlobal", "returnExportsGlobal", ...
    }]
  ]
}

See full list of UMD Templates

@nicolo-ribaudo
Copy link
Member

@fuweichin The thing with export default x is that it should similarly to export { x as default }, which in turn has the same semantics as export { x as foo }: default and named exports are the same thing, with only a slightly different syntax.

@pyrsmk
Copy link

pyrsmk commented Jun 2, 2020

@nicolo-ribaudo Sorry, but I feel kinda shocked that we have to adapt the code of our library for the plugin to work as expected... My bad, I was using the library in a wrong way.

@chrisknoll
Copy link

chrisknoll commented Jun 11, 2020

Will a fix be provided for this soon? I am currently using a library (FileSaver.js) where the UMD transform is not working properly (per this issue: eligrey/FileSaver.js#646. I think the issue in FileSaver.js is simply a matter of an invalid UMD wrapper. If the babel UMD transform want's to modify an 'exports', the AMD spec supports this by using the patern:

(function (global, factory) {
  if (typeof define === "function" && define.amd) {
    define(["exports"], factory);
...
...
})(this, function(_exports) {
...
module definition here
...
));

Otherwise, you need to return the value you are assigning to exports as the exported module (in AMD).

@chrisknoll
Copy link

The other solution is to just return the module value. I think setting exports=xxx along side a return xxx won't impact anything.

@leifjones
Copy link

I'd like to voice support for this insofar as it would enable resolving this issue: eligrey/FileSaver.js#538 as discussed in this PR: eligrey/FileSaver.js#602

@pravi
Copy link

pravi commented Jul 4, 2021

I think I was hit by this limitation whe trying to update babel to version 7 in autosize module for debian. See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=990458 for details and finally I had to use rollup to generate the umd bundle.

@zerofront
Copy link

any update ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants