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

0.7.3: curl throwing "multiple anonymous defines" error when loading css file #172

Closed jcollum closed 11 years ago

jcollum commented 11 years ago

I have a css file at : http://localhost:3001/css/style.css

 script
      curl = {
                      baseUrl: "/lib",
                      paths: {
                        "localcss": "/css",
                        "localjs": "/js",
                        "util" :"/js/util",
                        "socket": "/socket.io" }
          };
    script(src='/lib/curl.js')
    script
      curl({ preloads: ['curl-debug.js'] }, ['js!//cdnjs.cloudflare.com/ajax/libs/jquery/1.9.0/jquery.min.js'])
        .next([
        'js!bacon.js'
        ,'js!underscore-min!order'
        ,'js!socket/socket.io.js'
        ])
        .next([
          'css!/localcss/style.css' // <--------------- this line
         ])
        .then(function success(){  ....

The line marked will throw a m.a.d. error for all of these variations:

'css!style'
'css!localcss/style'
'css!localcss/style.css'
'css!style.css'
'css!/css/style.css'
'css!css/style'

Also tried moving the css file to /styles/style.css and changing the path accordingly, same error.

unscriptable commented 11 years ago

Hey Justin,

You have to start thinking about IDs instead of URLs. :)

The css! plugin defers resource resolution to curl, and, therefore, uses the same lookup rules as modules. Since curl assumes that modules are referenced by ID, the css! plugin assumes you are handing it IDs, too.

However, the leading slash in '/localcss/style.css' eliminates the possibility that it has been handed an ID because IDs don't start with slashes. Instead, it assumes you gave it a URL and skips all the module ID-to-URL resolution rules.

Hm, it seems there's a bug in the current master branch that prevents minified jquery from being used as a preload. The dev branch seems to work, though, according to some quick tests. (There were some changes made to this code.)

Use the dev branch and try this:

<script>
    curl = {
        baseUrl: "/lib", // you should consider pointing this to your app code, instead
        paths: {
            localcss: "../css",
            localjs: "js",
            util : "js/util",
            socket: "socket.io",
            jquery: '//cdnjs.cloudflare.com/ajax/libs/jquery/1.9.0/jquery.min'
        },
        preloads: [
            'curl-debug.js', // did you move this to the root?
            'jquery'
        ]
    };
</script>
<script src='/lib/curl.js'></script>
<script>
    curl([
        'js!bacon.js',
        // !order needs to be specified on all scripts that need ordering. what else needs it here?
        'js!underscore-min!order', // you should probably be using lo-dash instead
        'js!socket/socket.io.js',
        'css!localcss/style.css'
        ])
        .then(function success(){  /*...*/ });
</script>

I suspect the error came from the jquery issue, not the css resource, btw.

-- John

unscriptable commented 11 years ago

Let me know if this fixes the problem, Justin. Please close the issue if it does. :)

jcollum commented 11 years ago

You have to start thinking about IDs instead of URLs. :)

Well, I am, but I don't see the relevance here -- why would I be thinking in terms of IDs for plain js and css files? All the things I'm loading in this example are 3rd party js files or my own css.

I'll try your suggestions and let you know.

jcollum commented 11 years ago

Actually I'm wondering if this is a bug in .next. I'm doing this, essentially:

curl(['js!nonAMD.js'])
    .next(['dep1', 'dep2', 'dep3'], function (dep1, dep2, dep3) {
        // do something before the dom is ready
    })

Code looks like this:

    script
      curl = {
             baseUrl: "/js",
             paths: {
                app: "./app",
                jquery : '//cdnjs.cloudflare.com/ajax/libs/jquery/1.9.0/jquery.min',
                lib : "./lib"
                },
             pluginPath: 'lib/curl/plugin',
             preloads: [ 'lib/curl/debug.js', 'jquery' ]
          };
    script(src='/js/lib/curl.js')
    script
      moduleLoadError = function(err) {console.log("load config or logger failed: " +  err.message);}
      curl([
          'js!lib/bacon.js',
          'js!lib/underscore-min',
          'js!/socket.io/socket.io.js'
        ])
        .next(['app/config', 'lib/jslogger.min'],
          function(Config, Logger){ alert('config'); window.config = new Config(); window.logger = Logger; window.logger.useDefaults();})

The window.config object is never setup. However if I do this:

curl(['prev/config', 'lib/jslogger.min'],                                                                                    
  function(Config, Logger){ alert('config'); window.config = new Config(); window.logger = Logger; Logger.useDefaults();})   

the window.config is set up correctly. I've changed my curl over to use the dev branch version.

unscriptable commented 11 years ago

Did you try using the error handler on the .next()?

      curl([
          'js!lib/bacon.js',
          'js!lib/underscore-min.js',
          'js!/socket.io/socket.io.js'
        ])
        .next(['app/config', 'lib/jslogger.min'],
          function(Config, Logger){ alert('config'); window.config = new Config(); window.logger = Logger; window.logger.useDefaults();}
          moduleLoadError
      );
jcollum commented 11 years ago

Yeah, I added this line:

 .next(['css!/css/main.css'], (function() {
    return console.log('css load complete');
  }), moduleLoadError)

And got an error. Here's the debug:

curl getDeps return: Promise {then: function, resolve: function, reject: function, progress: function, id: ""…}
curl getDeps arguments: [Promise]
curl fetchDep arguments: ["css!/css/main.css", Promise]
curl toAbsId arguments: ["css!/css/main.css", "", Begetter]
curl toAbsId return: lib/curl/plugin/css!/css/main.csscurl resolvePathInfo arguments: ["lib/curl/plugin/css", Begetter]
curl resolveUrl arguments: ["./lib/curl/plugin/css", Begetter]
curl resolveUrl return: /js/./lib/curl/plugin/csscurl resolvePathInfo return: Object {config: Begetter, url: "/js/./lib/curl/plugin/css"}
curl createResourceDef arguments: [Begetter, "lib/curl/plugin/css", undefined]
curl createContext arguments: [Begetter, "lib/curl/plugin/css", undefined, undefined]
curl createContext return: Promise {then: function, resolve: function, reject: function, progress: function, id: "lib/curl/plugin/css"…}
curl createResourceDef return: Promise {then: function, resolve: function, reject: function, progress: function, id: "lib/curl/plugin/css"…}
curl checkToAddJsExt arguments: ["/js/./lib/curl/plugin/css", Begetter]
curl checkToAddJsExt return: /js/./lib/curl/plugin/css.jscurl fetchResDef arguments: [Promise]
curl getDefUrl arguments: [Promise]
curl getDefUrl return: /js/./lib/curl/plugin/css.jscurl loadScript arguments: [Promise, function, function]
curl loadScript return: <script type=​"text/​javascript" charset=​"utf-8" async src=​"/​js/​./​lib/​curl/​plugin/​css.js">​</script>​
curl fetchResDef return: Promise {then: function, resolve: function, reject: function, progress: function, id: "lib/curl/plugin/css"…}
curl fetchDep return: Promise {then: function, resolve: function, reject: function, progress: function}
curl getDeps return: Promise {then: function, resolve: function, reject: function, progress: function, id: ""…}
curl fixArgs arguments: [Arguments[1]]
curl fixArgs return: Object {id: undefined, deps: Array[0], res: function, cjs: undefined}
load failed: Multiple anonymous defines in url load-libraries.js:6
curl: ********** modules waiting: 1 curl: ********** module waiting: lib/curl/plugin/css  

Also tried it from the command line:

curl("css!./../css/main.css", function(){console.log("success");}, function(err){console.log(err.message);})
curl("css!css/main.css", function(){console.log("success");}, function(err){console.log(err.message);}) 
curl("css!/assets/css/main.css", function(){console.log("success");}, function(err){console.log(err.message);})

All resulted in the Multiple anonymous defines in url error. From looking at the server logs, it's never requesting the object.

I'll try debugging a bit and see if I can come up with something.

unscriptable commented 11 years ago

What is "load-libraries.js" and where is it being referenced?

Can you verify that "/js/./lib/curl/plugin/css.js" is the same as this? https://github.com/cujojs/curl/blob/dev/src/curl/plugin/css.js

jcollum commented 11 years ago

What is "load-libraries.js" and where is it being referenced?

It's loaded after the curl script tag. Moving it into a script not-src tag didn't change anything.

Can you verify that "/js/./lib/curl/plugin/css.js" is the same as this?

Yep, they match.

What I'm seeing is that the code is going to:

css.js 515: define(/=='curl/plugin/css',==/ { curl.js 1268: _define(args); curl.js 1232 ish: id = args.id; if (id == undef) { if (argsNet !== undef) { argsNet = { ex: 'Multiple anonymous defines in url' }; }

Seems to be hitting argsNet = {ex:} because the id is undefined. I wonder if it's loading the cram plugin or missing that part.

unscriptable commented 11 years ago

Does load-libraries.js have a define() call in it? If so, that could be the cause of the problem. Calling define() (without an id) by a script that wasn't requested via curl() would cause that error. The error would occur once another define() is called.

jcollum commented 11 years ago

Does load-libraries.js have a define() call in it?

Nope. It looks like:

  var loadComplete, moduleLoadError;

  moduleLoadError = function(err) {
    console.log(" load failed: " + err.message);
  };

  loadComplete = function(msg) {
    return console.log("scripts finished: " + msg);
  };

  curl(["js!lib/bacon.js", "js!lib/underscore-min", "js!/socket.io/socket.io.js"], function() {
    loadComplete("bacon, underscore, socket.io");
    window.und = _;
    return window.require = curl;
  }, function(err){ console.log("bacon, underscore, socket.io" + err.message);})

  curl(['css!/css/style.css'], loadComplete("css"), moduleLoadError)
      .next(['js!//cdnjs.cloudflare.com/ajax/libs/moment.js/1.7.2/moment.min.js',
        'js!//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.9.2/jquery-ui.min.js'], loadComplete("moment and jquery complete"), moduleLoadError)
      .next(['js!lib/modernizr-2.6.2.min.js', 'js!lib/plugins.js',
        'js!//cdnjs.cloudflare.com/ajax/libs/d3/3.0.1/d3.v3.min.js'], loadComplete("modernizer, plugins, d3 complete"), moduleLoadError)
      .next(['domReady!', 'js!index.js'], loadComplete('index'), moduleLoadError )
       .then(loadComplete('all files'), moduleLoadError);

But the domReady! call is throwing a Multiple anonymous defines in url error. If I take that out the final next loads correctly.

Note that now that I put the css call first (in the second set of loads) it's no longer throwing an error.

unscriptable commented 11 years ago

jquery is the problem. don't load it using the js! plugin. Also: moment is AMD -- although (grrrr) they are using a named module which means you must map a path to it: moment:"//cdnjs.cloudflare.com/ajax/libs/moment.js/1.7.2/moment.min", jquery://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.9.2/jquery-ui.min""

other notes:

  1. window.require = curl;: I suspect this is only going to cause confusion. If you plan to require dependencies from other modules, you probably want the "local require". You can get a reference to the local require by including it as a pseudo-module or as an implicit CJS var to your modules.

// pseudo-module define(["require"], function (require) {});

// implicit. CJS-style module define(function (require) {});

  1. I highly recommend lo-dash instead of underscore. It's more robust, more reliable, and is AMD.

Actually, we don't use language abstractions at all. We recommend cujojs/poly to shim older browsers. Result: fewer dependencies and better code longevity.

  1. .then(loadComplete('all files'), moduleLoadError);: since loadComplete does not return a function (returns undefined iiuc), you will see an error when curl() tries to call it.

-- John

jcollum commented 11 years ago

jquery is the problem. don't load it using the js! plugin

But I'm loading jquery in my preloads, like you suggested. Maybe jquery ui should be in preloads as well? That gets more complicated because one depends on the other.

means you must map a path to it:

Ok, done.

since loadComplete does not return a function

It's up there: loadComplete = function(msg) { ...


I added these lines to curl.js after line 945:

  if (def.id !== null && typeof def.id !== "undefined"){
                args.ex += "; module: " + def.id;
              }

To help me figure out what is happening with the loading. So I then load my page several times. Results for each attempt:

  1. everything loads with no problems
  2. loads fine
  3. Multiple anonymous defines in url; module: lib/curl/plugin/domReady
  4. loads fine
  5. fine
  6. fine 7,8. Multiple anonymous defines in url; module: lib/curl/plugin/domReady
  7. fine
  8. fine
  9. Multiple anonymous defines in url; module: //cdnjs.cloudflare.com/ajax/libs/lodash.js/1.0.1/lodash.min.js

So out of about 30 loads I saw 10% fail on domReady and 10% on lodash. I might be able to sort this out if I could get a better error message -- can you point me to a line of code that I can modify to get a better error out of the Promise? Is that the right direction to go? The lack of informative errors is burning up too much time, I need to improve it. The domReady fail might be a red herring, it's possible.

My curl for bacon, lodash and socket.io looks like:

curl(["js!lib/bacon.js", 
  "//cdnjs.cloudflare.com/ajax/libs/lodash.js/1.0.1/lodash.min.js", 
  "js!/socket.io/socket.io.js"], 
 function() {
    loadComplete("bacon, underscore, socket.io");
    window.und = _;
    return define(function() {
      return require;
    });
  }, moduleLoadError)
unscriptable commented 11 years ago

Hey @jcollum, were you able to get this working? -- John

jcollum commented 11 years ago

I ditched curl a few months ago. Loading problems went away.