-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Add support for combined AMD+CommonJS module export format #2605
Conversation
So why is this a thing?
|
thanks @csnover, we will need to talk about this in the design meeting. So I apologize in advance for the delay. A few questions :
|
Would agree with @mhegazy on both, especially on UMD. |
Doing this would require a valid JS identifier to be created for each module and I don’t know of any good way to do this automatically. One cannot just map module IDs by replacing "/" with "." and call it a day, for instance. Globals are an antipattern anyway for modular code so I’m not sure how useful it would be (though of course there is nothing that would preclude adding it later! :)). Long term, this module target (along with CommonJS and AMD targets) will hopefully become obsolete, though that is years away at least.
There is no ES6 module loader proposal, it was dropped from the ES spec and moved to WHATWG where it was significantly overhauled at least once. In any case it would be premature to try to use it as a target; we recently held a meeting for several different individuals with experience working on and developing module loaders for JavaScript (RequireJS team, Dojo team, CujoJS/RaveJS team, other stakeholders of the AMD specification), and the general consensus was that there are some design flaws that prevent the WHATWG proposal from being viable at the moment. There have historically been some issues getting the committee to engage on legitimate concerns that have been raised by community members with experience writing client-side loaders as well, and I don’t think that situation has improved any. |
My main concern is introducing new concepts. ppl are familiar to some extent with what UMD is, i would hate to have something that is mostly UMD though not it, and then creating a new flavour.
I would think just the file name converted to an identifier, and error if not a valid one. e.g.: "foo.bar.ts" is an error, "foo.ts" becomes "foo". we have also added AMD module name support using |
TS compiler already provides an option of outputting to CJS or AMD, this just combines the two, so it’s not really a new concept. It’s also not a new thing in the case of UMD, this code emits a (fixed) version of the “Node Adapter” pattern from the UMD repository, but since it doesn’t also emit any globals I didn’t want to imply that it did by calling it “umd”.
OK, so to make sure I understand what you thinking is here, what would these commands emit:
And what would it look like for them to get references to other external modules they Probably more importantly, what is the point of doing all this extra work? How would you load these modules into a browser in a consumable manner without introducing an antipattern (manual dependency resolution)? I can tell you right now the benefit of this patch but I would have a hard time telling you what the benefit would be of making it also try to generate objects in the global scope and resolve all the edge case problems of trying to turn the global scope into a module registry.
AMD module IDs are module IDs, not JavaScript identifiers so I don’t think this is a way out either. |
fair enough. thanks for the replies. i just find "camd" a bit unclear.. so maybe we just call it "umd" even without the globals. just an idea, |
Naming is always the hardest part. It could be called “legacy” or “combined” or something too; I’ll take a quick poll at work and see if anyone has a preference, also cc team @umdjs for any thoughts on what might be a decent short name for an export that is a Node.js+AMD module without globals. |
The serious alternative suggestions I received were:
Any preferences? |
let me do a similar poll around here and see what are the preferences. |
Isn't there any variation that is exactly the same as this: https://github.com/umdjs/umd Perhaps there should be. 👍 for adding this. |
See the above discussion :) or is there something that needs extra clarification? |
That discussion seems focused on why we are not doing global but I don't think all "valid" umd variations need globals e.g this one : https://github.com/umdjs/umd/blob/master/nodeAdapter.js (am I wrong ?) So I think what you have is still almost valid umd and can be made an exact valid umd variation "nodeAdapter" solves the naming problem (no new names created maybe even call it umd then) |
Or we can ask umd JS to make what you have here a valid variation. |
Also wouldn't the var foo = require("foo");
import bar = require("bar"); |
Unfortunately, and unbeknownst to many, most of the patterns in the UMD repository are wrong/broken. I already had to fix one of them last year and I could probably go through and find issues with everything in that repo. Specifically the nodeAdapter.js in UMD repository is broken in these ways:
I am sure if I submit a PR to umdjs to change nodeAdapter to match the output of this patch it will be accepted.
Not sure what you are getting at here, could you clarify? |
With nodeadaptor this whole thing: var foo = require("foo");
import bar = require("bar"); would get insert into : https://github.com/umdjs/umd/blob/master/nodeAdapter.js#L13-L17 as var foo = require("foo");
var bar = require("bar"); And then |
Yes, that’s true, but |
agreed ❤️ |
I definitely prefer what you have here |
Here are some ideas we're considering so far for the name. Feel free to add a few.
For the sake of humor, we also considered calling it "One Module System"
|
I've been giving some thought to this myself. I had been looking at a general purpose header that would support CJS, AMD, and System.register: import { x } from './y';
export function z() {}
console.log(x); // beginning of template
(function (deps, factory) {
function bind(require, exports) {
var module = factory(function (k, v) { return exports[k] = v; });
for (var i = 0; i < deps.length; ++i) { module.setters[i] = require(deps[i]); }
return module.execute();
}
if (typeof define === "function" && define.amd) {
define(["require", "exports"].concat(deps), bind);
}
else if (typeof module === "object" && typeof exports === "object") {
module.exports = bind(require, exports) || exports;
}
else if (typeof System === "object" && typeof System.register === "function") {
System.register(deps, factory);
}
})
// end of template
(["./y"], function (export_1) {
var y_1;
function z() {}
export_1('z', z);
return {
setters: [
function (v) { y_1 = v; }
],
execute: function () {
console.log(y_1.x);
}
};
}); |
CJS/AMD only might be something like this: (function (deps, factory) {
if (typeof define === "function" && define.amd) {
define(["require", "exports"].concat(deps), factory);
}
else if (typeof module === "object" && typeof exports === "object") {
module.exports = factory(require, exports) || exports;
}
})
(["./y"], function (require, exports) {
var y_1 = require('./y');
function z() {}
exports.z = z;
console.log(y_1.x);
}); |
@rbuckton Hey, could you point me to some concrete information showing that those System.* APIs are ever actually going to be implemented by browsers or server-side JS engine vendors? They were eliminated from the WHATWG-formerly-ES6 loader specification quite some time ago, but people keep bringing them up. I don’t understand what is going on, except I know that the platform does not need a third incompatible never-standard module loader API, and SystemJS consumes CJS and AMD modules already. |
@csnover thanks again, and sorry for the delay. Can you refresh the pull request against latest from master. My vote is for "umd" and if not then "unified"; we can add more details in the help message and in documentation about differences from the standard UMD. Just to avoid introducing new concepts and/or acronyms. As for system.js, we had a discussion about it today in the design meeting; while I agree there are no indications that systemJS support will be picked up by engines, TC39 standardizing a module syntax and semantics with no loader or bundeler pipeline has created a vacuum. SystemJS is based on the initial loader proposal that accompanied the es6 modules and that gives it momentum. Whether it is going to evolve into the standard is yet to be seen; there is enough support in tooling and workflows today that warrants considering it, at least from typescript standpoint. For the purposes of this change, I do not think we need to worry about systemJS; assuming umd expands its definition to contain systemJS nothing stops us from adding it later on. |
👍 umd |
Sounds good, I will un-bitrot this early next week and change the name and let you know when it is updated. Have a great weekend! |
|
Seems strange to me to call the flag |
@danquirk don’t do it man |
The new module format enables global-less universal modules, compatible with both AMD and CJS module loaders. Fixes #2036.
I updated per the feedback above and rebased against master. I improved the implementation so it should work with Browserify users and looks a little prettier in the output. Note there seems to be something wrong with the baseline test compiler, the compiler directives in all of the |
I just changed tests\cases\compiler\es5-declarations-amd.ts and test framework caught it, what do i need to do to repro this behavior? |
@mhegazy If you look at the baseline reference output you will see it’s not an AMD module, it’s been compiled like no module option was specified. |
that is because it is not a "module". a module needs to have at least one top level export or import. We have talked in the past about making this case to be emitted as a module regardless, but that seemed like a very corner case any ways (i.e. a module that does not export or import anything). |
👍 |
Oh, right, of course. Everything is fine then! |
Thanks @csnover! |
Thank you! |
woot! can we get a beta2 with just this change :) |
@niemyjski working on it. will update the road-map once we have a plan. |
I just had to do this manually and ran into some issues and had to hack around them: https://github.com/exceptionless/Exceptionless.JavaScript/blob/master/dist/exceptionless.es5.js#L1613-L1627 so this would be greatly welcomed! |
I really wonder how this would work with tsproject (https://github.com/ToddThomson/tsproject) It currently does bundling for the new es6 module syntax and works great. @csnover, if we outputted amd for every export, would it keep adding items to the global export (like the existing module output)? |
@niemyjski sorry, I don’t know how that project works, so I can’t really give you an answer. Every loader is a unique snowflake when it comes to how built layers are consumed, so it’s not something that I think something like tsproject can actually do correctly without changing how modules work (which I suspect is what it does, mutating modules to no longer be modules and sticking them in a closure). |
@csnover Thanks for the explanation. I'll look into it more. |
The new module format outputs global-less modules that are compatible with both AMD and CJS loaders.
Fixes #2036.