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 · 4 comments
Open

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

fuweichin opened this issue Nov 11, 2019 · 4 comments
Labels

Comments

@fuweichin
Copy link

@fuweichin fuweichin commented Nov 11, 2019

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

This comment has been minimized.

Copy link
Collaborator

@babel-bot babel-bot commented Nov 11, 2019

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

This comment has been minimized.

Copy link
Contributor

@JLHwung 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

This comment has been minimized.

Copy link
Author

@fuweichin 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

This comment has been minimized.

Copy link
Member

@nicolo-ribaudo nicolo-ribaudo commented Nov 12, 2019

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.