backbone, requirejs, grunt requirejs use external templates - javascript

I am using on a backbonejs project with requirejs. I am using Gruntjs as build process. In this project I am using external underscore templates. Below is my dir structure.
MainApp/
app/
images/
js/
styles/
templates/
index.html
Below is my requirejs options in Gruntfile.js
requirejs: {
compile: {
options: {
name: "views/app",
baseUrl: "prod/js",
mainConfigFile: "prod/js/main.js",
out: "prod/scripts/scripts.min.js",
include: ['libs/requirejs/require.js']
}
}
}
However, this seems to be not working. When I build it by running grunt command it does build the project successfully i.e. I am not getting any errors during build process. But when I want to run this project in browser, it does not work. It shows the home page correctly with correct styles but javascript functionality is not working. One of the reason I can think of is I am using external templates which grunt requirejs plugin seems to be not picking up.
How can I use external templates?
UPDATE
I am using grunt-contrib-requirejs plugin.

RequireJS config file is nto included in output file. You must split config and main app:
src/js/config.js
/*global require:false */
require.config({
urlArgs: 'version=' + (new Date()).getTime(),
paths: {
'jquery' : 'libs/jquery/dist/jquery',
'underscore' : 'libs/underscore/underscore',
'backbone' : 'libs/backbone/backbone',
'localStorage' : 'libs/backbone.localStorage/backbone.localStorage',
'text' : 'plugins/text'
}
});
src/js/main.js
/*global require:false */
/*global Backbone:false */
/*global _:false */
require(['views/app', 'collections/todos', 'router'], function (AppView, TodoCollections, Router) {
window.App = {
Vent: _.extend({}, Backbone.Events)
};
new AppView({
collection: new TodoCollections()
});
window.App.TodoRouter = new Router();
Backbone.history.start();
});
Gruntfile.js
requirejs: {
compile: {
options: {
baseUrl: "dist/js",
mainConfigFile: "dist/js/config.js",
name: 'main',
out: "dist/scripts/scripts.min.js",
include: 'libs/requirejs/require.js',
optimize: 'none',
preserveLicenseComments: false,
useStrict: true,
wrap: true
}
}
},

Related

Using the RequireJS text plugin

I am using Karma to run tests against some code.
Both the tests and the code are transpiled (ES6 => ES5 using babel) before being run by Karma.
This works great and the tests run fine.
But if I try and use the text! plugin from any of the files being tested...
import template from 'text!./template.html';
...I get:
There is no timestamp for /base/src/text.js!
Uncaught Error: Script error for "text", needed by: text!app/template.html_unnormalized2
http://requirejs.org/docs/errors.html#scripterror
Uncaught Error: Load timeout for modules: text!app/template.html_unnormalized2
Does anyone have any ideas why this might be?
The built artifact in the dist folder (i.e. the item under test) contains the successfully encoded text RequireJS items eg:
define('text!app/template.html',[],function () { return '<div>foo</div>';});
Additional Info
test-main.js
var TEST_REGEXP = /(spec|test)\.js$/i;
var allTestFiles = [];
Object.keys(window.__karma__.files).forEach(function(file) {
if (TEST_REGEXP.test(file)) {
var normalizedTestModule = file.replace(/^\/base\/|\.js$/g, '');
allTestFiles.push(normalizedTestModule);
}
});
require.config({
baseUrl: '/base/src',
paths: {},
shim: {},
deps: allTestFiles,
callback: window.__karma__.start
});
karma.conf.js
module.exports = function(config) {
'use strict';
var path = require('path');
var cdn = 'http://localhost:55635/modules/';
var basePath = path.dirname(__filename);
config.set({
basePath: '../../..',
frameworks: [
'requirejs',
'jasmine'
],
files: [
{
pattern: path.join(basePath, 'test-transpiled', '*-spec.js'),
included: false
},
path.join(basePath, 'dist', 'artifacts', 'app.js'),
path.join(basePath, 'test', 'unit', 'test-main.js')
],
proxies: {
'/cdn/': cdn
},
exclude: [],
preprocessors: {},
reporters: ['dots'],
colors: true,
autoWatch: false,
singleRun: false,
browsers: ['Chrome'],
});
};
Edit:
I have added the following to karma.conf.js:
files: [
{
pattern: path.join(basePath, 'node_modules/require-plugins/text/text.js'),
included: false
},
// ...
],
I continue to get an error when running the tests:
There is no timestamp for /base/src/text.js!
Presumably because I need to add "text" to the paths section of test-main.js?
require.config({
baseUrl: '/base/src',
paths: {
'text': '../node_modules/require-plugins/text/text'
},
// ...
});
But I have tried various combinations of baseUrl and the path in the text path and I cannot get it to stop 404-ing.
Your files option in karma.conf.js does not include the text plugin, which is why you get the error that there's no timestamp for it.
Add an item to your files list that hits the text plugin on your file system, and make sure that you have included: false for it. RequireJS plugins are like other modules: RequireJS must be able to load them to use them.
You may need to also set paths in test-main.js depending on where you put your plugin. RequireJS already is looking for it at /base/src/text.js. If you locate it so that the plugin is served at this URL, then there's no need to set paths. If you put it somewhere else, then you do need to set paths. Something like:
paths: {
text: 'path/to/text',
}
Remember that the paths in paths are interpreted relative to your baseUrl setting.
I tried using the require.js text! plugin, and also found that it conflicts with the baseUrl defined for the rest of the project.
The requirejs.config sets the baseUrl to the parent directory of the JS files, while my templates are defined in a sibling directory to the js.
There was no way to tell requirejs to load templates from /base/templates and js from base/js.
My solution was to change the text.js plugin, and add a hook to change the url just before the ajax call is made to fetch the HTML file. You can take my version of text.js from here.

Downloading Script Dependencies in Parallel RequireJS

Following this RequireJS example I'm trying to have a single file for all vendor js assets like jquery and foundation, whilst loading page specific code in other modules.
While I can build and copy the js successfully (using grunt requirejs optimiser) into a build folder, the baseUrl in the require.config is obviously now wrong.
baseUrl: 'js' points to public/js instead of public/build/js and so all paths are incorrect.
Is there a way of dynamically updating the baseUrl when running the optimiser? So it points to public/build/js?
Here's my grunt task:
requirejs: {
dist: {
options: {
baseUrl: '<%=pkg.paths.js%>',
dir:'project/public/build/js',
mainConfigFile: '<%=pkg.paths.js%>main.js',
optimize: 'none',
removeCombined: true,
uglify2: {
no_mangle: true,
compress: {
drop_console: true
}
},
modules: [
{
name: 'vendorCommon'
},
{
name: 'dashboard',
exclude: ['vendorCommon']
}
]
}
}
}
Vendor Common
define(['jquery', 'foundation'],
function () {
//Just an empty function, this is a place holder
//module that will be optimized to include the
//common depenendencies listed in this module's dependency array.
});
Require Config
require.config({
baseUrl: '/js',
priority: ['vendorCommon'],
paths: {
'vendorCommon':'vendor/vendor-common',
'jquery':'../bower_components/jquery/dist/jquery.min',
'foundation':'../bower_components/foundation/js/foundation/foundation',
'dashboard':'views/dashboard'
},
shim: {
'foundation': ['jquery']
}
});
I've used the optimizer's onBuildWrite setting to modify some modules when they are optimized. If your configuration is included in your optimized bundle then you could use onBuildWrite to patch it:
onBuildWrite: function (moduleName, path, contents) {
if (moduleName === '<%=pkg.paths.js%>main') {
return contents.replace("baseUrl: '/js'", "baseUrl: '/build/js'");
}
return contents;
}
Disclaimer: I'm writing this off the top of my head. Beware of typos.
Another possibility would be to override baseUrl at runtime. RequireJS is able to combine multiple configurations into a single configuration. A new value of baseUrl in a later call would override the earlier value. So if you have a way to set up the optimized version of your app (for instance, through different HTML served by your server) to call require.config({baseUrl: '/build/js'}); after the call you show in your question but before any code that needs the correct baseUrl, this could also be an option.

Built RequireJS app won't load

So, I built my RequireJS app using grunt-require, which I believe uses r.js behind the scenes. However, upon running the app, I get this every time:
Uncaught Error: Module name "underscore" has not been loaded yet for context: _. Use require([])
http://requirejs.org/docs/errors.html#notloaded require-2.1.9.min.js:8
GET http://localhost:8080/resources/js/app/App.js 404 (Not Found) require-2.1.9.min.js:34
Uncaught Error: Script error for: app/App
http://requirejs.org/docs/errors.html#scripterror
My "main" script is in app/Main.js and looks like this:
require(['common'], function() {
'use strict';
require(['app/App'], function(app) {
app.start();
});
});
And then the build options for grunt-require:
requirejs: {
options: {
baseUrl: 'resources/js',
dir: 'resources/js/build',
main: 'app/Main',
out: null,
optimize: 'uglify2',
skipDirOptimize: true,
priority: ['common'],
preserveLicenseComments: false,
modules: [
{
name: 'common'
},{
name: 'app/Main',
exclude: ['common'],
include: ['app/App']
}
],
paths: { ... },
shim: { ... }
}
}
As you can see, I've included app/App in the build of app/Main.js.
It loads Main.js and common.js separately, as expected, but then it tries to load app/App.js separately. When I look in the built version of Main.js, I see that app/App.js has in fact been built in.
Any ideas why it's trying to load app/App.js separately?
It seems, you forgot findNestedDependencies : true option in your build config and your nested require did not load. See its description.

Avoid duplication of "paths" configuration in RequireJS main file and r.js build file?

Here is (part of) my folder structure:
node-test
bower_components
build
public
main.js
build.js
Running the optimizer with r.js -o build.js and the following configuration works fine:
// main.js file
requirejs.config({
baseUrl: '../bower_components',
paths: {
'domready': 'domready/ready',
'jquery': 'jquery/jquery',
}
});
requirejs(['domready', 'jquery'], function (domReady, $) {
domReady(function () {
});
});
// build.js file
({
baseUrl: "bower_components",
name: "./almond/almond",
include: "./../public/main",
out: "build/main.js",
paths: {
'domready': 'domready/ready',
'jquery': 'jquery/jquery',
},
preserveLicenseComments: false
})
However, if I remove paths configuration in build.js it doesn't work anymore:
Tracing dependencies for: ./almond/almond Error: ENOENT, no such file
or directory
'C:\Users\Marco\Documents\Progetti\nodejs-opt\bower_components\domready.js'
In module tree:
../public/main
Error: Error: ENOENT, no such file or directory
'C:\Users\Marco\Documents\Progetti\nodejs-opt\bower_components\domready.js'
In module tree:
../public/main
at Object.fs.openSync (fs.js:427:18)
I'd like to be DRY, avoiding adding a dependency twice. Is this possible?
If you want to use same configuration from your runtime code to find the location of your libraries, you can use the mainConfigFile option:
...if you prefer the "main" JS file configuration to be read for the build so that you do not have to duplicate the values in a separate configuration, set this property to the location of that main JS file. The first requirejs({}), require({}), requirejs.config({}), or require.config({}) call found in that file will be used.
Something like this:
({
baseUrl: "bower_components",
mainConfigFile: '/some/path/main.js', // adjust path as needed
name: "./almond/almond",
include: "./../public/main",
out: "build/main.js",
preserveLicenseComments: false
})

Require.js + Backbone optimization

Good afternoon,
I'm trying to optimize a source code based on Require.js and Backbone using r.js but I'm getting the following error during the compilation :
Tracing dependencies for: main
Cannot optimize network URL, skipping: empty:.js
TypeError: Cannot read property 'normalize' of undefined
In module tree:
main
app
router
views/main_panel/event_details
helpers/template_manager
My template_manager module does not try to access any 'normalize' property so I don't really understand what is that supposed to mean.
Here's the entry point to my application as well as the require.js configuration.
require.config({
paths: {
order: 'libs/requirejs-plugins/order',
text: 'libs/requirejs-plugins/text',
jQuery: 'libs/jquery/jquery',
Underscore: 'libs/underscore/underscore',
Backbone: 'libs/backbone/backbone',
templates: '../templates',
Sync: 'helpers/sync'
}
});
require([
'app',
'event_manager',
'order!https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js',
'order!libs/underscore/underscore-min',
'order!libs/backbone/backbone-min',
'helpers/objects_extension',
'helpers/date_extension',
'helpers/assets'
], function(App){
App.initialize();
});
The application itself more or less follows what's in this tutorial.
My app.build.js file is as follow
({
appDir: "../",
baseUrl: "js",
dir: "../app-build",
modules: [
{
name: "main"
}
],
paths: {
order: 'empty:',
text: 'empty:',
jQuery: 'empty:',
Underscore: 'empty:',
Backbone: 'empty:',
templates: '../templates',
Sync: 'helpers/sync'
}
})
Thank you for your help.
James Burke says:
The text plugin needs be loaded by the loader for it to process text! depenencies. Loader plugins are executed as part of a build to resolve their resources.
So it should be enough to remove the text: "empty:" from the paths config, and just leave the excludes: in so it is not included in the final build result. This assumes you have text.js available locally to be read by the optimizer.
https://github.com/jrburke/r.js/issues/221

Categories

Resources