Skip to content
New issue

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

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CanJS and AMD #46

Closed
jeffrose opened this issue May 29, 2012 · 22 comments
Closed

CanJS and AMD #46

jeffrose opened this issue May 29, 2012 · 22 comments
Assignees
Labels
Milestone

Comments

@jeffrose
Copy link

Currently CanJS seems to have incomplete support for AMD. The core files, e.g. can.jquery.js, define themselves if an AMD loader is present.

define( "can", [], function () { return can; } );

However they do not set any dependencies. For libraries that support AMD, e.g. jquery, dojo, etc., the core files should list them as such.

define( "can", [ "jquery" ], function () { return can; } );

Otherwise it becomes necessary to do a workaround to make sure the dependent library is loaded before loading canjs.

Similarly, canjs plugins need to register themselves as AMD modules which depend on the core canjs module.

define( "can/construct/super", [ "can" ], function () { return null; } );

-Jeff

@justinbmeyer
Copy link
Contributor

Yep. Ralph, can you take a look at this?

Sent from my iPhone

On May 29, 2012, at 1:24 PM, Jeff [email protected] wrote:

Currently CanJS seems to have incomplete support for AMD. The core files, e.g. can.jquery.js, define themselves if an AMD loader is present.

define( "can", [], function () { return can; } );

However they do not set any dependencies. For libraries that support AMD, e.g. jquery, dojo, etc., the core files should list them as such.

define( "can", [ "jquery" ], function () { return can; } );

Otherwise it becomes necessary to do a workaround to make sure the dependent library is loaded before loading canjs.

Similarly, canjs plugins need to register themselves as AMD modules which depend on the core canjs module.

define( "can/construct/super", [ "can" ], function () { return null; } );

-Jeff


Reply to this email directly or view it on GitHub:
#46

@jeffrose
Copy link
Author

If you need guidance on adding optional AMD compatibility to code, check out https://github.com/umdjs/umd

@ralphholzmann
Copy link

Hey, yep. This is a good call. I'm just wondering how to support this for all the different library builds. If I recall correctly, jQuery and Dojo support an optional AMD call, but does Zepto, YUI, MooTools?

I'll work on this this afternoon.

@jeffrose
Copy link
Author

jeffrose commented Jun 5, 2012

I believe only jQuery and Dojo support it right now.

YUI has it's own non-AMD loader.

Zepto seems unwilling to add AMD support at this time (madrobby/zepto#342).

MooTools seems to be in the middle of adding support (mootools/mootools-core#2102).

That being the case, you could either not list those libraries as dependencies in your define() or make certain assumptions:

  • Developer is using RequireJS 2.0+ (or equivalent), which does not need loader plugins for non-AMD files
  • Developer has shimmed the non-AMD library in configuration (http://requirejs.org/docs/api.html#config-shim)
  • Developer used the name of the library in all lowercase as the module name

This would result in:

define( "can", [ "zepto" ], function () { return can; } );
define( "can", [ "mootools" ], function () { return can; } );
define( "can", [ "yui" ], function () { return can; } );

The only other choice would be to make it configurable in some way. That is, exclude the dependency unless the developer has set a value somewhere and use that value.

Doing that would open it up to developers that are not using RequireJS 2.0+ as they could set the value to include loader plugins.

// Load Zepto using the WrapJS loader plugin in RequireJS < 2.0
define.amd.Zepto = "wrap!zepto"
...
var deps = [];
define.amd.Zepto && deps.push( define.amd.Zepto );
define( "can", deps, function () { return can; } );

In my example, I am bastardizing the define.amd (https://github.com/amdjs/amdjs-api/wiki/AMD#wiki-defineAmd) object a little. James Burke (@jrburke) might have a better suggestion.

For plugins, it should not matter as they only need to list "can" as a dependency.

@jeffrose
Copy link
Author

jeffrose commented Jun 5, 2012

Looking over my own reply, this may make more sense:

// Load Zepto using the WrapJS loader plugin in RequireJS < 2.0
define.shim = define.shim || {};
define.shim.Zepto = "wrap!zepto";
...
var deps = [];
define.shim && define.shim.Zepto && deps.push( define.shim.Zepto );
define( "can", deps, function () { return can; } );

@jrburke
Copy link

jrburke commented Jun 5, 2012

Some quick notes, related to upgrading existing libraries:

  1. I suggest not using named modules for "can", use anonymous definitions. So prefer this:
define([], function () {});

not this:

define('can', [], function () {});
  1. I would not put anything in the define.amd area. That is just for a loader to set up, and the define.amd.jQuery thing was a special allowance given the high usage of jQuery even multiple versions on a page.

  2. I would not use the define() API if YUI is being used, but instead favor their module format. Since Zepto is supposed to be a replacement for jQuery, and if CanJS only uses APIs on it that also map to jQuery's APIs, then I would just use the 'jquery' name as the dependency. The end developer can use the AMD loader and configure it so that 'jquery' resolves to Zepto if they want. If you need instructions for RequireJS, I'm happy to provide them. So for the jQuery and Zepto options, do something like:

if (typeof define === 'function' && define.amd) {
    define(['jquery'], function ($) {});
}

If you need help fitting that in to your existing code, feel free to give me a pointer to where or the ideal way you would like to see it done and I can help out. Although hopefully the aforementioned umdjs/umd code templates help.

For Dojo, I would try to be specific about what modules you need from it, if that is an option:

    define(['dojo/array', 'dojo/event'], function (array, event) {});

Not sure if those are the right module IDs but hopefully that conveys the idea.

Similar for MooTools, although I am not experienced enough with that library to know. But if unsure, if they export one single object that has the methods you need:

    define(['mootools'], function (mootools) {});

@justinbmeyer
Copy link
Contributor

@jrburke Thanks for the suggestions! Your insight is super helpful.

Why should we not used name modules? I thought this was so people could have multiple modules in the same file.

When would be the appropriate time to use named modules?

For Zepto, we have a bunch of additional modifications to add features that jQuery supports. I'm not sure if knowing that changes anything ...

Thanks again!

@jrburke
Copy link

jrburke commented Jun 5, 2012

@justinbmeyer some notes here about anonymous modules:

https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon

but basically it is more flexible.

For Zepto, it may make sense to do:

    define(['zepto'], function ($) {});

if you add things specifically to zepto. RequireJS users can map 'zepto' to 'jquery' if they end up somehow with the zepto version of canjs in their jquery-based project. But in that case, you may want to feature detect any additions to the $ object or be sure the changes are non-destructive. Or, just tell them to swap out the canjs with the jquery one. That seems reasonable too.

@jeffrose
Copy link
Author

jeffrose commented Jun 5, 2012

Thanks, James.

The reason for doing...

define( 'can', [], function () {});

... instead of doing...

define([], function () {});

... is that there is no single CanJS core. CanJS sits on top of various libraries, e.g. jQuery, MooTools, etc. If the module was anonymous, you would end up with a core module that has different names, .e.g. "can.jquery", "can.mootools", etc. unless you rename the core file you decide to use. For the sake of CanJS plugins, I would think there needs to be a single, core "can" module.

I think hiding Zepto as jQuery is definitely the way to go.

@jrburke
Copy link

jrburke commented Jun 5, 2012

@jeffrose by impression though is that only one can.* is used in a project at a time, so it is OK if they are anonymous. Also any module ID that is used for a named module should match the file name by default. But I'm still new to the expectations around the use of canjs.

@justinbmeyer
Copy link
Contributor

One thing I've been thinking about is letting people require only the parts of canjs they need. The project is already broken up like this. For example,

require(['can/control'], function(can){
})

But i'd still like people to be able to require(['can']) so they aren't always writing:

require(['can/control','can/model','can/view/ejs','can/route'])

@jrburke
Copy link

jrburke commented Jun 5, 2012

@justinbmeyer there can be a top level can.js file that does all of those require calls and exports a module for those values:

define(function (require) {
    return {
        control: require('can/control'),
        model: require('can/model'),
        ejs: require('can/view/ejs'),
        route: require('can/route')
    };
});

So if they want the whole thing, require 'can' instead of 'can/control'.

@jeffrose
Copy link
Author

This issue has certainly grown bigger than I expected, though I am encouraged by the direction, i.e. being able require individual modules as well as CanJS as a whole. I understand this isn't a priority compared to other issues but I was wondering if there was a plan of attack? Is this something we would see in the next release?

@ghost ghost assigned ralphholzmann Jun 29, 2012
@jeffrose
Copy link
Author

As part of this effort, it would be nice to see can/util/zepto/fill (and similar) broken up into separate files, like can/util/zepto/fill/promise, can/util/zepto/fill/ajax, etc. This would provide the flexibility of letting developers choose how to fill those gaps. For instance, using whenjs for promises.

@justinbmeyer
Copy link
Contributor

I'm not sure that would help as they wouldn't be present on can. You'd hace to create your own to map to the can object.

Btw, deferred is a separate file.

And outside deferred, there's little that could be swapped.

And, we wouldn't want these things in zepto probably, but in can/util directly so they can be used by other base libraries.

Sent from my iPhone

On Aug 12, 2012, at 5:17 PM, Jeff Rose [email protected] wrote:

As part of this effort, it would be nice to see can/util/zepto/fill (and similar) broken up into separate files, like can/util/zepto/fill/promise, can/util/zepto/fill/ajax, etc. This would provide the flexibility of letting developers choose how to fill those gaps. For instance, using whenjs for promises.


Reply to this email directly or view it on GitHub.

@jeffrose
Copy link
Author

Yes, I was thinking you would create your own mapping in the RequireJS config to swap in the implementation you want to use for whatever can was expecting.

Ah, I see Deferred in can/util/deferred but I see the same code in can/util/zepto/fill (along with other code). I have to assume that can/util/zepto/fill must be built from multiple other files.

I do agree that outside of something common like Promises and maybe some language polyfill (e.g. an implementation of Array.reduce), there's little that could be swapped.

Yup, I was just using can/util/zepto/fill as an example.

@saunders99999
Copy link

Hi, will this issue be resolved in the next release?

@patie
Copy link

patie commented Oct 18, 2012

+1

1 similar comment
@ghost
Copy link

ghost commented Oct 29, 2012

+1

@jaxley
Copy link

jaxley commented Oct 31, 2012

+1, and Bump. What is the status of AMD support now?

@rjgotten
Copy link

rjgotten commented Nov 6, 2012

I would really like to know the status of this as well. Will AMD support make it for 1.1 ?

@daffl
Copy link
Contributor

daffl commented Nov 14, 2012

The version 1.1.0 download contains an amd folder that allows you to dynamically load any CanJS components and plugins as AMD modules. See the overview docs for an example on how to use it with RequireJS and jQuery. Let us know what you think and if you find any issues!

@daffl daffl closed this as completed Nov 14, 2012
justinbmeyer added a commit that referenced this issue Oct 10, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

10 participants