duojs / duo

A next-generation package manager for the front-end
3.42k stars 118 forks source link

Easier example HTML #419

Closed johntron closed 9 years ago

johntron commented 9 years ago

Adds --with_require to make example HTML easier. See https://github.com/duojs/duo/issues/350

For example, you could run:

$ duo --with_require cursor-move index.js

Then use this in example.html:

<script src="build/index.js"></script>
<script>
var Cursor = require('cursor-move');
...
</script>
matthewmueller commented 9 years ago

Hmm, I think I'd prefer to just export require. So instead of only allowing you to require one file (cursor-move) I think you should be able to require anything. In your example, it would probably change to:

<script src="build/index.js"></script>
<script>
var Cursor = require('root/index.js');
var Emitter = require('emitter');
...
</script>

Open to ideas though, cc/ @duojs/owners

dominicbarnes commented 9 years ago

Exporting require is a really good idea imo, especially if we ever want to support bundling.

dominicbarnes commented 9 years ago

If we're concerned about clashing in the global namespace, perhaps we could create a duo namespace, and add a var require = duo.noConflict() (ala jQuery -> $) in cases where require is in some sort of flux.

stephenmathieson commented 9 years ago

i'd much rather export require than duo

dominicbarnes commented 9 years ago

@stephenmathieson We'd export both, just like jQuery exports both $ and jQuery. But duo.noConflict() would exist just in case something else decides to overwrite require.

stephenmathieson commented 9 years ago

-1 from me. component did just fine only exposing require.

if a specific env already exposes require, why not do something like this:

build.js:
  echo ";(function(){" > $@
  duo < index.js >> $@
  echo "window.duo = require;" >> $@
  echo "})();" >> $@
dominicbarnes commented 9 years ago

Yeah, multiple libraries clobbering require is probably not nearly as common/likely as the multiple JS libraries fighting over the $ var.

johntron commented 9 years ago

Yeah, exposing require would be great - any idea how? Internally, it doesn't seem to maintain a list of names for each module:

require('component/model') Uncaught Error: cannot find module "component/model"

window.require.modules Object {1: Array[2], 2: Array[2], 3: Array[2], 4: Array[2], ... }

window.require.cache Object {1: Object, 2: Object, 3: Object, 4: Object, ... }

You have to know the numeric ID to use:

require(2) function createModel(name) { ... }

There's this part in require.js:

// expose as `name`.
if (name) cache[name] = cache[id];

... but with my testing, name is never set.

matthewmueller commented 9 years ago

... but with my testing, name is never set.

name is only set for standalone modules: https://github.com/duojs/duo/blob/master/lib/duo.js#L465-L469

I'm not exactly sure how we'd support this feature at the moment, but ideally it would be as flexible as node's. I'm not sure we have this flexibility using pre-compiling, so at the very least, I think it should be able to require any top-level entry module. an additional nice-to-have would be requiring any component. That's trickier though, because the install and build step are merged.

I think we'd need to modify duo-pack to support this feature, with potentially a few modification here: https://github.com/duojs/duo/blob/master/lib/duo.js#L461-L474

johntron commented 9 years ago

I've been working on a better implementation. Currently, I have window.require() working (see see johntron/pack@a2b5909), but the paths aren't intuitive – they have to match the include paths of the packed code. This means, for example, if my compiled code contains require('component/model'), I have to type exactly that in the browser. I cannot for instance type require('component/model:index.js') from Chrome's console. It also means some really strange paths are possible (i.e. require('./2.js:component/model:index.js')), but I'm not too concerned with this, since this is more for debugging purposes.

The way this currently works is by generating a lookup (require.lookup) to map component names to the numeric IDs used in the packed code. To get require() working in the browser for paths that weren't explicitly used in the compiled code, I was thinking of generating a big long list of all the possible paths; however, I seem to remember Component doing this, and my compiled code was massive as a result.

Instead, I'd like to resolve paths inside require() if it's feasible. Duo has already done the heavy-lifting of downloading dependencies and putting them in the right spot, so I'd need to write a couple of methods to look up files using relative paths and component paths. I found this changeset for Component that might help.

polarathene commented 9 years ago

Just like to point out that you can encounter require() conflicting in the namespace with Atom-Shell. As it can provide node require() js calls on the frontend, which has caused some problems loading in js libs via script tags that have module detection and completely break unless loaded via require().

Node's doesn't appear to support cdn urls so I'm looking for an alternative, being able to require via an alternative namespace could be handy. Either that or I'm too new to module loaders, majority seem to require a build step, it doesn't seem that I can load a script via url in the client and assign it to a var var momentjs = require( 'url/to/moment.min.js' ). Being able to use duo() or duo.require() would be nice.

dominicbarnes commented 9 years ago

If we decide to expose require globally, I think we'll approach it a little differently. Since this has been open for so long, I'm just going to close it.