IIUC, RequireJS allows its internals to be overridden to do things such as this. curl.js 0.6 (in dev) also allows this. Since there is no standard API for overriding the internals of AMD loaders and AMD builders, you'll have to create something specific to your favorite. :)
I agree that there needs to be more synergy with node.js. Maybe we can convince them to do things our way ... some day. :)
Warning, a bit long:
Here are the (enumerated) questions I think are being asked, but feel
free to correct me:
###########
1) How best to translate code written for node to work in the browser?
---
A more general note/rant first: node's module lookup rules may work
well for a server, but they are not good for a browser. They are still
figuring out what is best for their environment and they have made a
point to explicitly not care what works best in a browser. They are
inwardly focused. That is fine, but I'm also not going to pick up any
of their baggage that makes it harder to program in the browser. Even
though node is hot, it still pales in comparison to all front-end
development -- there are all kinds of people using different kinds of
servers but still targeting the browser, and it is not possible to
write FE programs that are completely separate from the server (mobile
apps).
---
On to specifics of reusing node modules in an AMD loader, how to deal
with paths/module ID resolution:
I can see two paths:
a) When doing the module conversion to wrap the modules in define(),
update the dependency names to match how they will be organized. In
your sample-npmish example, in a.js, for its require('c') call,
rewrite it to require('a/c').
b) Allow setting a config that would help the loader do the
resolution. I think this is the path you want to take. I think this
requires some upgrades to the loaders first to really understand (see
#2 below).
It would be interesting to know what path browserify takes when it
bundles up node modules. Not as a "we should just do that" but just to
get a point of reference. If anyone has more experience with it, feel
free to share.
###########
2) How can modules that may need their own versions of the same module work?
Node solves this via nested node_modules lookups and has the IO
flexibility to try different paths. AMD loaders should avoid multiple
IO lookups to find a module, it does not work on the web.
So the configuration to find a module at exactly one location needs to
be set before the loader starts looking up modules.
There have been proposals on how that might work in the past. I have
found them to be a bit unwieldy and rely too much on paths, where I
would rather have a nested module ID-centric config that probably has
a new wrapping function in the define API. AMD is already more "module
ID"-centric than node's path-centric. But we should start a separate
thread on that (see summary below).
###########
3) Does there need to be a lower level AMD service API (SPI) to
accomplish 1 and 2?
I would rather tackle the node-specific bundling first, work with
concrete use cases and if something general pops up, consider it later
at that time.
Also, I do not think standardizing the SPI for method names would work
unless there was some general agreement on allowing multiple versions
of modules and how they would work and get serialized into a file. So
in other words trying to get some agreement or sketch on #2 above.
For the more more futuristic "there might be something better later",
I expect it will be sufficiently different that we will not get the
SPI interface set up correctly if we try to sort it out now. I think
it is more likely there will be specialized loaders to handle the new
thing and if it seemed generally useful, then consider it more in this
group. In other words, I would like to see concrete implementations
driving the API work.
###########
Summary of how I would like to proceed:
* Start a thread about how to handle multiple versions of the "same"
module being loaded. It has come up before way in the past, but I
think we may be in a better position to work something out now. I have
some ideas, but I will save them for the separate thread. I'll start
that thread later today.
* Start a different thread on how node modules that have a layout like
sample-npmish can be used today. I think it can be done as mentioned
before by doing dependency rewriting in the bundling tool. I'm open to
exploring this more in code too. If it makes sense to send some pull
requests to browserify for this so it outputs AMD that works for this
situation, even better.
It may be that just doing the dependency rewriting is the way to go
for now -- it will work with more loaders, and makes the loaders
simpler, which is good. Since the modules need to be converted to
define() anyway, there is likely greater leverage in doing the hard
work in one well-written converter than have lots of loaders have to
change.
That said, I would like to work out the "multiversion" issue for AMD
for the long term.
> Note, I opened a bug at amdjs-api, because I couldn't think of a better
> place to ask this question, then I remembered this mailing list. The issue
> here is, can be closed if folks feel strongly about it, but guess the
> conversation should take place here for now. Or is there a better place?
>
> https://github.com/amdjs/amdjs-api/issues/6
I went ahead and closed the issue with a link to this thread, since
this is more of a general topic thread, trying to discover what
problems should be solved. Once those are identified, we can add
issues as needed.
James
it is not possible to
write FE programs that are completely separate from the server (mobile
apps).
I can see two paths:
a) When doing the module conversion to wrap the modules in define(),
update the dependency names to match how they will be organized. In
your sample-npmish example, in a.js, for its require('c') call,
rewrite it to require('a/c').
b) Allow setting a config that would help the loader do the
resolution. I think this is the path you want to take. I think this
requires some upgrades to the loaders first to really understand (see
#2 below).
It would be interesting to know what path browserify takes when it
bundles up node modules. Not as a "we should just do that" but just to
get a point of reference. If anyone has more experience with it, feel
free to share.
2) How can modules that may need their own versions of the same module work?
I would rather tackle the node-specific bundling first, work with
concrete use cases and if something general pops up, consider it later
at that time.
Also, I do not think standardizing the SPI for method names would work
unless there was some general agreement on allowing multiple versions
of modules and how they would work and get serialized into a file. So
in other words trying to get some agreement or sketch on #2 above.
* Start a thread about how to handle multiple versions of the "same"
module being loaded. It has come up before way in the past, but I
think we may be in a better position to work something out now. I have
some ideas, but I will save them for the separate thread. I'll start
that thread later today.
* Start a different thread on how node modules that have a layout like
sample-npmish can be used today. I think it can be done as mentioned
before by doing dependency rewriting in the bundling tool. I'm open to
exploring this more in code too. If it makes sense to send some pull
requests to browserify for this so it outputs AMD that works for this
situation, even better.
I went ahead and closed the issue with a link to this thread, sincethis is more of a general topic thread, trying to discover what
problems should be solved. Once those are identified, we can add
issues as needed.
Sorry, Front End -- applies to browser-based web development. Server
code is Back End (BE).
>> 2) How can modules that may need their own versions of the same module
>> work?
>
> If you imagine you've solved the node module resolution problem, then this
> problem is a solved problem. If you haven't solved (or enabled) the node
> resolution algorithm, then yeah, something needs to be done. But I'll
> always be using the node module resolution algorithm so I don't care.
I believe what I proposed in the other thread on a scope API would
allow someone to code up a config that would mimic a nested
node_modules setup, as long as you know what path node chose to load
each module.
> Again, dependency rewriting - aka as parsing, analyzing, and rewriting your
> source - is out of the question. It clearly couldn't work if I used
> variables for the require() parameter, for instance. I'm willing to wrap
> CJS-authored modules, but not to do major surgery on them.
For a generic bundling tool, one that does not require the developer
to hand write the dependencies, it will need to parse out the require
calls to properly find the dependency tree and include those modules
in a bundled package. Since it already needs to parse the require
calls, it can do the renaming.
Variable-based require calls are not parseable by that solution, and
they would likely fail anyway in a browser env because they are
imperative require calls, and should be handled by a callback-style
require([], function(){}).
I agree that it is not a 100% solution, but for modules with
declarative string dependencies (most modules) it would be a workable
solution, particularly for modules that might actually be runnable in
the browser vs modules that depend on node's execution model.
But I understand if you are not interested in pursuing that approach
for your own tooling. I am going to focus my energy on the larger
multiversion issue, but it will likely require more coordination and
time than the "renaming tool" path.
James
It would be interesting to know what path browserify takes when itbundles up node modules. Not as a "we should just do that" but just to
get a point of reference. If anyone has more experience with it, feel
free to share.
If it makes sense to send some pull
requests to browserify for this so it outputs AMD that works for this
situation, even better.
I believe what I proposed in the other thread on a scope API would
allow someone to code up a config that would mimic a nested
node_modules setup, as long as you know what path node chose to load
each module.
For a generic bundling tool, one that does not require the developerto hand write the dependencies, it will need to parse out the require
calls to properly find the dependency tree and include those modules
in a bundled package. Since it already needs to parse the require
calls, it can do the renaming.
Variable-based require calls are not parseable by that solution, and
they would likely fail anyway in a browser env because they are
imperative require calls, and should be handled by a callback-style
require([], function(){}).
Neat, thanks for sharing. Browserify seems to just dump files in there
by their path name. I'll have to think about this more. On first look
though, I dislike the use of full paths for module IDs and the
package.json stuff in there.
>> If it makes sense to send some pull
>> requests to browserify for this so it outputs AMD that works for this
>> situation, even better.
>
>
> Not sure where you're going with this one. Browserify doesn't rewrite my
> modules. It works it magic by embedding the npm module resolution into
> define() and require(). I guess we could see if Browserify could also
> become AMD compliant, but that's not really my goal. I would like AMD
> compliance to mean that it handles npm module resolution, perhaps
> optionally, or via configuration. I'd like to have the universe of npm
> modules available to all users of AMD compliant runtimes.
I just meant that if browserify output something that was close to AMD
already and just required a bit of small massaging, then that would be
useful. However, it is encoding some other node-isms in there that may
not make that path realistic. Needs more thought though.
James
No, not yet, but there are enough implementations that I think share
at least baseUrl and paths that it may make sense to do that now.
> I don't believe Browserify scans. I've written "generic bundling tools"
> which do not scan.
Besides dependency parsing, the only other way I have thought of doing
a generic tool is to actually run the modules and listen for what is
loaded. However, this does not work well once the modules that are
being run normally run in a different environment than the tool. For
instance, processing browser-based modules that may reference "window"
in node.
If there is another way of doing these kinds of tools though, please
let me know. Maybe I could use that path whenever I build my next
tool.
James
Neat, thanks for sharing. Browserify seems to just dump files in thereby their path name. I'll have to think about this more. On first look
though, I dislike the use of full paths for module IDs and the
package.json stuff in there.
<script>var require = {// adjust to root of https://github.com/pmuellr/sample-npmish-projectbaseUrl: 'npmish',packages: [{name: 'a',location: 'node_modules/a',main: 'a.js',packageMap: {c: 'c-a'}},{name: 'b',location: 'node_modules/b',main: 'b.js',packageMap: {c: 'c-b'}}],paths: {'c-a': 'node_modules/a/node_modules/c/c','c-b': 'node_modules/b/node_modules/b/index'},deps: ['./sample']};</script><script src="path/to/dojo/dojo.js"></script>
thanks,
ben...to use dojo to load the structure in the npmish project (assuming the modules were wrapped in define), the configuration would be something like below (i'll keep https://gist.github.com/1450027 updated if i make changes). i haven't actually made any attempt to wrap the modules and test this so it may need some tweaking but this shows the idea.
i saw the browserify sample and the only issue i had with doing the using/dojo was that i'm not sure of your criteria and end goal.
- can i copy the whole directory tree (with wrapped modules) into the using/dojo directory?
- do you want a "production" build (concatenated, minified, etc) or do you just want the bare minimum to get this to load?
- i thought i read a comment of yours recently that said you already have a tool for wrapping the modules so wasn't sure if that factored into this
- how automated does the process need to be? if you add module 'd' do i need to automatically pick that up?
i'm not sure that i'm up to putting very much time into this anytime soon which is why i stopped at offering a config off the top of my head but if i had some more guidance it would help me focus any time i could put into this.