I'm trying to use Generator-Backbone generator for Yeoman with RequireJS.
I don't need the lazy-loading of RequireJS, I just am using it for dependency management and organization. It's fine if it's used during development, but when I run grunt:build
I'd love for it to concat all my modules into a single file to minimize HTTP requests.
Currently when I build I am getting this error:
Running "requirejs:dist" (requirejs) task
>> Error: Error: ENOENT, no such file or directory '/Users/Tom/Code/myApp/.tmp/scripts/templates.js'
>> In module tree:
>> main
>> app
>>
>> at Error (native)
If I watch the directory, it seems the templates.js file is created in the right place by the JST task, however it's later overwritten by another task before the requirejs task can complete.
Below is my directory structure, as well as my Gruntfile:
Directory:
├── Gruntfile.js
├── app
│ ├── bower_components
│ ├── index.html
│ ├── scripts
│ │ ├── main.js
│ │ ├── modules
│ │ │ └── admanager.js
│ │ ├── templates
│ │ │ ├── ads.ejs
│ │ │ ├── app.ejs
│ │ │ ├── content.ejs
│ │ │ └── navigation.ejs
│ │ └── views
│ │ ├── ads.js
│ │ ├── app.js
│ │ ├── content.js
│ │ └── navigation.js
│ └── styles
│ └── main.css
├── bower.json
├── dist
├── node_modules
├── package.json
└── test
├── index.html
└── spec
└── test.js
Gruntfile:
'use strict';
var LIVERELOAD_PORT = 35729;
var SERVER_PORT = 9000;
var lrSnippet = require('connect-livereload')({port: LIVERELOAD_PORT});
var mountFolder = function (connect, dir) {
return connect.static(require('path').resolve(dir));
};
module.exports = function (grunt) {
require('time-grunt')(grunt);
require('load-grunt-tasks')(grunt);
// configurable paths
var yeomanConfig = {
app: 'app',
dist: 'dist'
};
grunt.initConfig({
yeoman: yeomanConfig,
watch: {
options: {
nospawn: true,
livereload: LIVERELOAD_PORT
},
livereload: {
options: {
livereload: grunt.option('livereloadport') || LIVERELOAD_PORT
},
files: [
'<%= yeoman.app %>/*.html',
'{.tmp,<%= yeoman.app %>}/styles/{,*/}*.css',
'{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js',
'<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp}',
'<%= yeoman.app %>/scripts/templates/*.{ejs,mustache,hbs}',
'test/spec/**/*.js'
]
},
jst: {
files: [
'<%= yeoman.app %>/scripts/templates/*.ejs'
],
tasks: ['jst']
},
test: {
files: ['<%= yeoman.app %>/scripts/{,*/}*.js', 'test/spec/**/*.js'],
tasks: ['test:true']
}
},
connect: {
options: {
port: grunt.option('port') || SERVER_PORT,
// change this to '0.0.0.0' to access the server from outside
hostname: 'localhost',
livereload: 35729
},
livereload: {
options: {
base: [
'.tmp',
'<%= yeoman.app %>'
],
middleware: function (connect) {
return [
lrSnippet,
mountFolder(connect, '.tmp'),
mountFolder(connect, yeomanConfig.app)
];
},
}
},
test: {
options: {
port: 9001,
middleware: function (connect) {
return [
mountFolder(connect, 'test'),
lrSnippet,
mountFolder(connect, '.tmp'),
mountFolder(connect, yeomanConfig.app)
];
}
}
},
dist: {
options: {
middleware: function (connect) {
return [
mountFolder(connect, yeomanConfig.dist)
];
}
}
}
},
open: {
server: {
path: 'http://localhost:9000'
},
test: {
path: 'http://localhost:<%= connect.test.options.port %>'
}
},
clean: {
dist: ['.tmp', '<%= yeoman.dist %>/*'],
server: '.tmp'
},
jshint: {
options: {
jshintrc: '.jshintrc',
reporter: require('jshint-stylish')
},
all: [
'Gruntfile.js',
'<%= yeoman.app %>/scripts/{,*/}*.js',
'!<%= yeoman.app %>/scripts/vendor/*',
'test/spec/{,*/}*.js'
]
},
mocha: {
all: {
options: {
run: true,
urls: ['http://localhost:<%= connect.test.options.port %>/index.html']
}
}
},
requirejs: {
dist: {
// Options: https://github.com/jrburke/r.js/blob/master/build/example.build.js
options: {
/*added:*/
wrap: true,
almond: true,
replaceRequireScript: [{
files: ['<%= yeoman.dist %>/index.html'],
module: 'main'
}],
modules: [{name: 'main'}],
baseUrl: '<%= yeoman.app %>/scripts',
mainConfigFile: '<%= yeoman.app %>/scripts/main.js',
dir: '.tmp/scripts',
optimize: 'none',
useStrict: true,
paths: {
'templates': '../../<%= yeoman.app %>/scripts/templates',
'jquery': '../../<%= yeoman.app %>/bower_components/jquery/jquery',
'underscore': '../../<%= yeoman.app %>/bower_components/lodash/dist/lodash',
'backbone': '../../<%= yeoman.app %>/bower_components/backbone/backbone'
}
/*end added*/
/*
baseUrl: '<%= yeoman.app %>/scripts',
optimize: 'none',
paths: {
'templates': '../../.tmp/scripts/templates',
'jquery': '../../<%= yeoman.app %>/bower_components/jquery/dist/jquery',
'underscore': '../../<%= yeoman.app %>/bower_components/lodash/dist/lodash',
'backbone': '../../<%= yeoman.app %>/bower_components/backbone/backbone'
},
preserveLicenseComments: false,
useStrict: true,
wrap: true
*/
}
}
},
/*added:*/
uglify: {
dist: {
files: {
'<%= yeoman.dist %>/scripts/main.js': [
'.tmp/scripts/main.js'
]
}
}
},/*end added*/
useminPrepare: {
html: '<%= yeoman.app %>/index.html',
options: {
dest: '<%= yeoman.dist %>'
}
},
usemin: {
html: ['<%= yeoman.dist %>/{,*/}*.html'],
css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
options: {
dirs: ['<%= yeoman.dist %>']
}
},
imagemin: {
dist: {
files: [{
expand: true,
cwd: '<%= yeoman.app %>/images',
src: '{,*/}*.{png,jpg,jpeg}',
dest: '<%= yeoman.dist %>/images'
}]
}
},
cssmin: {
dist: {
files: {
'<%= yeoman.dist %>/styles/main.css': [
'.tmp/styles/{,*/}*.css',
'<%= yeoman.app %>/styles/{,*/}*.css'
]
}
}
},
htmlmin: {
dist: {
options: {
/*removeCommentsFromCDATA: true,
// https://github.com/yeoman/grunt-usemin/issues/44
//collapseWhitespace: true,
collapseBooleanAttributes: true,
removeAttributeQuotes: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeOptionalTags: true*/
},
files: [{
expand: true,
cwd: '<%= yeoman.app %>',
src: '*.html',
dest: '<%= yeoman.dist %>'
}]
}
},
copy: {
dist: {
files: [{
expand: true,
dot: true,
cwd: '<%= yeoman.app %>',
dest: '<%= yeoman.dist %>',
src: [
'*.{ico,txt}',
'images/{,*/}*.{webp,gif}',
'styles/fonts/{,*/}*.*',
]
}, {
src: 'node_modules/apache-server-configs/dist/.htaccess',
dest: '<%= yeoman.dist %>/.htaccess'
}]
}
},
bower: {
all: {
rjsConfig: '<%= yeoman.app %>/scripts/main.js'
}
},
jst: {
options: {
amd: true
},
compile: {
files: {
// '.tmp/scripts/templates.js': ['<%= yeoman.app %>/scripts/templates/*.ejs']
'.tmp/scripts/templates.js': ['<%= yeoman.app %>/scripts/templates/*.ejs']
}
}
},
rev: {
dist: {
files: {
src: [
'<%= yeoman.dist %>/scripts/{,*/}*.js',
'<%= yeoman.dist %>/styles/{,*/}*.css',
'<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp}',
'/styles/fonts/{,*/}*.*',
]
}
}
}
});
grunt.registerTask('createDefaultTemplate', function () {
grunt.file.write('.tmp/scripts/templates.js', 'this.JST = this.JST || {};');
});
grunt.registerTask('server', function (target) {
grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
grunt.task.run(['serve' + (target ? ':' + target : '')]);
});
grunt.registerTask('serve', function (target) {
if (target === 'dist') {
return grunt.task.run(['build', 'open:server', 'connect:dist:keepalive']);
}
if (target === 'test') {
return grunt.task.run([
'clean:server',
'createDefaultTemplate',
'jst',
'connect:test',
'open:test',
'watch'
]);
}
grunt.task.run([
'clean:server',
'createDefaultTemplate',
'jst',
'connect:livereload',
'open:server',
'watch'
]);
});
grunt.registerTask('test', function (isConnected) {
isConnected = Boolean(isConnected);
var testTasks = [
'clean:server',
'createDefaultTemplate',
'jst',
'connect:test',
'mocha',
];
if(!isConnected) {
return grunt.task.run(testTasks);
} else {
// already connected so not going to connect again, remove the connect:test task
testTasks.splice(testTasks.indexOf('connect:test'), 1);
return grunt.task.run(testTasks);
}
});
grunt.registerTask('build', [
'clean:dist',
'createDefaultTemplate',
'jst',
'useminPrepare',
'imagemin',
'htmlmin',
'concat',
'cssmin',
'uglify:generated',
'copy',
'requirejs',
'uglify:dist',
'rev',
'usemin'
]);
grunt.registerTask('default', [
'jshint',
'test',
'build'
]);
};
Main.js:
/*global require*/
'use strict';
require.config({
shim: {
},
paths: {
// LIBS
jquery: '../bower_components/jquery/jquery',
backbone: '../bower_components/backbone/backbone',
underscore: '../bower_components/lodash/dist/lodash',
cookies: '../bower_components/js-cookie/src/js.cookie',
// CUSTOM MODULES
admanager: './modules/admanager',
// APP-SPECIFIC
app: '../scripts/views/app',
content: '../scripts/views/content',
ads: '../scripts/views/ads',
navigation: '../scripts/views/navigation'
}
});
require([
'backbone',
'app',
], function (Backbone, App) {
Backbone.history.start();
window.myApp = new App();
});
I spent nearly 2 entire hours to solve this and finally figured it out. Below is how my requireJs task looks like
options: {
wrap: true,
almond: true,
name: "../../<%= yeoman.app %>/bower_components/almond/almond",
include: ['main.js'],
out: "<%= yeoman.dist %>/scripts/app.js",
replaceRequireScript: [{
files: ['<%= yeoman.dist %>/index.html'],
module: 'main'
}],
baseUrl: '<%= yeoman.app %>/scripts',
mainConfigFile: '<%= yeoman.app %>/scripts/main.js',
optimize: 'uglify2',
useStrict: true,
paths: {
'templates': '../../.tmp/scripts/templates',
'jquery': '../../<%= yeoman.app %>/bower_components/jquery/dist/jquery',
'underscore': '../../<%= yeoman.app %>/bower_components/lodash/dist/lodash',
'backbone': '../../<%= yeoman.app %>/bower_components/backbone/backbone'
}
}
You are missing certain options like name
, include
, out
. Also dir
should not be used.
This is how registeredTask 'build' looks like
grunt.registerTask('build', [
'clean:dist',
'createDefaultTemplate',
'jst',
'sass:dist',
'useminPrepare',
'imagemin',
'htmlmin',
'concat',
'cssmin',
'uglify',
'copy',
'requirejs',
'rev',
'usemin'
]);
This configuration worked for me. I hope it works for you too.