Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

module.exports #86

Closed
kof opened this Issue Mar 26, 2011 · 18 comments

Comments

Projects
None yet
4 participants

kof commented Mar 26, 2011

Hi,

nodejs is actively using module.exports and libs, which targeting both browser and node are using this to check the env:

this is an example from underscore.js

if (typeof module !== 'undefined' && module.exports) {
  module.exports = _;

Using it with require.js, because module.exports isn't defined, it leads to defining a namespace in global context.

Can require.js additionally to setExports provide module.exports too ?

Oleg

Owner

jrburke commented Mar 26, 2011

Setting exports this way is allowed as long as the code is in a define wrapper and "exports" is a dependency, test case here:
https://github.com/jrburke/requirejs/blob/master/tests/exports/assign.js

How is that?

kof commented Mar 26, 2011

I know, that setting module.exports is allowed, the problem is that module.exports is initially not linked to exports object, so if checking if (module.exports) will return undefined and the check thinks it's not commonjs

kof commented Mar 26, 2011

see https://github.com/documentcloud/underscore/blob/master/underscore.js#L54

this will lead to global referencing of "_"

Contributor

fitzgen commented Mar 27, 2011

As James said, if you want to set module.exports, just require module:

define(['require', 'exports', 'module'], function () {
  function something () {
    ...
  }
  ...
  module.exports = something;
});

If you are trying to do this to have interop with existing code which relies on module.exports, you should probably just use the commonjs converter script:

./bin/x ./build/convert/commonjs/convert.js  path/to/module path/to/convertedModule

The converter script will automatically wrap the module in the proper define() call with module and exports required for you.

Good luck!

kof commented Mar 27, 2011

My description was probably too confusing.
Inside of with transport string wrapped module, following code should work:
typeof exports === 'object'; // true
module.exports === exports; // true

Currently module.exports is not set by require.js

Owner

jrburke commented Mar 27, 2011

I understand now but it will be difficult to support -- right now I use the presence of module.exports to know if that is what should define the module vs. using the return value of from the define function.

I could set module.exports = undefined, and then ask backbone if they could change their check to if('exports' in module) that may work out.

I do not want to automatically set module.exports to an object to avoid some ambiguity with circular dependencies that use return from define, and do not want to use getter/setters for it since I support browsers that do not have that capability.

I'll think about it more while I'm on medical leave, I agree it would be good to fix in some way. Other suggestions welcome.

kof commented Mar 27, 2011

workaround using module.exports = undefined is kind of messy, you can't ask every module dev to change his lib, because in node world module.exports is very popular and I think the most people are doing this check so.

I am not familiar with your specific implementation problems.

I think you already has some ambiguity supporting both exports and return. What is the module definition if I am using both of them?

I would define priorities:

  • highest priority is module.exports, if it is changed it will be used
  • then exports, if it is changed it will be used (if linking exports to module.exports it is the same)
  • then if nothing is set but return value is given, use it.
Contributor

fitzgen commented Mar 27, 2011

What if requirejs tested the existence of a return value instead of the existence of module.exports? This way, module.exports === exports and I don't see how this could affect testing for circular dependencies. Am I missing anything?

Owner

jrburke commented Apr 6, 2011

I think I can work out a solution, it will just explicitly require the wrapper to want 'exports', not just 'module' by itself.. So this would not work:

define(['module'], function (module) {
    module.exports = 'foo';
});

but this will:

define(['exports', 'module'], function (exports, module) {
    module.exports = 'foo';
});

and this will (the standard wrapper used for CommonJS modules):

define(function(require, exports, module){
    module.exports = 'foo';
});

and will allow the Backbone case to work. I'll look at doing that change.

kof commented Apr 6, 2011

I think you need getters for this, not?

why don't just to link module.exports to exports obj. I don't really understand the internal problem.

Owner

jrburke commented Apr 6, 2011

Allow scripts like Backbone to be able to test for if module.exports and have it work. It means the exports vs. return rules stricter but that is a good thing, will document in release notes. Closed by aa8a39f

@jrburke jrburke closed this Apr 6, 2011

Owner

jrburke commented Apr 6, 2011

kof: I went with just using the exports value for module.exports. I do not want to use getters/setters. I believe I worked out a solution, see this test as an example.

kof commented Apr 6, 2011

yeah .. this is what I was thinking about ... thank you.

Hi,

try my little contribution to the github network. i hope you can enjoy it.

https://github.com/dazagrohovaz/Require5.JS

almost like the Node.JS require function, but for the browser.

Features:

  • load and eval javascripts files once
  • no appendChild command
  • support sync and async XHR requests
  • save the scripts on HTML5 Storage if available
  • data transfer only if required, load scripts from storage or cache
    if available (no transfer), otherwise load scripts via XHR (data transfer)

regards

Owner

jrburke commented May 3, 2011

dazagrohovaz: thanks for sharing. RequireJS purposely avoided using XHRs, particularly synchronous XHRs, with eval, some of the reasons explained here. However, if it works for your use cases though, then that is great.

As for your library's name, you may want to consider a name that is not so close to RequireJS. It may get confused with RequireJS, so it may be harder for people to find your library.

WOW, thanks, i just work on "my library" since yesterday. it only a piece of another project. domain cross-over will to be not a big problem, because we plan to transfer almost data (pics, scripts, etc) via websocket, all on the same domain.

yes, you have right, eval is a great function, but i self turn mad with it if i need to debug something.

In our project, we need to cache a lot of data, pictures, etc. and if the users only refresh the page, we don't want to charge the server with this request. only if the cache or the storage are empty.

regards

johnjbarton pushed a commit to johnjbarton/requirejs that referenced this issue May 3, 2011

Allow scripts like Backbone to be able to test for if module.exports …
…and have it work. It means the exports vs. return rules stricter but that is a good thing, will document in release notes. Closes #86

Hi, again :-)

I have read in detail about how your library works, about the api, and xhr, and cross-domain, and ..., and ....
reaching the conclusion both libraries have similar features but different purposes.

You are right, XHR doesn't support cross-domain, and I don't plan to integrate this support in my library, as the target of this is to run the code on closed modules, and cache the scripts into the HTML5 These Storage to reduce data traffic. If i load each script with the document.createElement function, i can't access to the source, i need the source to save it into the storage.

About the functionallity of the library: the parent module only need to know the path of the child, and the child module doesn't need to know who called him. the child module can call anothers 'brothers' (modules on same path, same folder) easily ans shortly. Alias-names for the modules can be defines with the function unshift (almost like nodejs).
Unfortunately ALL this works only in the same domain, just like every computer on the world, if I want run the code or only read data from another machine, first i need to request the necessary permissions. This works if the other server allow XHR requets, with the header "Access-Control-Allow-Origin", otherwise... forget it.

Libraries like jQuery, dojo, mootools, etc. need to be global, and "cann't be modified", like example, if i load jQuery from http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js to get allways the last one, i cann't modified it to run in closed module, or save then into the storage.
For this problem we have a server-side solution, the proxy... ;-)

Now, THANKS for your advice about the eval function, I've make a better solution to load the script, now runs much better, leaving less traces.

regards

kof commented May 3, 2011

you can cache in localstorage/sessionstorage scripts loaded via script tag too, if the module is wrapped with amd.

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