-
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
umd module compiler option doesn't have a fallback for global namespace. #8436
Comments
There is no specification for UMD and any interfaces with the global/window object require some specific decisions, like you would have to determine a module name for each module. What names on the global scope would you give these modules, all part of the same project?
|
If I'm outputting to a single file (like a library like jquery) it becomes very easy to say the export should be namespaced under the file name or module namespace. https://github.com/umdjs/umd/blob/master/templates/commonjsStrictGlobal.js We have to support a fallback to globals in our library and I've had to work around this like: |
//cc: @RyanCavanaugh |
As discussed in #9678, if we use So merging the two needs, my proposal is: When
Example: developer is writing a library "myreact" to be consumed in modules and in global:
export function createElement() { return 42; };
export as namespace React; Typescript output
export declare function createElement() {};
export as namespace React;
(function (root, factory) {
if (typeof module === 'object' && typeof module.exports === 'object') {
var v = factory(require, exports); if (v !== undefined) module.exports = v;
}
else if (typeof define === 'function' && define.amd) {
define(["require", "exports"], factory);
}
else {
root.React = factory(require, exports);
}
})(this, function (require, exports) {
"use strict";
function createElement() {
return 42;
}
exports.createElement = createElement;
}); |
@nippur72 something like that could work but you also have to consider that https://github.com/exceptionless/Exceptionless.JavaScript/blob/master/dist/exceptionless.js#L1236-L1264 (which we may not what to do. |
@niemyjski yes the missing import _ from "lodash"; which translates into the now global code var _ = require("lodash"); which is nothing else than var _ = window["lodash"]; but which is also So, we can fake |
idk, it's something that has to be looked into... |
I don't see why the |
By the way, I did this two years ago, maybe it can be simplified and used in the compiler. |
👍 it would be good if there was a tsConfig option for this. I ran into this today. |
Any news on this issue? I need to implement a library composed of several modules that user may select accordint to its need. I would like each module be UMD, so user may decide how and if bundling the modules it needs. It appears that my plan at moment is very difficult to implement in TypeScript. The only way I can think of is:
The above appears to me the only way to get TRUE UMD modules from TypeScript. Any thought about this? Better proposals? |
I ran into this issue today. I really need an } else {
root.exceptionless = factory();
} |
@zixia, I wrote an article on how to author multi-platform (amd, commonjs, es6, global namespace) libraries with Typescript, and some simple software to process automatically all source files to get both .d.ts and compiled .js for all platforms. The article should appear in the upcoming May magazine of the DotNetCurry magazine |
Hi @frankabbruzzese, Thank you very much for replying me! In order to make my UMD bundle work directly by Firstly, I had to switch to use Secondly, I had to use The modification is like this one: 4c4
< (factory((global.brolog = global.brolog || {})));
---
> (factory(global));
227d226
< Object.defineProperty(exports, '__esModule', { value: true }); My repo: https://github.com/zixia/brolog I know it's not a good solution for all, but it really works very nice for my tiny module. ;-) I'll keep trying to find a better solution to replace this method, and I'm looking forward to reading your article of compile .js for all platforms. Please post the link to this thread after it publishes, so I can read it at the first time. Thanks! |
I just found another hacky workaround for rollup at rollup/rollup#494 (comment) |
@zixia , |
@frankabbruzzese Awesome, thank you very much! |
@zixia , |
Now my article on mult-platform support for TypeScript libraries has been published also here. |
How about this solution: Example input import stuff from './my-sub-module'
import _ from 'lodash'
export const theAnswer = 42 When following options are specified in {
"globalName": "MyModule", // global var for this module
"globalMap": { // map of modules and their global names
"lodash": "_"
}
} Generated (function (root, factory) {
// stuff that already works
if (typeof module === "object" && typeof module.exports === "object") {
module.exports = factory(require, exports)
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "./my-sub-module", "lodash"], factory)
}
// Use globals (interesting stuff starts here)
else {
// init exports object
root.MyModule = {}
// for sub-module it would be root.MyModule.MySubModule
// insert globalMap from config here
const globalMap = {…}
function require(mod) {
if (mod[0] == '.') { // if module is relative
// insert logic for resolving relative module here
// e.g. for ./foo-bar/thing it should return <current>.FooBar.Thing
return …
}
else { // if module is not relative
// do the mapping
if (mod in globalMap) mod = globalMap[mod]
// return name from global object
return root[mod]
}
}
factory(require, root.MyModule)
}
})(this, function (require, exports) {
const stuff = require("./my-sub-module") // returns root.MyModule.MySubModule
const _ = require("lodash") // returns root._
exports.theAnswer = 42 // assigns root.MyModule.theAnswer
}); It does generate a lot of overhead code for every file, but it's completely optional. Edit: For this to work, every library imported must either be contained in one file (i.e. all the libs that use UMD with globals currently) or use the same scheme. It's a pretty big win IMO. |
If you are defining a new module format, consider adding an TypeScript adds this when transpiling a module using |
Any progress on this? For simple using and lightweight building procedure reason, wish some config to emit export on root. |
I also would like this. I'm currently achieving this by doing a similar method as @frankabbruzzese suggested, which was preprocessing the TS and generating files for global and UMD and then running |
This no clear reason why you'd need hacks or additional tools to accomplish such a small thing. Why can't we have something like I really don't want hacks (or big tools like webpack) for a simple small library 😐 |
Is this the only blocker here? How about allowing (function(root, factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
} else if (typeof exports === 'object') {
module.exports = factory(require, exports, module);
} else {
var A = root.A;
(function (A) {
var B;
(function (B) {
B.C = factory(...);
})(B = A.B || (A.B = {}));
})(A || (A = {}));
root.A = A;
}
}(this, function(require, exports, module) {} |
One more example that uses multi-level namespace ( IMO it's not straightforward as it uses |
Is it not possible for the typescript compiler to incorporate a bundle process, either one that already exists - webpack/rollup or one that theoretically shouldn't be too difficult to implement? I mean there are solutions to this problem, namely export as CommonJS and have a bundler re-bundle as UMD. However that kind of defeats the purpose of having the UMD option in typescript. You wouldn't really have to change anything for the current UMD process, just add an extra config option so previous code is still maintained in the same way. |
If the |
@njleonzhang, UMD is a convention, not a standard and global modification is not entirely consistent or common in implementations. It is also a bit too late to remove it. |
Maybe we can accept some popular behavior, for example from rollup.js . https://github.com/twitter/twitter-text/blob/master/js/rollup.config.js#L36-L37 |
@saschanaz I am not saying that solving the problem isn't warranted. I am just pointing out that there is no UMD "standard" to adhere to and it makes it non-sensical to suggest removing the feature as @njleonzhang suggested. Of course once you start dealing with deep creation of the global var, you start making the emit pretty darn ugly and starts really encroaching on contravening non-goal:
If you need a bundler, use a bundler. |
Why? TS is already doing that and it isn't too ugly. namespace A.B {
var x = 0;
}
// emits:
var A;
(function (A) {
var B;
(function (B) {
var x = 0;
})(B = A.B || (A.B = {}));
})(A || (A = {}));
We don't need a bundler, instead we need a proper emission for |
@kitsonk Although I have some words to refute you, I shut up at this time to avoid meaningless argument in this thread. I just want |
Hello, any news on this?
that way when i reference using global i can just call
is there a way to do that? |
Wow, just ran into this today. It's 2021. This started in 2016. |
I was disappointed when I saw my UMD module did not work in browser. Standard or not, UMD means something to web developers and Typescript is just misleading when it says UMD but does not output proper UMD. |
A core part of "Universal" is to include browser support if no other module loader is found - note that this means it's only doing this when there is no other option - not all of the time. That is - the library would only do it if:
I'd say that's not ugly at all.
There is, actually a standard. It's been around since 2012. I see where you might have misunderstood, however. It was common practice for years to just copy code from their templates into your JavaScript file, and edit as needed, or just make code vaguely inspired off of the last time one saw a UMD module that does more-or-less the same thing - but perhaps not as good. Simply stated, there is no standard tooling but there is a standard - even if it isn't followed 100% of the time. Or, an alternative idea that's been bouncing around my head: in the documentation for the "umd" option it could explain that it doesn't have a global namespace fallback, and one would have to post-process the compiled typescript to add that. (No link to specific approach would be needed - but it would be nice to see at least one, like the ts->es6->rollup->umd one I've adopted) |
fallback will pollutes the global namespace. for example // a.ts
export function foo() {
console.log('foo a.ts')
} // b.ts
export function foo() {
console.log('foo b.ts')
} These two files will fallback to the global namespace and conflict, This produces some unexpected behavior. But sometimes a simple function is needed, without any module loader or bundler, it can be run directly from the <script> tag. Consider adding something directive like this, explicitly exporting to global namspace // a.ts
// @ts-umd-global-namespace foo
export function foo () { // ☑️ fallback to globalThis.foo
// do somethinig
}
// @ts-umd-global-namespace bar
export function bar () { // ☑️ fallback to globalThis.bar
// do something
}
export default function DefaultIsNotAllow() {} |
typically, UMD style fallbacks would be a global named after the library name. This global would be an object containing all of the exported functions. If the file needs a fallback and it's not supposed to be standalone, then it would be a sub-object underneath the globally exposed library name object. This recurses down with imports within the library. My understanding is that this would still be using the Granted, misunderstandings aside, I'm not as convinced this needs to be a feature in typescript in 2023 anymore, seeing current progress in ESM. Progress that used to by halted by typescript - by not properly supporting ESM until very recently - ironically |
Most umd patterns have a third fallback that allows exporting to the window.namesapace = export; As such the current umd module export is pretty broken when a huge number of users / library developers need to support all three.
The text was updated successfully, but these errors were encountered: