Search code examples
javascriptbackbone.jsgruntjsyeomangrunt-contrib-requirejs

'define' is not defined error on RequireJS & Webapp Yo generator


I have struggled a few days to figure this out,, but finally I need your help today.
my repo: https://github.com/seoyoochan/bitsnut-web


what I want to achieve:
- Load and optimize r.js - Write bower tasks for RequireJS and r.js :
   tasks are: minify & uglify & concatenation for RequireJS, and optimise with r.js on production
- How to exclude js script tags in index.html when using wiredep tasks and load them through RequireJS loader?


I use Yeoman 'Webapp' generator and generated the scaffold app.

I installed backbone, marionette, text, underscore, and etc via bower install I modified bower.json by removing dependencies and left only "requirejs": "~2.1.16" on dependencies. (devDependencies is empty)

because I use [2][grunt-wiredep], everything is automatically loaded bower_components into index.html. I modified .bowerrc to store dependencies at app/scripts/vendor.

However, the problem is that I don't know how to successfully load them through ReuqireJS and not to load the vendors as script tags inside index.html. I have to write some tasks for RequireJS and r.js, but don't know how to achieve this goal ( I installed grunt-contrib-requirejs though )

I want to follow the 4th method to make use of r.js at https://github.com/jrburke/requirejs/wiki/Patterns-for-separating-config-from-the-main-module. but the issue I encountered was that RequireJS's documentation does suggest to not use named module, but anonymous module. I would like to hear various opinions about how I should approach.

I really appreciate your help in advance!


Solution

  • You load your scripts manually here and here, rendering the whole point of requireJS useless. You also load main first here config.js#L49.

    Instead, you should only have this line in your index.html

    <script data-main="scripts/config" src="scripts/vendor/requirejs/require.js"></script>
    

    And load all your dependencies in that file (like you do with main) using define() and require(). As you have set exports, which sets the values as globals, the functions can be empty. Here's an sample:

    define([
        "jquery",
        "underscore", 
        "backbone",
        "marionette",         
        "modernizr"
    ], function () {
            require([
            "backbone.babysitter", 
            "backbone.wreqr", 
            "text", 
            "semantic"
        ], function () {
            /* plugins ready */
        });
    
        define(["main"], function (App) {
               App.start();
        });
    });
    

    Also the baseUrl is the same as the directory as your data-main attributes folder (http://requirejs.org/docs/api.html#jsfiles):

    RequireJS loads all code relative to a baseUrl. The baseUrl is normally set to the same directory as the script used in a data-main attribute for the top level script to load for a page. The data-main attribute is a special attribute that require.js will check to start script loading.

    So I think your baseUrl in config.js points to scripts/scripts/-folder which doesn't exist. It could/should be vendor/ instead (and remove the vendor part from all of the declarations) or just left empty.

    Instead of wiredep, you could try using https://github.com/yeoman/grunt-bower-requirejs which does similar things to wiredep but specially for bower/requirejs apps (see: https://github.com/stephenplusplus/grunt-wiredep/issues/7)

    Your repo doens't include the dist-folder for jQuery, but otherwise here's a working sample of config.js: http://jsfiddle.net/petetnt/z6Levh6r/

    As for the module-definition, it should be

    require(["dependency1", "dependency2"])

    and the module should return itself. Currently your main file sets itself as a dependency

    require(["main", "backbone", "marionette"], function(App, Backbone, Marionette){
    

    As you already set the backbone and marionette as globals with exports, you can again set the function attributes empty, so your main file should look like this:

    require(["backbone", "marionette"], function(){
      "use strict";
      var App = new Backbone.Marionette.Application();
    
      App.addInitializer(function(){
        console.log("hello world!");
        Backbone.history.start();
      });
    
      return App;
    });
    

    And as you already use define to load main, don't require it again. Instead just call App.start() inside the define function.

    https://jsfiddle.net/66brptd2/ (config.js)