0

I've written a full web app and I bootstrap it with require in this line at the end of my HTML document:

<script src="js/vendor/require-2.1.6.min.js" data-main="js/main.js"></script>

Inside main.js, I declare my app using the following two functions:

requirejs.config({

    paths: {
        'jquery': 'vendor/jquery-1.9.1.min',
        'lodash': 'vendor/lodash-1.3.1.min',
        'knockout': 'vendor/knockout-2.2.1.min',
        'bootstrap': 'vendor/bootstrap-2.3.2.min'
    },
    shim: { 'bootstrap': { deps: ['jquery'] } }
});

requirejs(dependencies, function main(dependencies) { ... });

Now, I want to port my app as a jQuery plugin. As such, the end result I desire is to wrap my app in a jQuery fn,

(function($) { $.fn.myPlugin = function() { ... }; }(jQuery));

modularize the code to compile into a single js file (plugin-1.0.0.min.js), include the code in another project, with or without AMD, and load my app into a div using

$('#pluginDiv').myPlugin({ options: {} }); 

First, do I need to change the requirejs/requirejs.config functions to declares in order to package my app as a modular component? Or do I simply leave my app like this? What about the config?

Next, how do I expose my plugin to the jQuery which the plugin user will be using, e.g. the one declared in the global scope? Will this work if they're using an AMD?

UPDATE

For question 1, I moved my requirejs.config to a build file, build.js, which requires some additional properties:

({
    baseUrl: ".",
    paths: {
        'jquery': 'vendor/jquery-1.9.1.min',
        'lodash': 'vendor/lodash-1.3.1.min',
        'knockout': 'vendor/knockout-2.2.1.min',
        'bootstrap': 'vendor/bootstrap-2.3.2.min'
    },
    shim: { 'bootstrap': { deps: ['jquery'] } },
    optimize: "none", // for debug
    name: "main",
    out: "plugin.js"
})

I was able to use r.js to compile this and it works great.

I am still stuck on question two, however. The code in my compiled myPlugin.js is as follows:

requirejs(dependencies, function main(dependencies) {
    (function($) {

        $.fn.myPlugin = function(options) {

            ...

            return this;
        };
    })(jQuery);
});

where dependencies does not include jQuery. Then, I bootstrap the app by calling:

<script src="js/vendor/require-2.1.6.min.js" data-main="js/app.js"></script>

And the code in app.js is

requirejs.config({
    paths: { 'jquery': 'vendor/jquery-1.9.1.min' },
    shim: { 'myPlugin': ['jquery'] }
});

requirejs(['jquery','myPlugin'], function main($) {
    $('#plugin').myPlugin(options);
});

However, my code always attempts to bind the plugin (from app.js), fails when the plugin is not a method on jQuery, and THEN loads the compiled plugin code and creates the method on jQuery. What's the problem here??

UPDATE 2

So, I've created a modular JavaScript file plugin.js using requirejs and its optimizer. The main code in the compiled plugin script,

requirejs(dependencies, function main(dependencies) {
    (function($) {
        $.fn.plugin = function(options) { return this; }
    })(window.jQuery);
);

doesn't get called until after the main code in the parent app:

requirejs(['jquery','plugin'], function main($) {
    $('#plugin').plugin({});
});

I assume this is because they are both making calls to requirejs. So the problem here is how to write my plugin such that it is usable within an AMD loader.

Sorry, I'm still figuring out the right questions to ask.

Matthew James Davis
  • 12,134
  • 7
  • 61
  • 90
  • 1
    Let me see if I have your question correctly. You have a fancy plugin that uses require, but want to bind the plugin invocation to the main window.$ jquery object? – Thinking Sites Jul 09 '13 at 13:50
  • Potentially. I have a fancy plugin that uses require, and I want to compile it and use it in another project. However, I'm having trouble getting it to attach to the jQuery dependency from my app's shim. – Matthew James Davis Jul 09 '13 at 13:53
  • 1
    I'm still unclear about something. Are you trying to get this plugin to the window.$ or to the shimmed jQuery? Is require.js required for the parent application? – Thinking Sites Jul 09 '13 at 13:58
  • Shimmed to the jQuery. It isn't required, exactly, but I do want the option to use it. I want my plugin to be compatible with this scenario. – Matthew James Davis Jul 09 '13 at 14:01
  • 1
    I think I see... your plugin is intended to be used as a shim, not the jquery object. Your plugin uses the jquery object, but you want it to use the global jquery object, not require's dependency jquery. Correct? – Thinking Sites Jul 09 '13 at 14:12
  • So, either (a) someone could include – Matthew James Davis Jul 09 '13 at 14:16
  • 1
    Now, for my final line of inquiry. The plugin uses require. If the parent application does not use require, how is requirejs loaded? – Thinking Sites Jul 09 '13 at 14:20
  • That's one of the other problems I'm researching now. I was hoping there was a way to compile a javascript application using require such that it no longer depends on require after compilation. Looking at Grunt, Yeoman, etc., but the documentation isn't stellar. Is this an option? – Matthew James Davis Jul 09 '13 at 14:27
  • 1
    Not as far as I know. I haven't tried loading require from $.loadScript however. Let me play with that, and I'll have a solution for you later today. – Thinking Sites Jul 09 '13 at 14:35

1 Answers1

1

There is a pretty standard convention for modularizing plugins for use with and without requirejs. It looks something like this:

(function(){

    var makeplugin = function(dependancies){
        //do your plugin
    };

    if(define && define.amd) {
        defined(dependancies,function(dependancies){
            makeplugin(dependancies);
        });
    } else {
        makeplugin(dependancies)
    }
}());

Because your plugin uses require internally, but your parent app doesn't have to, you can load requirejs using $.getScript()

(function(){
    var makeplugin = function($){
        //do your plugin
    };

    if(define && define.amd) {
        // require is defined already, just use the plugin and have it load what it needs
        define(["jquery"],makeplugin);
    } else if(jQuery){
        // load require
        jQuery.getScript("/vendor/require",function(){
            require.config({
                // your config
            });
            makeplugin(jQuery);
        });
    } else {
        throw "requirejs or jquery are required for this plugin";
    }
}());

It isn't pretty but it should work.

Thinking Sites
  • 3,494
  • 17
  • 30
  • I apologize, I got kind of sidetracked from the actual problem. So the problem is that in my app.js file, the last snippet of code, $('#plugin').myPlugin(options) yields an error "Object has no method 'plugin'" and then immediately the plugin is loaded. I'm not sure why my plugin isn't being loaded prior to running the app.js code since plugin is listed as a dependency. – Matthew James Davis Jul 09 '13 at 16:57
  • 1
    Because require creates its own version of jQuery and passes it around instead of using the global method. That's why I kept asking you if you are using the window.$ object or a shimmed jQuery. The example above will suss that out for you, and you can write your plugin as normal. – Thinking Sites Jul 09 '13 at 17:37
  • True. The problem I have though is that my makeplugin function uses AMD. See my edit. – Matthew James Davis Jul 09 '13 at 17:53
  • Right, that's where the second if statement in my solution comes in. If the parent doesn't use require, it loads it, configures it and it becomes a global variable available to makeplugin. – Thinking Sites Jul 09 '13 at 19:24
  • Err, negative. define and define.amd are defined, but it does not load the module before executing the main app code, but after. See the edit. – Matthew James Davis Jul 09 '13 at 19:35
  • Load which module? Any modules you need would be loaded in your config, or in a require call inside of makeplugin. – Thinking Sites Jul 09 '13 at 19:40
  • The compiled plugin script. The first section of code in the update. – Matthew James Davis Jul 09 '13 at 19:42
  • http://jsfiddle.net/thinkingsites/DYTDC/2/ In this fiddle, make plugin has all the code your app/plugin will need to run. If you need it to load the build.js file, just do a nested getScript inside of the success of your first. Am I missing something? – Thinking Sites Jul 09 '13 at 19:58
  • Yes, I think so. So, my plugin is a js file based on require. I want to use require to load that file. When I do that, however, it fails to execute the AMD code within the file until after it finishes the AMD code in the parent. See http://stackoverflow.com/questions/17557091/requirejs-modules-loading-out-of-order-when-loading-one-module-into-another – Matthew James Davis Jul 09 '13 at 20:17