I am using BrunchJS to handle coffee script and assets compilation.
The project uses several Brunch plugins such as brunch-handlebar which requires the "commonjs" wrapper to operate.
Extract from my config.coffee
modules:
# We cant avoid require js wrapping since brunch modules use commonjs
# Otherwise Marionnette JS offers its own modules loading strategy
# loading mechanism
wrapper: "commonjs"
definition: "commonjs"
On the Marionette side I can have a simple application loading just fine.
index.html
<script type="text/javascript">
var app = require('application');
app.initialize()
</script>
application.coffee
# Load handlebars block helpers
require 'lib/view_helper'
class Application extends Backbone.Marionette.Application
initialize: =>
#addInitializer((options) =>
console.log "HELLO WORLD"
AppLayout = require 'views/app_layout'
#layout = new AppLayout()
#layout.render()
)
#start()
# module.exports is the object that's actually returned as the result of
# a require call.
module.exports = new Application()
Starting from there how do I use Marionette JS modules ? I read about using modules with AMD here https://github.com/marionettejs/backbone.marionette/wiki/AMD-Modules-vs-Marionette's-Modules but I can not use the define keyword in my marionette module definition since "define" and "require" are not exposed. Brunch only uses it to load its plugins and my app source files.
A usual Marionette module looks like this:
MyApp = new Backbone.Marionette.Application();
MyApp.module("Foo", function(){
// module code goes here
});
MyApp.start();
In a separate file moduleA.coffee I tried to do:
MyApp = require 'application'
define ["MyApp", "Marionette"], (MyApp, Marionette) ->
MyModule = MyApp.module("MyModule")
MyModule.addInitializer ->
console.log "HELLO FROM MODULE"
MyModule
But define is not defined.
I also tried to do:
MyApp = require 'application'
MyApp.module "ModuleA", (MyApp, ModuleA, Backbone, Marionette, $, _) ->
ModuleA.addInitializer ->
console.log "HELLO FROM MODULE"
but then I need to require all my marionette modules ("moduleA") in application.coffee and ran into some circular dependency issues.
One of the solution I am thinking about is to disable BrunchJS commonjs wrapping and load handlebars from vendor folder instead of as a brunch plugin.
JavaScript example in case it helps!
I think the important bit is initiating you bootstrapper (main.js, in this example) via require. It should be provided in a script tag attribute called 'data-main'. Perhaps this is something Brunch does for you, I'm not sure.
<script type="text/javascript" data-main="js/main" src="js/libs/require.js')}"></script>
Then your main.js might look something like this.
/* global require, console */
require.config({
baseUrl: 'js',
paths: {
'jquery': 'libs/jquery/jquery',
'underscore': 'libs/underscore/underscore',
'backbone': 'libs/backbone/backbone',
'backbone.wreqr': 'libs/backbone.wreqr/lib/backbone.wreqr',
'backbone.babysitter': 'libs/backbone.babysitter/lib/backbone.babysitter',
'marionette': 'libs/marionette/lib/core/backbone.marionette'
...
},
shim: {
'underscore': {
deps: ['jquery'],
exports: '_'
},
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
'backbone.wreqr': {
deps: ['backbone', 'underscore'],
exports: 'wreqr'
},
'backbone.babysitter': {
deps: ['backbone', 'underscore'],
exports: 'babysitter'
},
'marionette': {
deps: ['backbone', 'backbone.wreqr', 'backbone.babysitter'],
exports: 'Marionette'
}
}
});
// Start the main app logic.
require([
'app'
],
function (App) {
App.start();
});
Once this is all setup, you can initiate any resource prefixing the function with define, like this...
define([
'jquery',
'underscore',
'backbone',
'marionette'
],
function ($, _, Backbone, Marionette) {
'use strict';
// code goes here
});
Related
I am using AMD (specifically RequireJS) along with TypeScript in my sample to support external modules. Here's the main.ts which RequireJS loads (through data-main attribute):
/// <reference path="../typings/tsd.d.ts" />
requirejs.config({
paths: {
jquery: '../bower_components/jquery/dist/jquery',
angular: '../bower_components/angular/angular',
uiRouter: '../bower_components/angular-ui-router/release/angular-ui-router',
domReady: '../bower_components/requirejs-domready/domReady"
},
shim: {
angular: {
deps: ['jquery'],
exports: 'angular'
},
uiRouter: {
deps: ['angular']
}
},
deps: ['bootstrapper']
});
and here's the bootstrapper.ts (not in a working state) file which bootstraps the app:
import angular = require('angular') // works fine
import uiRouter = require('uiRouter') // VS Code throws error. External module not found.
import domReady = require('domReady') // Same error as above.
angular.bootstrap(domReady, ['myApp'], {strictDi: true}); // Not working due to domReady.
I have to resort to use regular RequireJS' define method in order to get the router and domReady dependencies:
define(['angular',
'domReady',
'uiRouter'
], (angular, document, uiRouter) => {
angular.bootstrap(document, ['myApp'], {strictDi: true});
});
Any ideas how can I make the domReady and uiRouter as TypeScript external modules.
Any ideas how can I make the domReady and uiRouter as TypeScript external modules.
HotFix
Have a file vendor.d.ts containing the following:
declare module "uiRouter" {
var dummy:any;
export = exp;
}
declare module "domReady" {
var dummy:any;
export = exp;
}
Alternatively you can create proper ambient declarations for these, or look these up at DefinitelyTyped.
I am trying to use gulp-requirejs to build a demo project. I expect result to be a single file with all js dependencies and template included. Here is my gulpfile.js
var gulp = require('gulp');
var rjs = require('gulp-requirejs');
var paths = {
scripts: ['app/**/*.js'],
images: 'app/img/**/*'
};
gulp.task('requirejsBuild', function() {
rjs({
name: 'main',
baseUrl: './app',
out: 'result.js'
})
.pipe(gulp.dest('app/dist'));
});
// The default task (called when you run `gulp` from cli)
gulp.task('default', ['requirejsBuild']);
The above build file works with no error, but the result.js only contains the content of main.js and config.js. All the view files, jquery, underscore, backbone is not included.
How can I configure gulp-requirejs to put every js template into one js file?
If it is not the right way to go, can you please suggest other method?
Edit
config.js
require.config({
paths: {
"almond": "/bower_components/almond/almond",
"underscore": "/bower_components/lodash/dist/lodash.underscore",
"jquery": "/bower_components/jquery/dist/jquery",
"backbone": "/bower_components/backbone/backbone",
"text":"/bower_components/requirejs-text/text",
"book": "./model-book"
}
});
main.js
// Break out the application running from the configuration definition to
// assist with testing.
require(["config"], function() {
// Kick off the application.
require(["app", "router"], function(app, Router) {
// Define your master router on the application namespace and trigger all
// navigation from this instance.
app.router = new Router();
// Trigger the initial route and enable HTML5 History API support, set the
// root folder to '/' by default. Change in app.js.
Backbone.history.start({ pushState: false, root: '/' });
});
});
The output is just a combination this two files, which is not what I expected.
gulp-requirejs has been blacklisted by the gulp folks. They see the RequireJS optimizer as its own build system, incompatible with gulp. I don't know much about that, but I did find an alternative in amd-optimize that worked for me.
npm install amd-optimize --save-dev
Then in your gulpfile:
var amdOptimize = require('amd-optimize');
var concat = require('gulp-concat');
gulp.task('bundle', function ()
{
return gulp.src('**/*.js')
.pipe(amdOptimize('main'))
.pipe(concat('main-bundle.js'))
.pipe(gulp.dest('dist'));
});
The output of amdOptimize is a stream which contains the dependencies of the primary module (main in the above example) in an order that resolves correctly when loaded. These files are then concatenated together via concat into a single file main-bundle.js before being written into the dist folder.
You could also minify this file and perform other transformations as needed.
As an aside, in my case I was compiling TypeScript into AMD modules for bundling. Thinking this through further I realized that when bundling everything I don't need the asynchronous loading provided by AMD/RequireJS. I am going to experiment with having TypeScript compile CommonJS modules instead, then bundling them using webpack or browserify, both of which seem to have good support within gulp.
UPDATE
My previous answer always reported taskReady even if requirejs reported an error. I reconsidered this approach and added error logging. Also I try to fail the build completely as described here gulp-jshint: How to fail the build? because a silent fail really eats your time.
See updated code below.
Drew's comment about blacklist was very helpfull and gulp folks suggest using requirejs directly. So I post my direct requirejs solution:
var DIST = './dist';
var requirejs = require('requirejs');
var requirejsConfig = require('./requireConfig.js').RJSConfig;
gulp.task('requirejs', function (taskReady) {
requirejsConfig.name = 'index';
requirejsConfig.out = DIST + 'app.js';
requirejsConfig.optimize = 'uglify';
requirejs.optimize(requirejsConfig, function () {
taskReady();
}, function (error) {
console.error('requirejs task failed', JSON.stringify(error))
process.exit(1);
});
});
The file at ./dist/app.js is built and uglified. And this way gulp will know when require has finished building. So the task can be used as a dependency.
My solution works like this:
./client/js/main.js:
require.config({
paths: {
jquery: "../vendor/jquery/dist/jquery",
...
},
shim: {
...
}
});
define(["jquery"], function($) {
console.log($);
});
./gulpfile.js:
var gulp = require('gulp'),
....
amdOptimize = require("amd-optimize"),
concat = require('gulp-concat'),
...
gulp.task('scripts', function(cb) {
var js = gulp.src(path.scripts + '.js')
.pipe(cached('scripts'))
.pipe(jshint())
.pipe(jshint.reporter('default'))
.pipe(remember('scripts'))
.pipe(amdOptimize("main",
{
name: "main",
configFile: "./client/js/main.js",
baseUrl: './client/js'
}
))
.pipe(concat('main.js'));
.pipe(gulp.dest(path.destScripts));
}
...
This part was important:
configFile: "./client/js/main.js",
baseUrl: './client/js'
This allowed me to keep my configuration in one place. Otherwise I was having to duplicate my paths and shims into gulpfile.js.
This works for me. I seems that one ought to add in uglification etc via gulp if desired. .pipe(uglify()) ...
Currently I have to duplicate the config in main.js to run asynchronously.
....
var amdOptimize = require("amd-optimize");
...
var js = gulp.src(path.scripts + '.js')
.pipe(cached('scripts'))
.pipe(jshint())
.pipe(jshint.reporter('default'))
.pipe(remember('scripts'))
.pipe(amdOptimize("main",
{
name: "main",
paths: {
jquery: "client/vendor/jquery/dist/jquery",
jqueryColor: "client/vendor/jquery-color/jquery.color",
bootstrap: "client/vendor/bootstrap/dist/js/bootstrap",
underscore: "client/vendor/underscore-amd/underscore"
},
shim: {
jqueryColor : {
deps: ["jquery"]
},
bootstrap: {
deps: ["jquery"]
},
app: {
deps: ["bootstrap", "jqueryColor", "jquery"]
}
}
}
))
.pipe(concat('main.js'));
Try this code in your gulpfile:
// Node modules
var
fs = require('fs'),
vm = require('vm'),
merge = require('deeply');
// Gulp and plugins
var
gulp = require('gulp'),
gulprjs= require('gulp-requirejs-bundler');
// Config
var
requireJsRuntimeConfig = vm.runInNewContext(fs.readFileSync('app/config.js') + '; require;'),
requireJsOptimizerConfig = merge(requireJsRuntimeConfig, {
name: 'main',
baseUrl: './app',
out: 'result.js',
paths: {
requireLib: 'bower_modules/requirejs/require'
},
insertRequire: ['main'],
// aliases from config.js - libs will be included to result.js
include: [
'requireLib',
"almond",
"underscore",
"jquery",
"backbone",
"text",
"book"
]
});
gulp.task('requirejsBuild', ['component-scripts', 'external-scripts'], function (cb) {
return gulprjs(requireJsOptimizerConfig)
.pipe(gulp.dest('app/dist'));
});
Sorry for my english. This solution works for me. (I used gulp-requirejs at my job)
I think you've forgotten to set mainConfigFile in your gulpfile.js. So, this code will be work
gulp.task('requirejsBuild', function() {
rjs({
name: 'main',
mainConfigFile: 'path_to_config/config.js',
baseUrl: './app',
out: 'result.js'
})
.pipe(gulp.dest('app/dist'));
});
In addition, I think when you run that task in gulp, require can not find its config file and
This is not gulp-requirejs fault.
The reason why only main.js and config.js is in the output is because you're not requiring/defining any other files. Without doing so, the require optimizer wont understand which files to add, the paths in your config-file isn't a way to require them!
For example you could load a main.js file from your config file and in main define all your files (not optimal but just a an example).
In the bottom of your config-file:
// Load the main app module to start the app
requirejs(["main"]);
The main.js-file: (just adding jquery to show the technique.
define(["jquery"], function($) {});
I might also recommend gulp-requirejs-optimize instead, mainly because it adds the minification/obfuscation functions gulp-requirejs lacks: https://github.com/jlouns/gulp-requirejs-optimize
How to implement it:
var requirejsOptimize = require('gulp-requirejs-optimize');
gulp.task('requirejsoptimize', function () {
return gulp.src('src/js/require.config.js')
.pipe(requirejsOptimize(function(file) {
return {
baseUrl: "src/js",
mainConfigFile: 'src/js/require.config.js',
paths: {
requireLib: "vendor/require/require"
},
include: "requireLib",
name: "require.config",
out: "dist/js/bundle2.js"
};
})).pipe(gulp.dest(''));
});
I am doing my first try using requireJS and it works great !
I now would like to use the optimizer and i meet some issues when running my code in the browser.
I have these JS files:
/public/javascripts/build.js
/public/javascripts/main.js
/public/javascripts/lib/jquery.min.js
/public/javascripts/lib/require.min.js
/public/javascripts/a.js
/public/javascripts/b.js
/public/javascripts/c.js
a.js, b.js and c.js are modules i define for my application using requireJS.
main.js:
require.config({
paths: {
'jQuery': 'lib/jquery.min.js'
},
shim: {
'jQuery': {
exports: '$'
}
}
});
require(['a.js'], function(A){
var Entity = new A();
});
build.js
({
baseUrl: ".",
paths: {
requireLib: "lib/require.min",
jquery: "lib/jquery.min"
},
name: "main",
out: "main-built.js",
include: ["requireLib"]
})
Also i am wondering why do we have to specify the paths of the libraries into the build.js and not the other javascript files.
When i do not use the optimizer and only load the file
<script src="/javascripts/lib/require.min.js" data-main="/javascripts/main"></script>
it works great, but when i run r.js -o ./public/javascripts/build.js and only load
<script src="/javascripts/main-built.js"></script> i get the error Uncaught TypeError: undefined is not a function in the minified code.
How to explain that ?
Here are the logs i get when running r.js
Tracing dependencies for: main
Uglifying file: /public/javascripts/main-built.js
/public/javascripts/main-built.js
----------------
/public/javascripts/lib/require.min.js
/public/javascripts/a.js
/public/javascripts/b.js
/public/javascripts/lib/jquery.min.js
/public/javascripts/c.js
/public/javascripts/main.js
This is definitely wrong:
require(['a.js'], function(A){
var Entity = new A();
});
You should not use extensions in the list of dependencies you give to require or define. Modules should be named without extension. So here 'a', not 'a.js'. Using 'a.js' will cause RequireJS to fail loading what you really want once the optimizer has run. Let's say you have a file named a.js which has:
define(function () {
return function () {};
});
The optimizer will include it into your main-built.js file like this:
define("a", function () {
return function () {};
});
Note how the first parameter to define is now "a". This has been added by r.js. This is the name of the module. When you load main-built.js, a module named "a" is defined. When you use require with "a.js", you are telling RequireJS you want something in a file named a.js so RequireJS will go looking for that and ignore what is in main-built.js.
Also, jQuery 1.8 or over does not need a shim.
I just have added
shim: {
'jQuery': {
exports: '$'
}
}
into the build.js file, and it works perfectly !
Thanks !
I'm having an issue attempting to create a RequireJS shim for some javascript code that was written by another team in my organization. The script is loaded via a noraml HTML script as such:
<script src="MyCustomModule.js" type="text/javascript"></script>
My main.js contains the following:
requirejs.config({
paths: { 'text': 'durandal/amd/text' },
shim: {
'MyCustomModule': { exports: 'My.Custom.Module' }
}
});
And I have tried accessing the custom module in a variety of ways, but this is my current code:
define(['MyCustomModule'], function (require, MyCustomModule) {
...
}
But each time the page/app loads I get an error from RequireJS indicating that it failed to load app/MyCustomModule.js (and I can see the 404 error in the console where it attempted to request the file from the server). What am I doing wrong?
You also need to include MyCustomModule in paths:
requirejs.config({
paths: {
'text': 'durandal/amd/text'
'MyCustomModule': 'path/to/MyCustomModule'
},
shim: {
'MyCustomModule': {
exports: 'My.Custom.Module'
}
}
});
If you don't do that, define(['MyCustomModule'] (...) will look for the dependency in the baseUrl location, in your case: app/MyCustomModule.
In other words: shim can't "pick up" global variables that are not loaded by RequireJS.
Say I want to use jquery together with a standard, non-amd enabled jquery plugin that has been defined using standard closure: (function($))( $.fn.myplugin = { ... } )(jQuery); and it all sits inside of a js/libs/jquery/jquery.myplugin.js.
I use this config:
require.config({
baseUrl: 'js/',
paths: {
'jquery': 'libs/jquery/jquery-noconflict',
'underscore': 'libs/underscore/underscore',
'backbone': 'libs/backbone/backbone',
'jquery-myplugin': 'libs/jquery/jquery.myplugin'
},
shim: {
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
'jquery-myplugin': {
deps: ['jquery']
}
});
I load jQuery in no-conflict mode in libs/jquery/jquery-noconflict.js, becase I don't want to pollute global namespace:
define(['libs/jquery'], function () {
return jQuery.noConflict(true);
});
and this is how I load my main app.js:
define([
'jquery',
'underscore',
'backbone',
'jquery-myplugin'],
function($, _, Backbone, MyPlugin){
//MyPlugin is always undefined, not even sure if
//I should be passing it here if it only extends jQuery?
});
Now, here is the problem I am experiencing - while I can use all libraries defined above without any problems, I could not work out the correct shim configuration to load non-AMD enabled jquery plugins.
I've tried setting up jquery-myplugin as deps of the jquery (and other way around) but I could never get it working.
It seems like I'm having problem with the following scenario:
jQuery loads in no-conflict mode.
plugin code runs, extending the instance of the jQuery above
I can use $ within my application, extended by the plugin code, so $.myplugin is available.
I have seen similar questions floating around but none of them actually resolves this issue giving only vague suggestions such as "use shim config"...
Edit
I also tried using
"jquery-myplugin": {
deps: ["jquery"],
exports: "jQuery.fn.myplugin"
}
And whilst plugin methods are available once loaded as AMD module this way, I still can't access: $('.class').myplugin() as default $ object hasn't been extended with myplugin code.
Using jQuery.noConflict(true) removes the jQuery global variable. When your plugin loads, it tries to access jQuery, but can't, causing this failure.
If your plugin was a module, it could get access to jQuery as a dependency. Or you could leave jQuery available as a global.
First, insure that "path/to/jquery-myplugin" actually extends window.jQuery and not $
noConflict() leaves window.jQuery object defined but unbinds itself from window.$ On some new browsers window.$ is built in alias for native document.querySelectorAll function.
Second, your myplugin does NOT need to return itself, as it cannot be used by itself. Since it extends jQuery, return jQuery from myplugin call.
Lastly, "path/to/jquery-myplugin" is NOT a module. It's a plain JS file. It's possible RequireJS tries to load it like a module and does not find define() call, which leads to mess. Try actually adding ".js" file extension to the reference to signal to RequireJS that it needs to use "js!" plugin to load the resource.
require.config({
paths: {
"jquery": "path/to/jquery",
"jquery-myplugin": "path/to/jquery-myplugin.js"
},
shim: {
"jquery": {
init: function() {
return window.jQuery.noConflict();
},
"jquery-myplugin": {
deps: ['jquery']
init: function($) {
return $;
},
}
}
});
I had the same problem as you today. Here is how I could fix it :
requirejs.config({
"baseUrl": "js/lib",
"paths": {
"app": "../app"
}
});
// Final version of jQuery with all needed plugins.
define("jquery", ["jquery-core", "myplugin"], function(jqCore) {
return jqCore;
});
// Define core jQuery module.
define("jquery-core", ["jquery-1.9.1.min"], function() {
return jQuery.noConflict(true);
});
// This module exposes jquery module to the global scope and returns previous defined version.
define("jq-global", ["jquery-core"], function(jqCore) {
var tmp = jQuery;
jQuery = jqCore;
return tmp;
});
// Define your plugin here, and don't forget to reset previous jQuery version.
define("myplugin", ["jq-global", "jquery.myplugin"], function(jqGlobal, jqPlugin) {
jQuery = jqGlobal;
});
// Load the main app module to start the app
requirejs(["app/main"]);
Now in my app/main.js file I can do the following :
define(["jquery"], function($) {
$('body').myplugin();
});
The idea here is to expose jQuery temporary before plugin code is executed. So far I didn't test the solution in a larger environment with a lot more modules to load, so I can't guarantee it will work in the long term ;)
Edit
This solution won't work!! Since requirejs doesn't load the scripts sequentially, it is possible the plugin js file loads before jquery which will cause the execution to fail. Sorry for this.
If someone has another idea...