Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Option to use requirejs path resolution logic #4

Open
jrburke opened this Issue · 19 comments

5 participants

James Burke Stephen Tudor Joe Grund Agelos Pikoulas Herbert Vojčík
James Burke
Owner

This came up from a use case from @chrispitzer:

If you have modules coded to use amdefine and they work in reqiurejs, if they need to run in something like a busterjs test, or anything that has a command line command that just jumps right into node (cannot insert requirejs in the middle), it would be nice to be able to have the same config used in requirejs loading to work in that scenario, since the modules may not be in the node_modules area for node to find.

First idea: amdefine could read an environment variable that specifies a file that has the requirejs config, and it would use that for loading modules before handing off to node's loader if it cannot find it at that config.

Stephen Tudor

+1, I'm running into this very situation when running a Mocha test from command line.

Joe Grund

+1, Having this will make amdefine a usable option for me.

Agelos Pikoulas

+1, I have stumpled upon this and wondered if I am missing something...

As I understand it, our true options for node+browser AMD modules right now is amdefine and the define [dep], (dep)-> suggested format to support baseUrl & paths. Otherwise we'll have to be using requirejs=require 'requirejs'; requirejs.config {baseUrl: 'my/app/path/'} and requirejs [dep], (dep)-> in each module, after we check we are on node or browser etc etc, which wont make them very portable and boilerplate free... Or am I missing something else ?

I am not so sure about the environment variable though... what if you have multiple apps, with different paths ? Perhaps some convention over configuration is needed - something like
'on first use, visit each subpath backwards and read [requireJSConfig.js].' or something...

James Burke
Owner

@anodynos the other option is to use a UMD approach to detect node vs. AMD when writing a library.

When loading code for tests, you can use requirejs in node to kick off module loading.

I suggest that last path if you have multiple apps. I think the env variable is useful just for the cases where something, like test frameworks, want to be the top level script executed by node, vs. a script that you make to load requirejs then load the test framework.

Agelos Pikoulas

UMD could be a solution, but it has one great obstacle and a ugly caveat.

  • The caveat is that it looks really awful, its way TOO much boilerplate for anyone's real usage. People are cursing it, and I can see why. They will start calling it MUD. I see it being used only as an after step in some automated build that is added around the non-amd code to modularize and amdify the deployment (like this screencast), rather than being used to modularize code structure during development.

Coming from the java territory, I want to be able to write every logically different code chunk on a different file, without so much boilerplate. And trust me, I 've done my boilerplate with java :-)

  • The obstacle with is that when running on browser/AMD you can use 'package-relative' dependencies views/generic/PersonView', but when running on node you can only use file-relative../../../PersonView` which is awful. What does that last ../../PersonView point at? it blows my mind!

If you have to keep the two 'formats' in sync everytime you add a dependency, I am sorry, its not usable.

Using requirejs on node confuses me : where is define defined ? How can I define a module that is executed on node ?

In overall, how can I write once and without having any other concern, have it run seamlessly on both browser and node (which is what most people use anyway). It's such a shame that so much work has been done with AMD (& node), but they still can't really work together. See more on this issue

So, after realizing the void here, I am in the middle of writing a simple converter, where you write AMD modules and it translates them to UMD that run everywhere, taking care of the details.. I will update when v0.0.1 is done :-)

James Burke
Owner

A path that may be workable, which I have not tried because I did not want to hijack that lower level part of node, is to just replace the require.extensions entry for 'js' and have it auto-inject the define() definition for that module. I would not recommend that for a generic module that should be used in node, but for applications that want to use AMD, allow them to require('') this thing in their top-level script, and then amdefine's version of define() would be available for all modules loaded, even if they do not require('amdefine'). So for something like mocha tests, at the top of your test file if you do a require('auto-amdefine') (name TBD), it would do this require.extensions change in node.

I'll look into that, to see what it feels like, but will likely do it as a separate module/repo that uses amdefine as part of it.

@anodynos I appreciate the frustration though. I think this is just a transition period as javascript works out a good general module solution. Even when if it gets sorted, say in ECMAScript modules, I do expect some roughness around paths assumptions in different environments, but we'll see how it evolves.

James Burke
Owner

Thinking more about this, that require.extensions path is not really an improvement over just doing a require('requirejs') in that top level script, it will amount to the same effect.

So I think the trick is to work out examples to show to set up a module for mocha tests that does that require, sets up the requirejs config for that set of tests, then exports that requirejs require out of that setup module for use by the mocha tests.

That would result in all your modules just using define(). Then, amdefine is just for create a lib that stands alone in node, and that may inline other modules as part of that file, but if just a simple one module script, the UMD pattern may work better.

Agelos Pikoulas

What skips me though is how one can use both define [], -> and require [],-> on the same file on node.
Excuse my coffescript :-)

James Burke
Owner

If it is code you control, it is app code, and not something you want to publish as an npm-installabled package for other people who may not be using requirejs/AMD, then loading requirejs then having it load the modules in node works out. If you are using coffeescript, you can use the coffeescript AMD loader plugin to first convert to JS, then have requirejs load it as a module.

However, if it will be a reusable piece of code that is npm-installable into possibly non-AMD projects, then amdefine or the UMD pattern is a better fit.

Herbert Vojčík

@jrburke I'd like to return to @anodynos's question: where is define defined when using requirejs for Node, or IOW, how can I have both requirejs and define in node at once (same as in browser)? So that I can load modules from configured paths and at the same time have possibility to define them with id and having require catch them?

Once crazy idea seems to be include both and requirejs.config({nodeRequire:define.require});, is this how it should be done?

James Burke
Owner

@herby: if the .js file is loaded via a top-level requirejs() call, then requirejs loads it and define is available to it. If the .js file is loaded with node's loader, then there is no define, and amdefine can be used for that case.

Once node' require() is used to load the file, I cannot inject any define() for that file, as the define() needs to be specific to the module being loaded. However, node does not give enough hooks to make this possible.

Herbert Vojčík
James Burke
Owner

@herby if amdefine is conditionally set to define mentioned in the usage docs, then I expect that to work out. If you are inlining amdefine in the file with your define()'d modules, and always calling it, I expect there will be problems.

Herbert Vojčík
James Burke
Owner

Sorry I missed this in my earlier pass. I think I understand, trying a full code block, so that hopefully it reflects what I think the question is. Say I have main.js and it is run in node.js via node main.js, and main.js looks like this:

var requirejs = require('requirejs');

//Define a named module before starting other module loading
requirejs.define('foo', function(){});

//Later in main.js, this should work, used the named define above
requirejs(['foo'], function (foo){});
Herbert Vojčík
James Burke
Owner

If main.js uses requirejs to load the file, then define() should be available to that other file, and 'foo' should be visible to it. So, if in the above main.js, it did requirejs(['something']) and then something.js was found via the requirejs config, then this would work:

//in something.js loaded by requirejs
define(['foo'], function(foo){});

However, what will not work is if 'something' was a node module that was loaded by a requirejs() call, but not found via requirejs config. In those cases, requirejs delegates to node's require() to load it. That is a fundamental limitation when working with Node. I have no plans to do some sort of deep injection hack into the Node module system to add things to their namespace as there is not guarantee that would work in the future if they make changes, or even disallow it if it worked today.

But if 'something' is loaded by requirejs and found via its config (so that it exposes define() and the set of modules it knows about) then the above should work.

Herbert Vojčík
James Burke
Owner

Oh, this again looks like misunderstanding... of course I want to define foo in main.js. Just it should be requireable anywhere in the require tree, nor just in main.js. Which is what one awaits from define. Which is not present in main.js (such that requirejs honours it, which is not true in case of amdefine's one). Which is the only problem.

Sorry, yes I am getting confused. If you wanted to get it sorted out, then it will be best if you could construct a test that demonstrates the problem, what you would like to work, and then I could probably provide better feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.