0

Curl 0.7.3

I got some AMD/CommonJS adapter code from here: supporting both CommonJS and AMD

(function (name, definition) {
      if (typeof module != 'undefined') {
        module.exports = definition();
      }
      else if (typeof define == 'function' && typeof define.amd == 'object') {
        define(name, [], definition);
      }
      else {
        this[name] = definition();
      }
    }('modXyz', {
          sayHi:function (name) {
            console.log('Hi ' + name + '!');
          }
        }
    ));

I'd like to use that code with Curl to make all my code AMD/CommonJS compatible. What I was expecting to be able to do was this:

greeter = curl(['modXyz']); 
greeter.sayHi("Gracie"); 

But the object that curl returns isn't the object I'm expecting. The closest I can get is this:

curl(['modXyz'], function(mod) { window.greeter = mod; }); 
greeter.sayHi("Gracie");

Which seems to defeat the purpose of AMD. Is curl capable of doing something like this? Do I have to use require.js to get it to happen?

Community
  • 1
  • 1
jcollum
  • 43,623
  • 55
  • 191
  • 321

3 Answers3

1

CurlJS and RequireJS both support the AMD AND CommonJS require call patterns:

The difference between CJS and AMD is the use of Array as wrapper for the list of dependencies. Also note that in CurlJS's config you can alias curl to require to make your code fully compatible with AMD spec. Observe:

CJS require call pattern:

var a = require('name')

ONLY CurlJS AMD require call pattern:

var promise = require(['name'])
promise.then(callbackFunction)

This commonly can be shortened to:

require(['name']).then(callbackFunction)

Note, CurlJS's returning of a Promise object is NOT part of the AMD spec. The AMD spec does not seem to prescribe a return value for AMD-style require call.

Both, RequireJS and CurlJS support standard AMD require call patterns:

require(['name'], callbackFunction)

Again, note the use of Array as a flag that this is an AMD style require. That triggers different logic for return value.

To make your thing work as you want, on CurlJS, your code must be inside a module that is wrapped as if it's a CommonJS module, use:

define(function(require) { ... });

I am told that in that case, the require you get behaves like CommonJS style require - sync. In reality, what happens behind the scenes is the loader scans the factory function for things you require and folds them into list of requirements for that define. Then, inside the factory you can do:

var greeter = require('modXyz'); 
greeter.sayHi("Gracie");

Note, the "sync" behavior is still an illusion, as what actually happens is that detected required resources are preloaded.

On RequireJS even global require can be called CJS style: var resource = require('resource/path'), but only after you have already loaded it before.

In other words, the loading always happens async in both CurlJS and RequireJS before the factory function runs, but in RequireJS you can use that almost everywhere, while in CurlJS it's very very niche scenario.

ddotsenko
  • 4,926
  • 25
  • 24
  • Very thorough response, thanks. I addressed a bit of this in the OP: "But the object that curl returns isn't the object I'm expecting." I tried it again just now with `var greeter = curl(['mod']); ` (object loads correctly) and `greeter.sayHi` and got undefined back. The object that curl is returning has `config`, `next` and `then` functions. It doesn't look like curl is returning the object that I told it to load. – jcollum Feb 14 '13 at 17:56
  • @jcollum Just in case you are not sure what I mean by lack or presence of "array brackets", examples: `require(/*no bracket*/'a'/*no bracket*/)` compared to `require(/*bracket!!! ->*/['a']/*<-- bracket!!!!!*/)` – ddotsenko Feb 15 '13 at 17:53
  • I tried it both ways, curl returns the same object in both cases. – jcollum Feb 15 '13 at 18:40
  • can you check what version of curl you are using? – jcollum Feb 15 '13 at 18:40
  • @jcollum Apologies. The use case for `var a = require('o')` is indeed very narrow in CurlJS. Only special kind of local `require` appear to support CJS style call. The module must be wrapped into `define(function(require) { ... })` <- literally in that pattern, in order for it to get a special local `require` that could do CJS style calls. My memory played the trick on me and I confused this with how things work on RequireJS side. :( – ddotsenko Feb 17 '13 at 21:55
  • @ddotsenko: the fact that curl doesn't declare a global `require` object does not violate the AMD spec. The global `require` is problematic and causes nothing but confusion (as we can see in this discussion :) ). https://github.com/amdjs/amdjs-api/wiki/AMD – unscriptable Mar 06 '13 at 20:44
1

Because the browser is remote from its resources, it has to either block the main thread while it fetches those resources or it has to fetch them asynchronously. Since we should never block the main thread (effectively making the browser unresponsive), we have to fetch resources async. (It's the same with any AMD loader, RequireJS, dojo, etc.)

Therefore, things like the following just can't work:

var foo = require('lib/foo');

Well, it can't work in the usual global-ish space you may be used to in browsers. It can, however, work in a controlled environment such as inside an AMD module.

Step 1: write your code inside of modules.

Step 2: write a bootstrap module to launch your app.

<script src="lib/curl/src/curl.js"><script>
<script src="myapp/run.js"><script>

Inside run.js:

// curl 0.7.x requires a named module here ('myapp/run')
define('myapp/run', ['curl'], function (curl) {
    curl.config(
        baseUrl: '',
        packages: [ /* configure your 3rd-party packages */ ],
        paths: { /* configure any non-package 3rd-party libs here */ },
        preloads: [ /* put modules that *must* be loaded first here (e.g. shims) */ ],
        main: 'myapp/main'
    });
});

"myapp/main" could then look something like this:

define(function (require) {
    var view1 = require('myapp/view1/controller');
    view1.render();
    view1.placeAt('body');
});

Inside the main module (or any AMD module), the require acts more like I think you're expecting. Note that this is a special AMD signature. You can't specify dependencies in the define and expect the require to behave like this. You have to specify your dependencies in only one way. This will not work:

define(['myapp/view1'], function (view1) {
    var foo = require('foo');
});

Hope this helps.

-- John

unscriptable
  • 766
  • 6
  • 12
0

the curl function is asynchronous, and returns a promise. So you can only get the result either as a callback you pass to curl, or with a callback you pass to then (called once the promise has been fulfilled.

curl(['dep1', 'dep2', 'dep3' /* etc */], callback);

or

curl(['dep1', 'dep2', 'dep3' /* etc */])
    .then(callback, errorback);
Pascal Belloncle
  • 11,184
  • 3
  • 56
  • 56
  • Hmm, that seems quite a bit different from how require does it. Do I have to attach the result object to the window if I want to use it outside of the callback? I've tried returning it in the callback and it didn't work. – jcollum Feb 14 '13 at 00:56
  • This is not related to require, but how curl.js is implemented. You might be better off using a synchronous variant such as jQuery.ajax with async set to false. – Pascal Belloncle Feb 14 '13 at 00:58
  • Well that's just making me more confused. It doesn't seem like require.js is synchronous. – jcollum Feb 14 '13 at 01:00
  • require.js only loads the code. Once the code is loaded and you can execute it, require.js is no longer involved. Then it is only a matter of how you use curl. Fully unrelated. – Pascal Belloncle Feb 14 '13 at 01:03
  • curl.js and RequireJS both have globals that are *async*. Once you are *inside* an AMD-wrapped CJS module or a classic AMD module that has already listed its dependencies, the *local* `require` variable can be used sync. (Note the difference between "global" and "local".) – unscriptable Mar 06 '13 at 20:40