cujojs / curl

curl.js is small, fast, extensible module loader that handles AMD, CommonJS Modules/1.1, CSS, HTML/text, and legacy scripts.
https://github.com/cujojs/curl/wiki
Other
1.88k stars 216 forks source link

Add the possibility for aliases to packages #200

Open skiadas opened 11 years ago

skiadas commented 11 years ago

I thought I'd make an issue about it so that we have it around. It would be nice to be able to refer to a package by multiple names.

Use case: You want to use one package that expects to find 'underscore' and another package that expects to find 'lodash', but you want to use LoDash for both. Currently one would now have to include the lodash specification twice, something like:

packages: [
    { name: 'lodash', location: 'lib/lodash/', main: 'lodash.js' }, 
    { name: 'underscore', location: 'lib/lodash/', main: 'lodash.js' }, 
 ]

Some possible syntaxes:

packages: [
    { name: 'lodash', location: 'lib/lodash/', main: 'lodash.js' }, 
    { alias: 'underscore', for: 'lodash' }, 
 ]

Or incorporate it into the same item:

packages: [
    { name: 'lodash', location: 'lib/lodash/', main: 'lodash.js', alias: ['underscore'] } 
 ]

Or perhaps a separate section:

aliases: {
    'underscore': 'lodash'
}

Or as part of paths with something like:

paths: [
    'underscore': 'alias!lodash'
]
unscriptable commented 11 years ago

Thanks for posting this issue, @skiadas!

I particularly like these two:

packages: [
    { name: 'lodash', location: 'lib/lodash/', main: 'lodash.js' }, 
    { alias: 'underscore', for: 'lodash' }, 
 ]

Or incorporate it into the same item:

packages: [
    { name: 'lodash', location: 'lib/lodash/', main: 'lodash.js', alias: ['underscore'] } 
 ]
briancavalier commented 11 years ago

I think I like this idea, although I'm having a hard time coming up with use cases other than this particular lodash/underscore snafu. Are there other similar use cases, or entirely different ones where aliasing would help?

One interesting thing about this format is that it allows easy one-to-many package-to-aliases. But again, why? Is that even useful?!?

packages: [
    { name: 'lodash', location: 'lib/lodash/', main: 'lodash.js', alias: ['underscore', 'underdash', 'loscore'] } 
]

The separate aliases config is kinda interesting as well:

aliases: {
    'underscore': 'lodash'
}

It's quite compact. Would that allow aliases for both paths and packages?

Here's another to throw into the mix:

packages: [
    { name: 'lodash', location: 'lib/lodash/', main: 'lodash.js' }, 
    { name: 'underscore', aliasFor: 'lodash' }, 
]

I think there are two reasons this variant is interesting:

  1. It uses name in the same way for "real" packages and for aliases (I see that as a good thing, but others may find it confusing??)
  2. It allows curl's object-based package config format (which I prefer) to work similarly/intuitively:
packages: {
    lodash: { location: 'lib/lodash/', main: 'lodash.js' }, 
    underscore: { aliasFor: 'lodash' }, 
}
skiadas commented 11 years ago

I think the { name: ..., aliasFor: ...} form is nice, I don't find it particularly confusing. I do prefer the separate aliases hash myself I think. I could swear requirejs has something similar.

As for use cases, yeah I can't think of too many, but in general I think it might come up whenever there are multiple packages providing essentially the same utilities, and other packages require one but could work with the other. Maybe something similar could occur with jquery and zepto? I don't know enough about zepto.

Another use case could potentially be to for instance support multiple character casing for jquery, say also jQuery, but maybe at that point we're more catering to users being sloppy.

mk-pmb commented 10 years ago

In the meantime, try if this monkey-patch extension of curl helps:

  curl.alias = function (origModName) {
    var addAlias = function (alName) {
      define(alName, addAlias.factory);
      return addAlias;
    };
    addAlias.factory = function (require) { return require(origModName); };
    addAlias.as = addAlias;
    return addAlias;
  };

  curl.alias('zepto').as('jquery').as('jquery2');

Yours, MK

unscriptable commented 10 years ago

Clever :)

Note that build tools (r.js, cram.js, browserify) won't know about aliases. You'd have to explicitly tell the build tools to embed aliases if you want them in the bundle.

mk-pmb commented 10 years ago

Would they recognize it if you alias aliasto require?

  (function myAliasDefs(require) {
    require('zepto').as('jquery').as('jquery2');
    require('lodash').as('underscore');
  }(curl.alias));
unscriptable commented 10 years ago

The build tools look for require("id") and define("id"?, []?, factory). They won't see the .as("id") parts unless you tell them to. :)

mk-pmb commented 10 years ago

Oh right... I guess with the second version they would add zepto and lodash to the pack, but would add jquery and underscore as well if they are require()d from within other files.

csnover commented 10 years ago

n.b., AMD Common Config for this is map, which has the added benefits of being able to perform exclusions and also remap/alias entire module segments, not just packages:

{
  map: {
    'some/module/useRealJqueryForThisOne': {
      'jquery': 'jquery'
    },
    '*': {
      'underscore': 'lodash',
      'jquery': 'zepto'
    }
  }
}
mk-pmb commented 10 years ago

Thanks for introducing me to that part of AMD! I failed to find any "map" in the spec in the AMD wiki, but there's a Common Config spec in only draft stage, so I assume you mean that one?