├── .editorconfig ├── .gitattributes ├── .gitignore ├── .jshintrc ├── .travis.yml ├── contributing.md ├── generators ├── all │ └── index.js ├── app │ ├── index.js │ └── templates │ │ ├── Gruntfile.js │ │ ├── _bower.json │ │ ├── _package.json │ │ ├── app.coffee │ │ ├── app.js │ │ ├── app │ │ ├── 404.html │ │ ├── favicon.ico │ │ └── robots.txt │ │ ├── bowerrc │ │ ├── editorconfig │ │ ├── gitattributes │ │ ├── gitignore │ │ ├── index.html │ │ ├── jshintrc │ │ ├── main.scss │ │ ├── requirejs_app.coffee │ │ └── requirejs_app.js ├── collection │ └── index.js ├── model │ └── index.js ├── router │ └── index.js ├── templates │ ├── coffeescript │ │ ├── collection.coffee │ │ ├── model.coffee │ │ ├── requirejs │ │ │ ├── collection.coffee │ │ │ ├── model.coffee │ │ │ ├── router.coffee │ │ │ └── view.coffee │ │ ├── router.coffee │ │ ├── view.coffee │ │ └── view.ejs │ ├── collection.js │ ├── model.js │ ├── requirejs │ │ ├── collection.js │ │ ├── model.js │ │ ├── router.js │ │ └── view.js │ ├── router.js │ ├── view.ejs │ └── view.js └── view │ └── index.js ├── package.json ├── readme.md ├── script-base.js ├── test ├── helper.js ├── test-apppath.js ├── test-coffee-requirejs.js ├── test-coffee.js ├── test-foo.js ├── test-handlebars.js ├── test-mustache.js └── test-requirejs.js └── util.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | temp/ 3 | coverage/ 4 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "esnext": true, 4 | "bitwise": false, 5 | "camelcase": true, 6 | "curly": false, 7 | "eqeqeq": true, 8 | "immed": true, 9 | "indent": 2, 10 | "latedef": true, 11 | "newcap": true, 12 | "noarg": true, 13 | "quotmark": "single", 14 | "undef": true, 15 | "unused": true, 16 | "strict": true 17 | } 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - '4' 5 | - '0.12' 6 | - '0.10' 7 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | See the [contributing docs](https://github.com/yeoman/yeoman/blob/master/contributing.md) 2 | -------------------------------------------------------------------------------- /generators/all/index.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var util = require('util'); 3 | var mkdirp = require('mkdirp'); 4 | var yeoman = require('yeoman-generator'); 5 | var pascalCase = require('pascal-case'); 6 | 7 | var BackboneGenerator = yeoman.generators.Base.extend({ 8 | constructor: function () { 9 | yeoman.generators.Base.apply(this, arguments); 10 | 11 | this.argument('app_name', { type: String, required: false }); 12 | this.appname = this.app_name || this.appname; 13 | this.appname = pascalCase(this.appname); 14 | 15 | this.env.options.appPath = this.options.appPath || 'app'; 16 | this.config.set('appPath', this.env.options.appPath); 17 | 18 | this.dirs = 'models collections views routes helpers templates'.split(' '); 19 | 20 | this.option('coffee'); 21 | 22 | this.args = [this.appname]; 23 | 24 | this.option('coffee'); 25 | this.env.options.appPath = this.options.appPath || 'app'; 26 | this.config.set('appPath', this.env.options.appPath); 27 | 28 | this.args = ['application']; 29 | 30 | if (this.options.coffee) { 31 | this.args.push('--coffee'); 32 | } 33 | 34 | this.option('requirejs'); 35 | 36 | if (this.options.requirejs) { 37 | this.args.push('--requirejs'); 38 | } 39 | 40 | if (this.options['template-framework']) { 41 | this.env.options['template-framework'] = this.options['template-framework']; 42 | } 43 | 44 | this.testFramework = this.options['test-framework'] || 'mocha'; 45 | 46 | this.on('end', function () { 47 | if (/^.*test$/.test(process.cwd())) { 48 | process.chdir('..'); 49 | } 50 | this.installDependencies({ skipInstall: this.options['skip-install'] }); 51 | }); 52 | }, 53 | 54 | writing: { 55 | createDirLayout: function () { 56 | var done = this.async(); 57 | this.dirs.forEach(function (dir) { 58 | this.log.create('app/scripts/' + dir); 59 | mkdirp(path.join('app/scripts', dir), done); 60 | }.bind(this)); 61 | } 62 | }, 63 | 64 | install: function () { 65 | this.composeWith('backbone:router', {arguments: this.args}); 66 | this.composeWith('backbone:view', {arguments: this.args}); 67 | this.composeWith('backbone:model', {arguments: this.args}); 68 | this.composeWith('backbone:collection', {arguments: this.args}); 69 | } 70 | }); 71 | 72 | module.exports = BackboneGenerator; 73 | -------------------------------------------------------------------------------- /generators/app/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var util = require('util'); 3 | var path = require('path'); 4 | var ejs = require('ejs'); 5 | var htmlWiring = require('html-wiring'); 6 | var mkdirp = require('mkdirp'); 7 | var pascalCase = require('pascal-case'); 8 | var paramCase = require('param-case'); 9 | var yeoman = require('yeoman-generator'); 10 | 11 | var BackboneGenerator = yeoman.generators.Base.extend({ 12 | constructor: function () { 13 | yeoman.generators.Base.apply(this, arguments); 14 | 15 | this.option('appPath', { 16 | desc: 'Name of application directory', 17 | type: 'String', 18 | defaults: 'app', 19 | banner: 'some banner' 20 | }); 21 | 22 | this.option('requirejs', { 23 | desc: 'Support requirejs', 24 | defaults: false 25 | }); 26 | 27 | this.option('template-framework', { 28 | desc: 'Choose template framework. lodash/handlebars/mustache', 29 | type: 'String', 30 | defaults: 'lodash' 31 | }); 32 | 33 | this.option('test-framework', { 34 | desc: 'Choose test framework. mocha/jasmine', 35 | type: 'String', 36 | defaults: 'mocha' 37 | }); 38 | 39 | this.option('skip-install', { 40 | desc: 'Skip the bower and node installations', 41 | defaults: false 42 | }); 43 | 44 | this.argument('app_name', { type: String, required: false }); 45 | this.appname = this.app_name || this.appname; 46 | this.appname = pascalCase(this.appname); 47 | 48 | this.env.options.appPath = this.options.appPath || 'app'; 49 | this.config.set('appPath', this.env.options.appPath); 50 | 51 | this.testFramework = this.options['test-framework'] || 'mocha'; 52 | this.templateFramework = this.options['template-framework'] || 'lodash'; 53 | 54 | this.config.defaults({ 55 | appName: this.appname, 56 | ui: this.options.ui, 57 | coffee: this.options.coffee, 58 | testFramework: this.testFramework, 59 | templateFramework: this.templateFramework, 60 | sassBootstrap: this.sassBootstrap, 61 | includeRequireJS: this.options.requirejs 62 | }); 63 | 64 | this.indexFile = htmlWiring.readFileAsString(this.templatePath('index.html')); 65 | }, 66 | 67 | prompting: function () { 68 | var cb = this.async(); 69 | 70 | // welcome message 71 | this.log(this.yeoman); 72 | this.log('Out of the box I include HTML5 Boilerplate, jQuery and Backbone.js.'); 73 | 74 | var prompts = [{ 75 | type: 'checkbox', 76 | name: 'features', 77 | message: 'What more would you like?', 78 | choices: [{ 79 | name: 'Twitter Bootstrap for Sass', 80 | value: 'sassBootstrap', 81 | checked: true 82 | }, { 83 | name: 'Use CoffeeScript', 84 | value: 'coffee', 85 | checked: this.options.coffee || false 86 | }, { 87 | name: 'Use RequireJS', 88 | value: 'requirejs', 89 | checked: this.options.requirejs || false 90 | }, { 91 | name: 'Use Modernizr', 92 | value: 'modernizr', 93 | checked: false 94 | }] 95 | }]; 96 | 97 | this.prompt(prompts, function (answers) { 98 | var features = answers.features; 99 | 100 | function hasFeature(feat) { return features.indexOf(feat) !== -1; } 101 | 102 | // manually deal with the response, get back and store the results. 103 | // we change a bit this way of doing to automatically do this in the self.prompt() method. 104 | this.sassBootstrap = hasFeature('sassBootstrap'); 105 | this.includeRequireJS = hasFeature('requirejs'); 106 | this.includeModernizr = hasFeature('modernizr'); 107 | this.config.set('sassBootstrap', this.sassBootstrap); 108 | 109 | 110 | if (!this.options.coffee) { 111 | this.options.coffee = hasFeature('coffee'); 112 | this.config.set('coffee', this.options.coffee); 113 | } 114 | 115 | if (!this.options.requirejs) { 116 | this.options.requirejs = this.includeRequireJS; 117 | this.config.set('includeRequireJS', this.includeRequireJS); 118 | } 119 | cb(); 120 | }.bind(this)); 121 | }, 122 | 123 | writing: { 124 | 125 | git: function () { 126 | this.fs.copyTpl( 127 | this.templatePath('gitignore'), 128 | this.destinationPath('.gitignore'), 129 | { 130 | appPath: this.env.options.appPath 131 | } 132 | ); 133 | this.fs.copyTpl( 134 | this.templatePath('gitattributes'), 135 | this.destinationPath('.gitattributes') 136 | ); 137 | }, 138 | 139 | bower: function () { 140 | this.fs.copyTpl( 141 | this.templatePath('bowerrc'), 142 | this.destinationPath('.bowerrc'), 143 | { 144 | appPath: this.env.options.appPath 145 | } 146 | ); 147 | this.fs.copyTpl( 148 | this.templatePath('_bower.json'), 149 | this.destinationPath('bower.json'), 150 | { 151 | appParamCaseName: paramCase(this.appname), 152 | sassBootstrap: this.sassBootstrap, 153 | includeRequireJS: this.includeRequireJS, 154 | includeModernizr: this.includeModernizr, 155 | templateFramework: this.templateFramework 156 | } 157 | ); 158 | }, 159 | 160 | jshint: function () { 161 | this.fs.copyTpl( 162 | this.templatePath('jshintrc'), 163 | this.destinationPath('.jshintrc'), 164 | { 165 | appName: this.appname, 166 | appPascalCaseName: pascalCase(this.appname), 167 | includeRequireJS: this.includeRequireJS 168 | } 169 | ); 170 | }, 171 | 172 | editorConfig: function () { 173 | this.fs.copyTpl( 174 | this.templatePath('editorconfig'), 175 | this.destinationPath('.editorconfig') 176 | ); 177 | }, 178 | 179 | gruntfile: function () { 180 | this.fs.copyTpl( 181 | this.templatePath('Gruntfile.js'), 182 | this.destinationPath('Gruntfile.js'), 183 | { 184 | appPath: this.env.options.appPath, 185 | hasCoffee: this.options.coffee, 186 | includeRequireJS: this.includeRequireJS, 187 | sassBootstrap: this.sassBootstrap, 188 | templateFramework: this.templateFramework, 189 | testFramework: this.testFramework 190 | } 191 | ); 192 | }, 193 | 194 | packageJSON: function () { 195 | this.fs.copyTpl( 196 | this.templatePath('_package.json'), 197 | this.destinationPath('package.json'), 198 | { 199 | hasCoffee: this.options.coffee, 200 | includeRequireJS: this.includeRequireJS, 201 | sassBootstrap: this.sassBootstrap, 202 | templateFramework: this.templateFramework, 203 | testFramework: this.testFramework 204 | } 205 | ); 206 | }, 207 | 208 | mainStylesheet: function () { 209 | var contentText = [ 210 | 'body {\n background: #fafafa;\n}', 211 | '\n.hero-unit {\n margin: 50px auto 0 auto;\n width: 300px;\n}' 212 | ]; 213 | var ext = '.css'; 214 | if (this.sassBootstrap) { 215 | this.fs.copyTpl( 216 | this.templatePath('main.scss'), 217 | this.destinationPath(this.env.options.appPath + '/styles/main.scss') 218 | ); 219 | return; 220 | } 221 | this.fs.write( 222 | this.destinationPath(this.env.options.appPath + '/styles/main' + ext), 223 | contentText.join('\n') 224 | ); 225 | }, 226 | 227 | writeIndex: function () { 228 | if (this.includeRequireJS) { 229 | return; 230 | } 231 | 232 | this.indexFile = htmlWiring.readFileAsString(this.templatePath('index.html')); 233 | this.indexFile = ejs.render( 234 | this.indexFile, 235 | { 236 | appName: this.appname, 237 | includeModernizr: this.includeModernizr, 238 | includeRequireJS: this.includeRequireJS, 239 | sassBootstrap: this.sassBootstrap 240 | } 241 | ); 242 | 243 | var vendorJS = [ 244 | 'bower_components/jquery/dist/jquery.js', 245 | 'bower_components/lodash/dist/lodash.compat.js', 246 | 'bower_components/backbone/backbone.js' 247 | ]; 248 | 249 | if (this.templateFramework === 'handlebars') { 250 | vendorJS.push('bower_components/handlebars/handlebars.js'); 251 | } 252 | 253 | this.indexFile = htmlWiring.appendScripts(this.indexFile, 'scripts/vendor.js', vendorJS); 254 | 255 | if (this.sassBootstrap) { 256 | // wire Twitter Bootstrap plugins 257 | this.indexFile = htmlWiring.appendScripts(this.indexFile, 'scripts/plugins.js', [ 258 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/affix.js', 259 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/alert.js', 260 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/button.js', 261 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/carousel.js', 262 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/collapse.js', 263 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/dropdown.js', 264 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/modal.js', 265 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/tooltip.js', 266 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/popover.js', 267 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/scrollspy.js', 268 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/tab.js', 269 | 'bower_components/bootstrap-sass-official/assets/javascripts/bootstrap/transition.js' 270 | ]); 271 | } 272 | 273 | this.indexFile = htmlWiring.appendFiles({ 274 | html: this.indexFile, 275 | fileType: 'js', 276 | searchPath: ['.tmp', this.env.options.appPath], 277 | optimizedPath: 'scripts/main.js', 278 | sourceFileList: [ 279 | 'scripts/main.js', 280 | 'scripts/templates.js' 281 | ] 282 | }); 283 | }, 284 | 285 | writeIndexWithRequirejs: function () { 286 | if (!this.includeRequireJS) { 287 | return; 288 | } 289 | this.indexFile = htmlWiring.readFileAsString(this.templatePath('index.html')); 290 | this.indexFile = ejs.render( 291 | this.indexFile, 292 | { 293 | appName: this.appname, 294 | includeModernizr: this.includeModernizr, 295 | includeRequireJS: this.includeRequireJS, 296 | sassBootstrap: this.sassBootstrap 297 | } 298 | ); 299 | }, 300 | 301 | setupEnv: function () { 302 | mkdirp.sync( 303 | this.templatePath(this.env.options.appPath) 304 | ); 305 | mkdirp.sync( 306 | this.templatePath(this.env.options.appPath + '/scripts') 307 | ); 308 | mkdirp.sync( 309 | this.templatePath(this.env.options.appPath + '/scripts/vendor/') 310 | ); 311 | mkdirp.sync( 312 | this.templatePath(this.env.options.appPath + '/styles') 313 | ); 314 | mkdirp.sync( 315 | this.templatePath(this.env.options.appPath + '/images') 316 | ); 317 | this.fs.copyTpl( 318 | this.templatePath('app/404.html'), 319 | this.destinationPath(this.env.options.appPath + '/404.html') 320 | ); 321 | this.fs.copyTpl( 322 | this.templatePath('app/favicon.ico'), 323 | this.destinationPath(this.env.options.appPath + '/favicon.ico') 324 | ); 325 | this.fs.copyTpl( 326 | this.templatePath('app/robots.txt'), 327 | this.destinationPath(this.env.options.appPath + '/robots.txt') 328 | ); 329 | this.fs.write( 330 | this.destinationPath(path.join(this.env.options.appPath, '/index.html')), 331 | this.indexFile 332 | ); 333 | }, 334 | 335 | createRequireJsAppFile: function () { 336 | if (!this.includeRequireJS) { 337 | return; 338 | } 339 | this._writeTemplate( 340 | 'requirejs_app', 341 | this.env.options.appPath + '/scripts/main', 342 | { 343 | sassBootstrap: this.sassBootstrap, 344 | templateFramework: this.templateFramework 345 | } 346 | ); 347 | }, 348 | 349 | createAppFile: function () { 350 | if (this.includeRequireJS) { 351 | return; 352 | } 353 | this._writeTemplate( 354 | 'app', 355 | this.env.options.appPath + '/scripts/main', 356 | { 357 | appPascalCaseName: pascalCase(this.appname) 358 | } 359 | ); 360 | }, 361 | 362 | composeTest: function () { 363 | if (['backbone:app', 'backbone'].indexOf(this.options.namespace) >= 0) { 364 | this.composeWith(this.testFramework, { 365 | 'skip-install': this.options['skip-install'], 366 | 'ui': this.options.ui, 367 | 'skipMessage': true, 368 | }); 369 | } 370 | } 371 | }, 372 | 373 | setSuffix: function () { 374 | this.scriptSuffix = '.js'; 375 | 376 | if (this.env.options.coffee || this.options.coffee) { 377 | this.scriptSuffix = '.coffee'; 378 | } 379 | }, 380 | 381 | _writeTemplate: function (source, destination, data) { 382 | if (typeof source === 'undefined' || typeof destination === 'undefined') { 383 | return; 384 | } 385 | 386 | if (typeof this.scriptSuffix === 'undefined') { 387 | this.setSuffix(); 388 | } 389 | 390 | var ext = this.scriptSuffix; 391 | this.fs.copyTpl( 392 | this.templatePath(source + ext), 393 | this.destinationPath(destination + ext), 394 | data 395 | ); 396 | }, 397 | 398 | install: function () { 399 | var shouldInstall = !this.options['skip-install']; 400 | var isInstallable = ['backbone:app', 'backbone'].indexOf(this.options.namespace) > -1; 401 | if (shouldInstall && isInstallable) { 402 | this.npmInstall(); 403 | this.bowerInstall('', { 404 | 'config.cwd': this.destinationPath('.'), 405 | 'config.directory': path.join(this.config.get('appPath'), 'bower_components') 406 | }); 407 | } 408 | } 409 | }); 410 | 411 | module.exports = BackboneGenerator; 412 | -------------------------------------------------------------------------------- /generators/app/templates/Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var LIVERELOAD_PORT = 35729; 3 | var SERVER_PORT = 9000; 4 | var lrSnippet = require('connect-livereload')({port: LIVERELOAD_PORT}); 5 | var mountFolder = function (connect, dir) { 6 | return connect.static(require('path').resolve(dir)); 7 | }; 8 | 9 | // # Globbing 10 | // for performance reasons we're only matching one level down: 11 | // 'test/spec/{,*/}*.js' 12 | // use this if you want to match all subfolders: 13 | // 'test/spec/**/*.js' 14 | // templateFramework: '<%= templateFramework %>' 15 | 16 | module.exports = function (grunt) { 17 | 18 | // show elapsed time at the end 19 | require('time-grunt')(grunt); 20 | 21 | // Automatically load required Grunt tasks 22 | require('jit-grunt')(grunt, { 23 | useminPrepare: 'grunt-usemin' 24 | }); 25 | 26 | // configurable paths 27 | var yeomanConfig = { 28 | app: '<%= appPath %>', 29 | dist: 'dist' 30 | }; 31 | 32 | grunt.initConfig({ 33 | yeoman: yeomanConfig, 34 | watch: { 35 | options: { 36 | nospawn: true, 37 | livereload: LIVERELOAD_PORT 38 | },<% if (hasCoffee) { %> 39 | coffee: { 40 | files: ['<%%= yeoman.app %>/scripts/{,*/}*.coffee'], 41 | tasks: ['coffee:dist'] 42 | }, 43 | coffeeTest: { 44 | files: ['test/spec/{,*/}*.coffee'], 45 | tasks: ['coffee:test'] 46 | },<% } %><% if (sassBootstrap) { %> 47 | sass: { 48 | files: ['<%%= yeoman.app %>/styles/{,*/}*.{scss,sass}'], 49 | tasks: ['sass:server'] 50 | },<% } %> 51 | livereload: { 52 | options: { 53 | livereload: grunt.option('livereloadport') || LIVERELOAD_PORT 54 | }, 55 | files: [ 56 | '<%%= yeoman.app %>/*.html', 57 | '{.tmp,<%%= yeoman.app %>}/styles/{,*/}*.css', 58 | '{.tmp,<%%= yeoman.app %>}/scripts/{,*/}*.js', 59 | '<%%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp}', 60 | '<%%= yeoman.app %>/scripts/templates/*.{ejs,mustache,hbs}', 61 | 'test/spec/**/*.js' 62 | ] 63 | }<% if (templateFramework === 'mustache') { %>, 64 | mustache: { 65 | files: [ 66 | '<%%= yeoman.app %>/scripts/templates/*.mustache' 67 | ], 68 | tasks: ['mustache'] 69 | }<% } else if (templateFramework === 'handlebars') { %>, 70 | handlebars: { 71 | files: [ 72 | '<%%= yeoman.app %>/scripts/templates/*.hbs' 73 | ], 74 | tasks: ['handlebars'] 75 | }<% } else { %>, 76 | jst: { 77 | files: [ 78 | '<%%= yeoman.app %>/scripts/templates/*.ejs' 79 | ], 80 | tasks: ['jst'] 81 | }<% } %>, 82 | test: { 83 | files: ['<%%= yeoman.app %>/scripts/{,*/}*.js', 'test/spec/**/*.js'], 84 | tasks: ['test:true'] 85 | } 86 | }, 87 | connect: { 88 | options: { 89 | port: grunt.option('port') || SERVER_PORT, 90 | // change this to '0.0.0.0' to access the server from outside 91 | hostname: 'localhost' 92 | }, 93 | livereload: { 94 | options: { 95 | middleware: function (connect) { 96 | return [ 97 | lrSnippet, 98 | mountFolder(connect, '.tmp'), 99 | mountFolder(connect, yeomanConfig.app) 100 | ]; 101 | } 102 | } 103 | }, 104 | test: { 105 | options: { 106 | port: 9001, 107 | middleware: function (connect) { 108 | return [ 109 | mountFolder(connect, 'test'), 110 | lrSnippet, 111 | mountFolder(connect, '.tmp'), 112 | mountFolder(connect, yeomanConfig.app) 113 | ]; 114 | } 115 | } 116 | }, 117 | dist: { 118 | options: { 119 | middleware: function (connect) { 120 | return [ 121 | mountFolder(connect, yeomanConfig.dist) 122 | ]; 123 | } 124 | } 125 | } 126 | }, 127 | open: { 128 | server: { 129 | path: 'http://localhost:<%%= connect.options.port %>' 130 | }, 131 | test: { 132 | path: 'http://localhost:<%%= connect.test.options.port %>' 133 | } 134 | }, 135 | clean: { 136 | dist: ['.tmp', '<%%= yeoman.dist %>/*'], 137 | server: '.tmp' 138 | }, 139 | jshint: { 140 | options: { 141 | jshintrc: '.jshintrc', 142 | reporter: require('jshint-stylish') 143 | }, 144 | all: [ 145 | 'Gruntfile.js', 146 | '<%%= yeoman.app %>/scripts/{,*/}*.js', 147 | '!<%%= yeoman.app %>/scripts/vendor/*', 148 | 'test/spec/{,*/}*.js' 149 | ] 150 | }, 151 | <% if (testFramework === 'mocha') { -%> 152 | mocha: { 153 | all: { 154 | options: { 155 | run: true, 156 | urls: ['http://localhost:<%%= connect.test.options.port %>/index.html'] 157 | } 158 | } 159 | }, 160 | <% } else { -%> 161 | jasmine: { 162 | all:{ 163 | src : '<%%= yeoman.app %>/scripts/{,*/}*.js', 164 | options: { 165 | keepRunner: true, 166 | specs : 'test/spec/**/*.js', 167 | vendor : [ 168 | '<%%= yeoman.app %>/bower_components/jquery/dist/jquery.js', 169 | '<%%= yeoman.app %>/bower_components/lodash/dist/lodash.js', 170 | '<%%= yeoman.app %>/bower_components/backbone/backbone.js', 171 | '.tmp/scripts/templates.js' 172 | ] 173 | } 174 | } 175 | }, 176 | <% } -%> 177 | <% if (hasCoffee) { -%> 178 | coffee: { 179 | dist: { 180 | files: [{ 181 | // rather than compiling multiple files here you should 182 | // require them into your main .coffee file 183 | expand: true, 184 | cwd: '<%%= yeoman.app %>/scripts', 185 | src: '{,*/}*.coffee', 186 | dest: '.tmp/scripts', 187 | ext: '.js' 188 | }] 189 | }, 190 | test: { 191 | files: [{ 192 | expand: true, 193 | cwd: 'test/spec', 194 | src: '{,*/}*.coffee', 195 | dest: '.tmp/spec', 196 | ext: '.js' 197 | }] 198 | } 199 | }, 200 | <% } -%> 201 | <% if (sassBootstrap) { -%> 202 | sass: { 203 | options: { 204 | sourceMap: true, 205 | includePaths: ['app/bower_components'] 206 | }, 207 | dist: { 208 | files: [{ 209 | expand: true, 210 | cwd: '<%%= yeoman.app %>/styles', 211 | src: ['*.{scss,sass}'], 212 | dest: '.tmp/styles', 213 | ext: '.css' 214 | }] 215 | }, 216 | server: { 217 | files: [{ 218 | expand: true, 219 | cwd: '<%%= yeoman.app %>/styles', 220 | src: ['*.{scss,sass}'], 221 | dest: '.tmp/styles', 222 | ext: '.css' 223 | }] 224 | } 225 | }, 226 | <% } -%> 227 | <% if (includeRequireJS) { -%> 228 | requirejs: { 229 | dist: { 230 | // Options: https://github.com/jrburke/r.js/blob/master/build/example.build.js 231 | options: { 232 | almond: true, 233 | 234 | replaceRequireScript: [{ 235 | files: ['<%%= yeoman.dist %>/index.html'], 236 | module: 'main' 237 | }], 238 | 239 | modules: [{name: 'main'}], 240 | baseUrl: '<%%= yeoman.app %>/scripts', 241 | <% if (hasCoffee) { %> 242 | paths: { 243 | 'main': '../../.tmp/scripts/main' 244 | }, 245 | allowSourceOverwrites: true, 246 | mainConfigFile: '.tmp/scripts/main.js', // contains path specifications and nothing else important with respect to config 247 | <% } else { %> 248 | mainConfigFile: '<%%= yeoman.app %>/scripts/main.js', // contains path specifications and nothing else important with respect to config 249 | <% } %> 250 | keepBuildDir: true, 251 | dir: '.tmp/scripts', 252 | 253 | optimize: 'none', // optimize by uglify task 254 | useStrict: true<% if (templateFramework !== 'handlebars') { %>, 255 | wrap: true 256 | <% } %> 257 | } 258 | } 259 | }, 260 | uglify: { 261 | dist: { 262 | files: { 263 | '<%%= yeoman.dist %>/scripts/main.js': [ 264 | '.tmp/scripts/main.js' 265 | ] 266 | } 267 | } 268 | }, 269 | <% } else { -%> 270 | // not enabled since usemin task does concat and uglify 271 | // check index.html to edit your build targets 272 | // enable this task if you prefer defining your build targets here 273 | /*uglify: { 274 | dist: {} 275 | },*/ 276 | <% } -%> 277 | useminPrepare: { 278 | html: '<%%= yeoman.app %>/index.html', 279 | options: { 280 | dest: '<%%= yeoman.dist %>' 281 | } 282 | }, 283 | usemin: { 284 | html: ['<%%= yeoman.dist %>/{,*/}*.html'], 285 | css: ['<%%= yeoman.dist %>/styles/{,*/}*.css'], 286 | options: { 287 | dirs: ['<%%= yeoman.dist %>'] 288 | } 289 | }, 290 | imagemin: { 291 | dist: { 292 | files: [{ 293 | expand: true, 294 | cwd: '<%%= yeoman.app %>/images', 295 | src: '{,*/}*.{png,jpg,jpeg}', 296 | dest: '<%%= yeoman.dist %>/images' 297 | }] 298 | } 299 | }, 300 | cssmin: { 301 | dist: { 302 | files: { 303 | '<%%= yeoman.dist %>/styles/main.css': [ 304 | '.tmp/styles/{,*/}*.css', 305 | '<%%= yeoman.app %>/styles/{,*/}*.css' 306 | ] 307 | } 308 | } 309 | }, 310 | htmlmin: { 311 | dist: { 312 | options: { 313 | /*removeCommentsFromCDATA: true, 314 | // https://github.com/yeoman/grunt-usemin/issues/44 315 | //collapseWhitespace: true, 316 | collapseBooleanAttributes: true, 317 | removeAttributeQuotes: true, 318 | removeRedundantAttributes: true, 319 | useShortDoctype: true, 320 | removeEmptyAttributes: true, 321 | removeOptionalTags: true*/ 322 | }, 323 | files: [{ 324 | expand: true, 325 | cwd: '<%%= yeoman.app %>', 326 | src: '*.html', 327 | dest: '<%%= yeoman.dist %>' 328 | }] 329 | } 330 | }, 331 | copy: { 332 | dist: { 333 | files: [{ 334 | expand: true, 335 | dot: true, 336 | cwd: '<%%= yeoman.app %>', 337 | dest: '<%%= yeoman.dist %>', 338 | src: [ 339 | '*.{ico,txt}', 340 | 'images/{,*/}*.{webp,gif}', 341 | 'styles/fonts/{,*/}*.*', 342 | <% if (sassBootstrap) { -%> 343 | 'bower_components/bootstrap-sass-official/assets/fonts/bootstrap/*.*' 344 | <% } -%> 345 | ] 346 | }, { 347 | src: 'node_modules/apache-server-configs/dist/.htaccess', 348 | dest: '<%%= yeoman.dist %>/.htaccess' 349 | }] 350 | } 351 | }, 352 | <% if (includeRequireJS) { -%> 353 | bower: { 354 | all: { 355 | rjsConfig: '<%%= yeoman.app %>/scripts/main.js' 356 | } 357 | }, 358 | <% } -%> 359 | <% if (templateFramework === 'mustache') { -%> 360 | mustache: { 361 | files: { 362 | src: '<%%= yeoman.app %>/scripts/templates/', 363 | dest: '.tmp/scripts/templates.js', 364 | options: { 365 | <% if (includeRequireJS) { -%> 366 | prefix: 'define(function() { this.JST = ', 367 | postfix: '; return this.JST;});' 368 | <% } else { -%> 369 | prefix: 'this.JST = ', 370 | postfix: ';' 371 | <% } -%> 372 | } 373 | } 374 | } 375 | <% } else if (templateFramework === 'handlebars') { -%> 376 | handlebars: { 377 | compile: { 378 | options: { 379 | <% if (includeRequireJS) { -%> 380 | amd: true, 381 | <% } -%> 382 | namespace: 'JST' 383 | }, 384 | files: { 385 | '.tmp/scripts/templates.js': ['<%%= yeoman.app %>/scripts/templates/*.hbs'] 386 | } 387 | } 388 | }, 389 | <% } else { -%> 390 | jst: { 391 | <% if (includeRequireJS) { -%> 392 | options: { 393 | amd: true 394 | }, 395 | <% } -%> 396 | compile: { 397 | files: { 398 | '.tmp/scripts/templates.js': ['<%%= yeoman.app %>/scripts/templates/*.ejs'] 399 | } 400 | } 401 | }, 402 | <% } -%> 403 | rev: { 404 | dist: { 405 | files: { 406 | src: [ 407 | '<%%= yeoman.dist %>/scripts/{,*/}*.js', 408 | '<%%= yeoman.dist %>/styles/{,*/}*.css', 409 | '<%%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp}', 410 | '<%%= yeoman.dist %>/styles/fonts/{,*/}*.*', 411 | <% if (sassBootstrap) { -%> 412 | 'bower_components/bootstrap-sass-official/assets/fonts/bootstrap/*.*' 413 | <% } -%> 414 | ] 415 | } 416 | } 417 | } 418 | }); 419 | 420 | grunt.registerTask('createDefaultTemplate', function () { 421 | grunt.file.write('.tmp/scripts/templates.js', 'this.JST = this.JST || {};'); 422 | }); 423 | 424 | grunt.registerTask('server', function (target) { 425 | grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.'); 426 | grunt.task.run(['serve' + (target ? ':' + target : '')]); 427 | }); 428 | 429 | grunt.registerTask('serve', function (target) { 430 | if (target === 'dist') { 431 | return grunt.task.run(['build', 'open:server', 'connect:dist:keepalive']); 432 | } 433 | 434 | if (target === 'test') { 435 | return grunt.task.run([ 436 | 'clean:server', 437 | <% if (hasCoffee) { -%> 438 | 'coffee', 439 | <% } -%> 440 | 'createDefaultTemplate', 441 | <% if (templateFramework === 'mustache' ) { -%> 442 | 'mustache', 443 | <% } else if (templateFramework === 'handlebars') { -%> 444 | 'handlebars', 445 | <% } else { -%> 446 | 'jst', 447 | <% } -%> 448 | <% if (sassBootstrap) { -%> 449 | 'sass:server', 450 | <% } -%> 451 | 'connect:test', 452 | 'open:test', 453 | 'watch' 454 | ]); 455 | } 456 | 457 | grunt.task.run([ 458 | 'clean:server', 459 | <% if (hasCoffee) { -%> 460 | 'coffee:dist', 461 | <% } -%> 462 | 'createDefaultTemplate', 463 | <% if (templateFramework === 'mustache') { -%> 464 | 'mustache', 465 | <% } else if (templateFramework === 'handlebars') { -%> 466 | 'handlebars', 467 | <% } else { -%> 468 | 'jst', 469 | <% } -%> 470 | <% if (sassBootstrap) { -%> 471 | 'sass:server', 472 | <% } -%> 473 | 'connect:livereload', 474 | 'open:server', 475 | 'watch' 476 | ]); 477 | }); 478 | 479 | grunt.registerTask('test', function (isConnected) { 480 | isConnected = Boolean(isConnected); 481 | var testTasks = [ 482 | 'clean:server', 483 | <% if (hasCoffee) { -%> 484 | 'coffee', 485 | <% } -%> 486 | 'createDefaultTemplate', 487 | <% if (templateFramework === 'mustache' ) { -%> 488 | 'mustache', 489 | <% } else if (templateFramework === 'handlebars') { -%> 490 | 'handlebars', 491 | <% } else { -%> 492 | 'jst', 493 | <% } -%> 494 | <% if (sassBootstrap) { -%> 495 | 'sass', 496 | <% } -%> 497 | <% if(testFramework === 'mocha') { -%> 498 | 'connect:test', 499 | 'mocha' 500 | <% } else { -%> 501 | 'jasmine' 502 | <% } -%> 503 | ]; 504 | 505 | if(!isConnected) { 506 | return grunt.task.run(testTasks); 507 | } else { 508 | // already connected so not going to connect again, remove the connect:test task 509 | testTasks.splice(testTasks.indexOf('connect:test'), 1); 510 | return grunt.task.run(testTasks); 511 | } 512 | }); 513 | 514 | grunt.registerTask('build', [ 515 | 'clean:dist', 516 | <% if (hasCoffee) { -%> 517 | 'coffee', 518 | <% } -%> 519 | 'createDefaultTemplate', 520 | <% if (templateFramework === 'mustache' ) { -%> 521 | 'mustache', 522 | <% } else if (templateFramework === 'handlebars') { -%> 523 | 'handlebars', 524 | <% } else { -%> 525 | 'jst', 526 | <% } -%> 527 | <% if (sassBootstrap) { -%> 528 | 'sass:dist', 529 | <% } -%> 530 | 'useminPrepare', 531 | 'imagemin', 532 | 'htmlmin', 533 | 'concat', 534 | 'cssmin', 535 | <% if (includeRequireJS) { -%> 536 | 'requirejs', 537 | <% } -%> 538 | 'uglify', 539 | 'copy', 540 | 'rev', 541 | 'usemin' 542 | ]); 543 | 544 | grunt.registerTask('default', [ 545 | 'jshint', 546 | 'test', 547 | 'build' 548 | ]); 549 | }; 550 | -------------------------------------------------------------------------------- /generators/app/templates/_bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= appParamCaseName %>", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | <% if (sassBootstrap) { -%> 6 | "bootstrap-sass-official": "~3.3.1", 7 | <% } -%> 8 | "jquery": "~2.1.0", 9 | "backbone": "~1.1.0", 10 | <% if (includeRequireJS) { -%> 11 | "requirejs": "~2.1.10", 12 | "requirejs-text": "~2.0.10", 13 | <% } if (includeModernizr) { -%> 14 | "modernizr": "~2.7.1", 15 | <% } if (templateFramework === 'handlebars') { -%> 16 | "handlebars": "~1.3.0", 17 | <% } -%> 18 | "lodash": "~2.4.1" 19 | }, 20 | "devDependencies": {} 21 | } 22 | -------------------------------------------------------------------------------- /generators/app/templates/_package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "devDependencies": { 4 | "apache-server-configs": "^2.8.0", 5 | "grunt": "^0.4.5", 6 | "grunt-contrib-copy": "^0.5.0", 7 | "grunt-contrib-concat": "^0.5.0", 8 | <% if (hasCoffee) { -%> 9 | "grunt-contrib-coffee": "^0.11.0", 10 | <% } -%> 11 | <% if (templateFramework === 'mustache') { -%> 12 | "grunt-mustache": "^0.1.7", 13 | <% } else if (templateFramework === 'handlebars') { -%> 14 | "grunt-contrib-handlebars": "^0.8.0", 15 | <% } else { -%> 16 | "grunt-contrib-jst": "^0.6.0", 17 | <% } -%> 18 | "grunt-contrib-uglify": "^0.6.0", 19 | "grunt-contrib-jshint": "^0.10.0", 20 | "grunt-contrib-cssmin": "^0.10.0", 21 | "grunt-contrib-connect": "^0.8.0", 22 | "grunt-contrib-clean": "^0.6.0", 23 | "grunt-contrib-htmlmin": "^0.3.0", 24 | "grunt-contrib-imagemin": "^0.9.2", 25 | "grunt-contrib-watch": "^0.6.1", 26 | <% if (testFramework === 'jasmine') { -%> 27 | "grunt-contrib-jasmine": "^0.9.2", 28 | <% }else{ -%> 29 | "grunt-mocha": "^0.4.11", 30 | <% } -%> 31 | "grunt-usemin": "^2.4.0", 32 | <% if(includeRequireJS){ -%> 33 | "grunt-bower-requirejs": "^1.1.0", 34 | "grunt-requirejs": "^0.4.2", 35 | <% } -%> 36 | "grunt-rev": "^0.1.0", 37 | "grunt-open": "^0.2.3", 38 | <% if (sassBootstrap) { -%> 39 | "grunt-sass": "^1.0.0", 40 | <% } -%> 41 | "connect-livereload": "^0.4.0", 42 | "jit-grunt": "^0.9.1", 43 | "time-grunt": "^1.0.0", 44 | "jshint-stylish": "^1.0.0" 45 | }, 46 | "engines": { 47 | "node": ">=0.10.0" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /generators/app/templates/app.coffee: -------------------------------------------------------------------------------- 1 | window.<%= appPascalCaseName %> = 2 | Models: {} 3 | Collections: {} 4 | Views: {} 5 | Routers: {} 6 | init: -> 7 | 'use strict' 8 | console.log 'Hello from Backbone!' 9 | 10 | $ -> 11 | 'use strict' 12 | <%= appPascalCaseName %>.init(); 13 | -------------------------------------------------------------------------------- /generators/app/templates/app.js: -------------------------------------------------------------------------------- 1 | /*global <%= appPascalCaseName %>, $*/ 2 | 3 | 4 | window.<%= appPascalCaseName %> = { 5 | Models: {}, 6 | Collections: {}, 7 | Views: {}, 8 | Routers: {}, 9 | init: function () { 10 | 'use strict'; 11 | console.log('Hello from Backbone!'); 12 | } 13 | }; 14 | 15 | $(document).ready(function () { 16 | 'use strict'; 17 | <%= appPascalCaseName %>.init(); 18 | }); 19 | -------------------------------------------------------------------------------- /generators/app/templates/app/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |Sorry, but the page you were trying to view does not exist.
141 |It looks like this was the result of either:
142 |HTML5 Boilerplate is a professional front-end template for building fast, robust, and adaptable web apps or sites.
53 | 54 |Sleek, intuitive, and powerful mobile first front-end framework for faster and easier web development.
56 | 57 | <% if (includeModernizr) { -%> 58 |Modernizr is an open-source JavaScript library that helps you build the next generation of HTML5 and CSS3-powered websites.
60 | <% } -%> 61 |Backbone.js gives structure to web applications by providing models, collections, views and connects it all to your existing API over a RESTful JSON interface.
65 | 66 |A utility library delivering consistency, customization, performance, & extras.
68 | 69 | <% if (includeRequireJS) { -%> 70 |RequireJS is a JavaScript file and module loader. It is optimized for in-browser use, but it can be used in other JavaScript environments, like Rhino and Node.
72 | <% } -%> 73 |You now have
85 |Your content here.
2 | 3 | -------------------------------------------------------------------------------- /generators/templates/collection.js: -------------------------------------------------------------------------------- 1 | /*global <%= appClassName %>, Backbone*/ 2 | 3 | <%= appClassName %>.Collections = <%= appClassName %>.Collections || {}; 4 | 5 | (function () { 6 | 'use strict'; 7 | 8 | <%= appClassName %>.Collections.<%= className %> = Backbone.Collection.extend({ 9 | 10 | model: <%= appClassName %>.Models.<%= className %> 11 | 12 | }); 13 | 14 | })(); 15 | -------------------------------------------------------------------------------- /generators/templates/model.js: -------------------------------------------------------------------------------- 1 | /*global <%= appClassName %>, Backbone*/ 2 | 3 | <%= appClassName %>.Models = <%= appClassName %>.Models || {}; 4 | 5 | (function () { 6 | 'use strict'; 7 | 8 | <%= appClassName %>.Models.<%= className %> = Backbone.Model.extend({ 9 | 10 | url: '', 11 | 12 | initialize: function() { 13 | }, 14 | 15 | defaults: { 16 | }, 17 | 18 | validate: function(attrs, options) { 19 | }, 20 | 21 | parse: function(response, options) { 22 | return response; 23 | } 24 | }); 25 | 26 | })(); 27 | -------------------------------------------------------------------------------- /generators/templates/requirejs/collection.js: -------------------------------------------------------------------------------- 1 | /*global define*/ 2 | 3 | define([ 4 | 'underscore', 5 | 'backbone', 6 | 'models/<%= className %>' 7 | ], function (_, Backbone, <%= className %>Model) { 8 | 'use strict'; 9 | 10 | var <%= className %>Collection = Backbone.Collection.extend({ 11 | model: <%= className %>Model 12 | }); 13 | 14 | return <%= className %>Collection; 15 | }); 16 | -------------------------------------------------------------------------------- /generators/templates/requirejs/model.js: -------------------------------------------------------------------------------- 1 | /*global define*/ 2 | 3 | define([ 4 | 'underscore', 5 | 'backbone' 6 | ], function (_, Backbone) { 7 | 'use strict'; 8 | 9 | var <%= className %>Model = Backbone.Model.extend({ 10 | url: '', 11 | 12 | initialize: function() { 13 | }, 14 | 15 | defaults: { 16 | }, 17 | 18 | validate: function(attrs, options) { 19 | }, 20 | 21 | parse: function(response, options) { 22 | return response; 23 | } 24 | }); 25 | 26 | return <%= className %>Model; 27 | }); 28 | -------------------------------------------------------------------------------- /generators/templates/requirejs/router.js: -------------------------------------------------------------------------------- 1 | /*global define*/ 2 | 3 | define([ 4 | 'jquery', 5 | 'backbone' 6 | ], function ($, Backbone) { 7 | 'use strict'; 8 | 9 | var <%= className %>Router = Backbone.Router.extend({ 10 | routes: { 11 | } 12 | 13 | }); 14 | 15 | return <%= className %>Router; 16 | }); 17 | -------------------------------------------------------------------------------- /generators/templates/requirejs/view.js: -------------------------------------------------------------------------------- 1 | /*global define*/ 2 | 3 | define([ 4 | 'jquery', 5 | 'underscore', 6 | 'backbone', 7 | 'templates' 8 | ], function ($, _, Backbone, JST) { 9 | 'use strict'; 10 | 11 | var <%= className %>View = Backbone.View.extend({ 12 | template: JST['<%= jst_path %>'], 13 | 14 | tagName: 'div', 15 | 16 | id: '', 17 | 18 | className: '', 19 | 20 | events: {}, 21 | 22 | initialize: function () { 23 | this.listenTo(this.model, 'change', this.render); 24 | }, 25 | 26 | render: function () { 27 | this.$el.html(this.template(this.model.toJSON())); 28 | } 29 | }); 30 | 31 | return <%= className %>View; 32 | }); 33 | -------------------------------------------------------------------------------- /generators/templates/router.js: -------------------------------------------------------------------------------- 1 | /*global <%= appClassName %>, Backbone*/ 2 | 3 | <%= appClassName %>.Routers = <%= appClassName %>.Routers || {}; 4 | 5 | (function () { 6 | 'use strict'; 7 | 8 | <%= appClassName %>.Routers.<%= className %> = Backbone.Router.extend({ 9 | 10 | }); 11 | 12 | })(); 13 | -------------------------------------------------------------------------------- /generators/templates/view.ejs: -------------------------------------------------------------------------------- 1 |Your content here.
2 | 3 | -------------------------------------------------------------------------------- /generators/templates/view.js: -------------------------------------------------------------------------------- 1 | /*global <%= appClassName %>, Backbone, JST*/ 2 | 3 | <%= appClassName %>.Views = <%= appClassName %>.Views || {}; 4 | 5 | (function () { 6 | 'use strict'; 7 | 8 | <%= appClassName %>.Views.<%= className %> = Backbone.View.extend({ 9 | 10 | template: JST['<%= jst_path %>'], 11 | 12 | tagName: 'div', 13 | 14 | id: '', 15 | 16 | className: '', 17 | 18 | events: {}, 19 | 20 | initialize: function () { 21 | this.listenTo(this.model, 'change', this.render); 22 | }, 23 | 24 | render: function () { 25 | this.$el.html(this.template(this.model.toJSON())); 26 | } 27 | 28 | }); 29 | 30 | })(); 31 | -------------------------------------------------------------------------------- /generators/view/index.js: -------------------------------------------------------------------------------- 1 | /*jshint latedef:false */ 2 | var path = require('path'); 3 | var util = require('util'); 4 | var pascalCase = require('pascal-case'); 5 | var yeoman = require('yeoman-generator'); 6 | var scriptBase = require('../../script-base'); 7 | 8 | var ViewGenerator = scriptBase.extend({ 9 | constructor: function () { 10 | scriptBase.apply(this, arguments); 11 | 12 | var dirPath = this.options.coffee ? '../templates/coffeescript/' : '../templates'; 13 | this.sourceRoot(path.join(__dirname, dirPath)); 14 | }, 15 | 16 | writing: { 17 | createViewFiles: function () { 18 | var templateFramework = this.config.get('templateFramework') || 'lodash'; 19 | var templateExt = '.ejs'; 20 | if (templateFramework === 'mustache') { 21 | templateExt = '-template.mustache'; 22 | } else if (templateFramework === 'handlebars') { 23 | templateExt = '.hbs'; 24 | } 25 | this.jst_path = this.env.options.appPath + '/scripts/templates/' + this.name + templateExt; 26 | 27 | this.template('view.ejs', this.jst_path); 28 | if (templateFramework === 'mustache') { 29 | this.jst_path = this.name + '-template'; 30 | } 31 | 32 | this._writeTemplate( 33 | 'view', 34 | path.join(this.env.options.appPath + '/scripts/views', this.name), 35 | { 36 | appClassName: pascalCase(this.appname), 37 | className: pascalCase(this.name), 38 | 'jst_path': this.jst_path 39 | } 40 | ); 41 | 42 | if (!this.options.requirejs) { 43 | this._addScriptToIndex('views/' + this.name); 44 | } 45 | }, 46 | 47 | composeTest: function () { 48 | this._generateTest('view'); 49 | } 50 | } 51 | }); 52 | 53 | module.exports = ViewGenerator; 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-backbone", 3 | "version": "0.4.2", 4 | "description": "Scaffold out a Backbone.js project", 5 | "keywords": [ 6 | "yeoman-generator", 7 | "scaffold", 8 | "framework", 9 | "mvc", 10 | "backbone" 11 | ], 12 | "author": "Yeoman", 13 | "main": "generators/app/index.js", 14 | "repository": "yeoman/generator-backbone", 15 | "scripts": { 16 | "test": "mocha --reporter spec" 17 | }, 18 | "dependencies": { 19 | "ejs": "^2.3.0", 20 | "html-wiring": "^1.0.0", 21 | "mkdirp": "^0.5.1", 22 | "param-case": "^1.1.0", 23 | "pascal-case": "^1.1.0", 24 | "yeoman-generator": "^0.21.1" 25 | }, 26 | "peerDependencies": { 27 | "generator-mocha": ">=0.1.3", 28 | "generator-backbone-mocha": ">=0.0.2" 29 | }, 30 | "devDependencies": { 31 | "mocha": "*" 32 | }, 33 | "engines": { 34 | "node": ">=0.10.0" 35 | }, 36 | "license": "BSD", 37 | "files": [ 38 | "generators/all", 39 | "generators/app", 40 | "generators/collection", 41 | "generators/model", 42 | "generators/router", 43 | "generators/templates", 44 | "generators/view", 45 | "script-base.js", 46 | "util.js" 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Backbone.js generator [](http://travis-ci.org/yeoman/generator-backbone) [](https://coveralls.io/r/yeoman/generator-backbone?branch=master) 2 | 3 | A Backbone generator for Yeoman that provides a functional boilerplate Backbone app out of the box. You also get access to a number of sub-generators which can be used to easily create individual models, views, collections and so on. 4 | 5 | Optional RequireJS (AMD) support has recently been added as a prompt when using the generator on new projects. 6 | 7 | 8 | ## Usage 9 | 10 | Install: `npm install -g generator-backbone` 11 | 12 | Make a new directory and `cd` into it: 13 | ``` 14 | mkdir my-new-project && cd $_ 15 | ``` 16 | 17 | Run `yo backbone`, optionally passing an app name: 18 | ``` 19 | yo backbone [app-name] 20 | ``` 21 | 22 | ## Generators 23 | 24 | Available generators: 25 | 26 | - backbone:model 27 | - backbone:view 28 | - backbone:collection 29 | - backbone:router 30 | - backbone:all 31 | 32 | ## Typical workflow 33 | 34 | ``` 35 | yo backbone # generates your application base and build workflow 36 | yo backbone:model blog 37 | yo backbone:collection blog 38 | yo backbone:router blog 39 | yo backbone:view blog 40 | grunt serve 41 | ``` 42 | 43 | Also checkout this [NetTuts write-up](http://net.tutsplus.com/tutorials/javascript-ajax/building-apps-with-the-yeoman-workflow/) for a guide to building Backbone.js apps using this generator. 44 | 45 | 46 | ## Options 47 | 48 | * `--appPath` 49 | 50 | Generate scaffold into a custom directory. 51 | 52 | * `--coffee` 53 | 54 | Generate scaffolds in CoffeeScript. By default check if project uses CoffeeScript. 55 | 56 | * `--requirejs` 57 | 58 | Generate scaffolds using RequireJS (AMD) Loader. By default check if project uses RequireJS. 59 | 60 | * `--skip-install` 61 | 62 | Skips the automatic execution of `bower` and `npm` after 63 | scaffolding has finished. 64 | 65 | * `--test-framework=[framework]` 66 | 67 | Defaults to `mocha`. Can be switched for 68 | another supported testing framework like `jasmine`. 69 | 70 | * `--template-framework=[framework]` 71 | 72 | Defaults to `lodash` templating with grunt-contrib-jst. 73 | `handlebars` and `mustache` are also supported. 74 | 75 | ## A note regarding JST templates and strict mode 76 | 77 | If you use strict mode in your app and JST templates the default grunt-jst implementation will cause your app to error out as the templates will be precompiled using a 'with' statement. 78 | 79 | This can be addressed by changing the jst grunt task as follows: 80 | 81 | ``` 82 | jst: { 83 | compile: { 84 | options: 85 | { 86 | templateSettings: 87 | { 88 | variable: 'data' 89 | } 90 | }, 91 | files: { 92 | '.tmp/scripts/templates.js': ['<%= yeoman.app %>/scripts/templates/*.ejs'] 93 | } 94 | } 95 | }, 96 | ``` 97 | A result of this change is that your template variable definitions must also be updated from `<%= templateVariable %>` to `<%= data.templateVariable %>`. More information on this can be found in the [Underscore documentation](http://underscorejs.org/#template). 98 | 99 | ## Contribute 100 | 101 | See the [contributing docs](https://github.com/yeoman/yeoman/blob/master/contributing.md) 102 | 103 | When submitting an issue, please follow the [guidelines](https://github.com/yeoman/yeoman/blob/master/contributing.md#issue-submission). Especially important is to make sure Yeoman is up-to-date, and providing the command or commands that cause the issue. 104 | 105 | When submitting a bugfix, write a test that exposes the bug and fails before applying your fix. Submit the test alongside the fix. 106 | 107 | When submitting a new feature, add tests that cover the feature. 108 | 109 | 110 | ## License 111 | 112 | [BSD license](http://opensource.org/licenses/bsd-license.php) 113 | -------------------------------------------------------------------------------- /script-base.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var util = require('util'); 4 | var yeoman = require('yeoman-generator'); 5 | var backboneUtils = require('./util.js'); 6 | 7 | var ScriptBase = yeoman.generators.NamedBase.extend({ 8 | constructor: function (name) { 9 | yeoman.generators.NamedBase.apply(this, arguments); 10 | 11 | this.appname = this.config.get('appName') || path.basename(process.cwd()); 12 | this.env.options.appPath = this.config.get('appPath') || 'app'; 13 | 14 | this.options.coffee = this.config.get('coffee') || false; 15 | 16 | if (typeof this.env.options.coffee === 'undefined') { 17 | this.env.options.coffee = this.options.coffee; 18 | } 19 | 20 | // check if --requirejs option provided or if require is setup 21 | if (typeof this.env.options.requirejs === 'undefined') { 22 | this.option('requirejs'); 23 | 24 | this.options.requirejs = this.config.get('includeRequireJS') || false; 25 | 26 | this.env.options.requirejs = this.options.requirejs; 27 | } 28 | 29 | }, 30 | 31 | _addScriptToIndex: function (script) { 32 | try { 33 | var appPath = this.env.options.appPath; 34 | var fullPath = this.destinationPath(path.join(appPath, 'index.html')); 35 | 36 | backboneUtils.rewriteFile({ 37 | file: fullPath, 38 | needle: '', 39 | splicable: [ 40 | '' 41 | ] 42 | }); 43 | } catch (e) { 44 | this.log('\n Unable to find ' + fullPath + '. Reference to ' + script + '.js ' + 'not added.\n'); 45 | } 46 | }, 47 | 48 | _setupSourceRootAndSuffix: function () { 49 | var sourceRoot = '/generators/templates'; 50 | this.scriptSuffix = '.js'; 51 | 52 | if (this.env.options.coffee || this.options.coffee) { 53 | sourceRoot = '/generators/templates/coffeescript'; 54 | this.scriptSuffix = '.coffee'; 55 | } 56 | 57 | if (this.env.options.requirejs || this.options.requirejs) { 58 | sourceRoot += '/requirejs'; 59 | } 60 | 61 | this.sourceRoot(path.join(__dirname, sourceRoot)); 62 | }, 63 | 64 | _writeTemplate: function (source, destination, data) { 65 | 66 | this._setupSourceRootAndSuffix(); 67 | var ext = this.scriptSuffix; 68 | this.fs.copyTpl( 69 | this.templatePath(source + ext), 70 | this.destinationPath(destination + ext), 71 | data 72 | ); 73 | }, 74 | 75 | _canGenerateTests: function () { 76 | return this.config.get('testFramework') === 'mocha' && !this.config.get('includeRequireJS'); 77 | }, 78 | 79 | _generateTest: function (type) { 80 | if (this._canGenerateTests()) { 81 | this.composeWith('backbone-mocha:' + type, { arguments: [this.name] }, { 82 | coffee: this.config.get('coffee'), 83 | ui: this.config.get('ui') 84 | }); 85 | } 86 | } 87 | }); 88 | 89 | module.exports = ScriptBase; 90 | -------------------------------------------------------------------------------- /test/helper.js: -------------------------------------------------------------------------------- 1 | var yeoman = require('yeoman-generator'); 2 | var helpers = yeoman.test; 3 | var path = require('path'); 4 | var fs = require('fs'); 5 | 6 | exports.createSubGenerator = function (config, type, asserts, deps) { 7 | deps = deps || []; 8 | helpers.run(path.join(__dirname, '../generators/' + type)) 9 | .inDir(path.join(__dirname, 'temp'), function () { 10 | fs.writeFileSync('.yo-rc.json', config); 11 | }) 12 | .withArguments(['foo']) 13 | .withGenerators(deps) 14 | .on('end', asserts); 15 | }; 16 | 17 | exports.createAppGenerator = function (config, prompts, done) { 18 | 19 | prompts = prompts || { features: ['sassBootstrap']}; 20 | 21 | var options = { skipInstall: true }; 22 | 23 | var deps = [ 24 | [helpers.createDummyGenerator(), 'mocha:app'] 25 | ]; 26 | helpers.run(path.join(__dirname, '../generators/app')) 27 | .inDir(path.join(__dirname, 'temp'), function () { 28 | fs.writeFileSync('.yo-rc.json', config); 29 | }) 30 | .withPrompts(prompts) 31 | .withOptions(options) 32 | .withGenerators(deps) 33 | .on('end', done); 34 | }; 35 | -------------------------------------------------------------------------------- /test/test-apppath.js: -------------------------------------------------------------------------------- 1 | /*global describe:true, beforeEach:true, it:true */ 2 | 'use strict'; 3 | var path = require('path'); 4 | var yeoman = require('yeoman-generator'); 5 | var helpers = yeoman.test; 6 | var assert = yeoman.assert; 7 | var fs = require('fs'); 8 | var test = require('./helper.js'); 9 | 10 | var config = [ 11 | '{', 12 | ' "generator-backbone": {', 13 | ' "appPath": "public",', 14 | ' "appName": "Temp"', 15 | ' }', 16 | '}' 17 | ].join('\n'); 18 | 19 | describe('backbone generator with appPath option', function () { 20 | beforeEach(function (done) { 21 | var deps = [ 22 | [helpers.createDummyGenerator(), 'mocha:app'] 23 | ]; 24 | helpers.run(path.join(__dirname, '../generators/app')) 25 | .inTmpDir(function () { 26 | fs.writeFileSync('.yo-rc.json', config); 27 | }) 28 | .withArguments(['temp']) 29 | .withOptions({skipInstall: true, appPath: 'public'}) 30 | .withPrompts({features: ['sassBootstrap']}) 31 | .withGenerators(deps) 32 | .on('end', done); 33 | 34 | }); 35 | 36 | describe('create expected files', function () { 37 | it('in path specified by --appPath', function () { 38 | var expectedContent = [ 39 | ['bower.json', /"name": "temp"/], 40 | ['Gruntfile.js', /app: 'public'/] 41 | ]; 42 | 43 | var expected = [ 44 | 'public/404.html', 45 | 'public/favicon.ico', 46 | 'public/robots.txt', 47 | 'public/index.html', 48 | '.gitignore', 49 | '.gitattributes', 50 | '.bowerrc', 51 | '.jshintrc', 52 | '.editorconfig', 53 | '.yo-rc.json', 54 | 'public/scripts/main.js', 55 | 'public/styles/main.scss' 56 | ]; 57 | 58 | assert.file(expected); 59 | assert.fileContent(expectedContent); 60 | }); 61 | }); 62 | 63 | describe('creates model', function () { 64 | it('without failure', function (done) { 65 | test.createSubGenerator(config, 'model', function () { 66 | assert.fileContent( 67 | 'public/scripts/models/foo.js', /Models.Foo = Backbone.Model.extend\(\{/ 68 | ); 69 | done(); 70 | }); 71 | }); 72 | }); 73 | 74 | describe('creates collection', function () { 75 | it('without failure', function (done) { 76 | test.createSubGenerator(config, 'collection', function () { 77 | assert.fileContent( 78 | 'public/scripts/collections/foo.js', /Collections.Foo = Backbone.Collection.extend\(\{/ 79 | ); 80 | done(); 81 | }); 82 | }); 83 | }); 84 | 85 | describe('creates router', function () { 86 | it('without failure', function (done) { 87 | test.createSubGenerator(config, 'router', function () { 88 | assert.fileContent( 89 | 'public/scripts/routes/foo.js', /Routers.Foo = Backbone.Router.extend\(\{/ 90 | ); 91 | done(); 92 | }); 93 | }); 94 | }); 95 | 96 | describe('creates view', function () { 97 | it('without failure', function (done) { 98 | 99 | test.createSubGenerator(config, 'view', function () { 100 | assert.fileContent( 101 | 'public/scripts/views/foo.js', /Views.Foo = Backbone.View.extend\(\{(.|\n)*public\/scripts\/templates\/foo.ejs/ 102 | ); 103 | assert.file('public/scripts/templates/foo.ejs'); 104 | done(); 105 | }); 106 | }); 107 | }); 108 | 109 | }); 110 | -------------------------------------------------------------------------------- /test/test-coffee-requirejs.js: -------------------------------------------------------------------------------- 1 | /*global describe:true, beforeEach:true, it:true */ 2 | 'use strict'; 3 | var path = require('path'); 4 | var yeoman = require('yeoman-generator'); 5 | var helpers = yeoman.test; 6 | var assert = yeoman.assert; 7 | var fs = require('fs'); 8 | var test = require('./helper.js'); 9 | 10 | var config = [ 11 | '{', 12 | ' "generator-backbone": {', 13 | ' "appPath": "app",', 14 | ' "appName": "Temp",', 15 | ' "coffee": "true",', 16 | ' "includeRequireJS": "true"', 17 | ' }', 18 | '}' 19 | ].join('\n'); 20 | 21 | describe('Backbone generator test with --coffee and --requirejs option', function () { 22 | beforeEach(function (done) { 23 | 24 | var prompts = { 25 | features: ['sassBootstrap', 'coffee', 'requirejs'] 26 | }; 27 | 28 | test.createAppGenerator(config, prompts, done); 29 | }); 30 | 31 | it('creates expected files', function () { 32 | var expectedContent = [ 33 | ['bower.json', /("name": "temp")(|.|\n)*(requirejs)/], 34 | ['app/index.html', /(Bootstrap)(|.|\n)*(RequireJS)/i] 35 | ]; 36 | var expected = [ 37 | 'Gruntfile.js', 38 | 'app/404.html', 39 | 'app/favicon.ico', 40 | 'app/robots.txt', 41 | '.gitignore', 42 | '.gitattributes', 43 | '.bowerrc', 44 | '.jshintrc', 45 | '.editorconfig', 46 | '.yo-rc.json', 47 | 'app/scripts/main.coffee' 48 | ]; 49 | 50 | assert.file(expected); 51 | assert.fileContent(expectedContent); 52 | 53 | }); 54 | 55 | describe('creates model in coffeescript with rjs', function () { 56 | it('without failure', function (done) { 57 | 58 | test.createSubGenerator(config, 'model', function () { 59 | assert.fileContent( 60 | 'app/scripts/models/foo.coffee', /class FooModel extends Backbone.Model/ 61 | ); 62 | done(); 63 | }); 64 | }); 65 | }); 66 | 67 | describe('creates collection in coffeescript with rjs', function () { 68 | it('without failure', function (done) { 69 | 70 | test.createSubGenerator(config, 'collection', function () { 71 | assert.fileContent( 72 | 'app/scripts/collections/foo.coffee', /class FooCollection extends Backbone.Collection/ 73 | ); 74 | done(); 75 | }); 76 | }); 77 | }); 78 | 79 | describe('creates router in coffeescript with rjs', function () { 80 | it('without failure', function (done) { 81 | 82 | test.createSubGenerator(config, 'router', function () { 83 | assert.fileContent( 84 | 'app/scripts/routes/foo.coffee', /class FooRouter extends Backbone.Router/ 85 | ); 86 | done(); 87 | }); 88 | 89 | }); 90 | }); 91 | 92 | describe('creates view in coffeescript with rjs', function () { 93 | it('without failure', function (done) { 94 | 95 | test.createSubGenerator(config, 'view', function () { 96 | assert.fileContent( 97 | 'app/scripts/views/foo.coffee', /class FooView extends Backbone.View(.|\n)*app\/scripts\/templates\/foo.ejs/ 98 | ); 99 | assert.file('app/scripts/templates/foo.ejs'); 100 | done(); 101 | }); 102 | 103 | }); 104 | }); 105 | 106 | }); 107 | -------------------------------------------------------------------------------- /test/test-coffee.js: -------------------------------------------------------------------------------- 1 | /*global describe:true, beforeEach:true, it:true */ 2 | 'use strict'; 3 | var path = require('path'); 4 | var yeoman = require('yeoman-generator'); 5 | var helpers = yeoman.test; 6 | var assert = yeoman.assert; 7 | var fs = require('fs'); 8 | var test = require('./helper.js'); 9 | 10 | var config = [ 11 | '{', 12 | ' "generator-backbone": {', 13 | ' "appPath": "app",', 14 | ' "appName": "Temp",', 15 | ' "coffee": "true"', 16 | ' }', 17 | '}' 18 | ].join('\n'); 19 | 20 | describe('Backbone generator test with --coffee option', function () { 21 | beforeEach(function (done) { 22 | var prompts = { 23 | features: ['sassBootstrap', 'coffee'] 24 | }; 25 | 26 | test.createAppGenerator(config, prompts, done); 27 | }); 28 | 29 | it('creates expected files', function () { 30 | var expectedContent = [ 31 | ['bower.json', /"name": "temp"/] 32 | ]; 33 | var expected = [ 34 | 'Gruntfile.js', 35 | 'app/404.html', 36 | 'app/favicon.ico', 37 | 'app/robots.txt', 38 | 'app/index.html', 39 | '.gitignore', 40 | '.gitattributes', 41 | '.bowerrc', 42 | '.jshintrc', 43 | '.editorconfig', 44 | '.yo-rc.json', 45 | 'app/scripts/main.coffee' 46 | ]; 47 | 48 | assert.file(expected); 49 | assert.fileContent(expectedContent); 50 | 51 | }); 52 | 53 | describe('creates model in coffeescript', function () { 54 | it('without failure', function (done) { 55 | 56 | test.createSubGenerator(config, 'model', function () { 57 | assert.fileContent( 58 | 'app/scripts/models/foo.coffee', /class Temp.Models.Foo extends Backbone.Model/ 59 | ); 60 | done(); 61 | }); 62 | }); 63 | }); 64 | 65 | describe('creates collection in coffeescript', function () { 66 | it('without failure', function (done) { 67 | 68 | test.createSubGenerator(config, 'collection', function () { 69 | assert.fileContent( 70 | 'app/scripts/collections/foo.coffee', /class Temp.Collections.Foo extends Backbone.Collection/ 71 | ); 72 | done(); 73 | }); 74 | }); 75 | }); 76 | 77 | describe('creates router in coffeescript', function () { 78 | it('without failure', function (done) { 79 | 80 | test.createSubGenerator(config, 'router', function () { 81 | assert.fileContent( 82 | 'app/scripts/routes/foo.coffee', /class Temp.Routers.Foo extends Backbone.Router/ 83 | ); 84 | done(); 85 | }); 86 | }); 87 | }); 88 | 89 | describe('creates view in coffeescript', function () { 90 | it('without failure', function (done) { 91 | 92 | test.createSubGenerator(config, 'view', function () { 93 | assert.fileContent( 94 | 'app/scripts/views/foo.coffee', /class Temp.Views.Foo extends Backbone.View/ 95 | ); 96 | assert.file('app/scripts/templates/foo.ejs'); 97 | done(); 98 | }); 99 | }); 100 | }); 101 | }); 102 | -------------------------------------------------------------------------------- /test/test-foo.js: -------------------------------------------------------------------------------- 1 | /*global describe:true, beforeEach:true, it:true */ 2 | 'use strict'; 3 | var path = require('path'); 4 | var yeoman = require('yeoman-generator'); 5 | var helpers = yeoman.test; 6 | var assert = yeoman.assert; 7 | var fs = require('fs'); 8 | var test = require('./helper.js'); 9 | 10 | var config = [ 11 | '{', 12 | ' "generator-backbone": {', 13 | ' "appPath": "app",', 14 | ' "appName": "Temp"', 15 | ' }', 16 | '}' 17 | ].join('\n'); 18 | 19 | describe('Backbone generator test', function () { 20 | beforeEach(function (done) { 21 | var prompts = { 22 | features: ['sassBootstrap'] 23 | }; 24 | 25 | test.createAppGenerator(config, prompts, done); 26 | }); 27 | 28 | it('every generator can be required without throwing', function () { 29 | // not testing the actual run of generators yet 30 | this.all = require('../generators/all'); 31 | this.app = require('../generators/app'); 32 | this.collection = require('../generators/collection'); 33 | this.model = require('../generators/model'); 34 | this.router = require('../generators/router'); 35 | this.view = require('../generators/view'); 36 | }); 37 | 38 | describe('create expected files', function () { 39 | it('in path /app', function () { 40 | var expectedContent = [ 41 | ['bower.json', /"name": "temp"/] 42 | ]; 43 | var expected = [ 44 | 'Gruntfile.js', 45 | 'app/404.html', 46 | 'app/favicon.ico', 47 | 'app/robots.txt', 48 | 'app/index.html', 49 | '.gitignore', 50 | '.gitattributes', 51 | '.bowerrc', 52 | '.jshintrc', 53 | '.editorconfig', 54 | '.yo-rc.json', 55 | 'app/scripts/main.js', 56 | 'app/styles/main.scss' 57 | ]; 58 | 59 | assert.file(expected); 60 | assert.fileContent(expectedContent); 61 | }); 62 | }); 63 | 64 | describe('creates backbone model', function () { 65 | it('without failure', function (done) { 66 | 67 | test.createSubGenerator(config, 'model', function () { 68 | assert.fileContent( 69 | 'app/scripts/models/foo.js', /Models.Foo = Backbone.Model.extend\(\{/ 70 | ); 71 | done(); 72 | }); 73 | }); 74 | }); 75 | 76 | describe('creates backbone collection', function () { 77 | it('without failure', function (done) { 78 | 79 | test.createSubGenerator(config, 'collection', function () { 80 | assert.fileContent( 81 | 'app/scripts/collections/foo.js', /Collections.Foo = Backbone.Collection.extend\(\{/ 82 | ); 83 | done(); 84 | }); 85 | }); 86 | }); 87 | 88 | describe('creates backbone router', function () { 89 | it('without failure', function (done) { 90 | 91 | test.createSubGenerator(config, 'router', function () { 92 | assert.fileContent( 93 | 'app/scripts/routes/foo.js', /Routers.Foo = Backbone.Router.extend\(\{/ 94 | ); 95 | done(); 96 | }); 97 | }); 98 | }); 99 | 100 | describe('creates backbone view', function () { 101 | it('without failure', function (done) { 102 | 103 | test.createSubGenerator(config, 'view', function () { 104 | assert.fileContent( 105 | 'app/scripts/views/foo.js', /Views.Foo = Backbone.View.extend\(\{(.|\n)*app\/scripts\/templates\/foo.ejs/ 106 | ); 107 | assert.file('app/scripts/templates/foo.ejs'); 108 | done(); 109 | }); 110 | }); 111 | }); 112 | 113 | describe('creates backbone all', function () { 114 | it('without failure', function (done) { 115 | 116 | // mocks composed generators to test for their execution 117 | var hasBeenCalled = {}; 118 | var depNames = [ 119 | 'backbone:model', 120 | 'backbone:collection', 121 | 'backbone:router', 122 | 'backbone:view' 123 | ]; 124 | var deps = depNames.map(function (name) { 125 | return [ 126 | yeoman.Base.extend({ 127 | exec: function () { 128 | hasBeenCalled[name] = true; 129 | testDepsBeenCalled(); 130 | } 131 | }), 132 | name 133 | ]; 134 | }); 135 | 136 | // tests that all dependencies have been called, will timeout otherwise 137 | var testDepsBeenCalled = function () { 138 | var allDepsCalled = depNames.every(function (name) { 139 | return hasBeenCalled[name]; 140 | }); 141 | if (allDepsCalled) { 142 | assert(true); 143 | done(); 144 | } 145 | }; 146 | 147 | // runs backbone:all generator 148 | test.createSubGenerator(config, 'all', testDepsBeenCalled, deps); 149 | }); 150 | }); 151 | 152 | 153 | }); 154 | -------------------------------------------------------------------------------- /test/test-handlebars.js: -------------------------------------------------------------------------------- 1 | /*global describe:true, beforeEach:true, it:true */ 2 | 'use strict'; 3 | var path = require('path'); 4 | var yeoman = require('yeoman-generator'); 5 | var helpers = yeoman.test; 6 | var assert = yeoman.assert; 7 | var fs = require('fs'); 8 | var test = require('./helper.js'); 9 | 10 | var config = [ 11 | '{', 12 | ' "generator-backbone": {', 13 | ' "appPath": "app",', 14 | ' "appName": "Temp",', 15 | ' "templateFramework": "handlebars"', 16 | ' }', 17 | '}' 18 | ].join('\n'); 19 | 20 | describe('Backbone generator with handlebars', function () { 21 | 22 | describe('creates backbone view', function () { 23 | it('without failure', function (done) { 24 | 25 | test.createSubGenerator(config, 'view', function () { 26 | assert.fileContent( 27 | 'app/scripts/views/foo.js', /Views.Foo = Backbone.View.extend\(\{(.|\n)*app\/scripts\/templates\/foo.hbs/ 28 | ); 29 | assert.file('app/scripts/templates/foo.hbs'); 30 | done(); 31 | }); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /test/test-mustache.js: -------------------------------------------------------------------------------- 1 | /*global describe:true, beforeEach:true, it:true */ 2 | 'use strict'; 3 | var path = require('path'); 4 | var yeoman = require('yeoman-generator'); 5 | var helpers = yeoman.test; 6 | var assert = yeoman.assert; 7 | var fs = require('fs'); 8 | var test = require('./helper.js'); 9 | 10 | var config = [ 11 | '{', 12 | ' "generator-backbone": {', 13 | ' "appPath": "app",', 14 | ' "appName": "Temp",', 15 | ' "templateFramework": "mustache"', 16 | ' }', 17 | '}' 18 | ].join('\n'); 19 | 20 | describe('Backbone generator with mustache', function () { 21 | 22 | it('creates backbone view', function (done) { 23 | 24 | test.createSubGenerator(config, 'view', function () { 25 | assert.fileContent( 26 | 'app/scripts/views/foo.js', /Views.Foo = Backbone.View.extend\(\{(.|\n)*foo-template/ 27 | ); 28 | assert.file('app/scripts/templates/foo-template.mustache'); 29 | done(); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /test/test-requirejs.js: -------------------------------------------------------------------------------- 1 | /*global describe:true, beforeEach:true, it:true */ 2 | 'use strict'; 3 | var path = require('path'); 4 | var yeoman = require('yeoman-generator'); 5 | var helpers = yeoman.test; 6 | var assert = yeoman.assert; 7 | var fs = require('fs'); 8 | var test = require('./helper.js'); 9 | 10 | var config = [ 11 | '{', 12 | ' "generator-backbone": {', 13 | ' "appPath": "app",', 14 | ' "appName": "Temp",', 15 | ' "includeRequireJS": "true"', 16 | ' }', 17 | '}' 18 | ].join('\n'); 19 | 20 | describe('Backbone generator with RequireJS', function () { 21 | 22 | beforeEach(function (done) { 23 | var prompts = { 24 | features: ['sassBootstrap', 'requirejs'] 25 | }; 26 | test.createAppGenerator(config, prompts, done); 27 | }); 28 | 29 | describe('creates expected files', function () { 30 | it('with sassBootstrap', function () { 31 | var expectedContent = [ 32 | ['bower.json', /("name": "temp")(|.|\n)*(requirejs)/], 33 | ['app/index.html', /(Bootstrap)(|.|\n)*(RequireJS)/i], 34 | ['app/scripts/main.js', /bootstrap/] 35 | ]; 36 | var expected = [ 37 | 'Gruntfile.js', 38 | 'app/404.html', 39 | 'app/favicon.ico', 40 | 'app/robots.txt', 41 | '.gitignore', 42 | '.gitattributes', 43 | '.bowerrc', 44 | '.jshintrc', 45 | '.editorconfig' 46 | ]; 47 | 48 | assert.file(expected); 49 | assert.fileContent(expectedContent); 50 | 51 | }); 52 | 53 | it('without sassBootstrap', function () { 54 | var expectedContent = [ 55 | ['bower.json', /("name": "temp")(|.|\n)*(requirejs)/], 56 | ['Gruntfile.js', /requirejs/], 57 | ['app/index.html', /(RequireJS)/i] 58 | ]; 59 | var expected = [ 60 | 'app/404.html', 61 | 'app/favicon.ico', 62 | 'app/robots.txt', 63 | '.gitignore', 64 | '.gitattributes', 65 | '.bowerrc', 66 | '.jshintrc', 67 | '.editorconfig', 68 | '.yo-rc.json', 69 | 'Gruntfile.js', 70 | 'package.json' 71 | ]; 72 | 73 | assert.file(expected); 74 | assert.fileContent(expectedContent); 75 | }); 76 | }); 77 | 78 | describe('creates model', function () { 79 | it('without failure', function (done) { 80 | test.createSubGenerator(config, 'model', function () { 81 | assert.fileContent( 82 | 'app/scripts/models/foo.js', /var FooModel = Backbone.Model.extend\(\{/ 83 | ); 84 | done(); 85 | }); 86 | }); 87 | }); 88 | 89 | describe('creates collection', function () { 90 | it('without failure', function (done) { 91 | test.createSubGenerator(config, 'collection', function () { 92 | assert.fileContent( 93 | 'app/scripts/collections/foo.js', /var FooCollection = Backbone.Collection.extend\(\{/ 94 | ); 95 | done(); 96 | }); 97 | }); 98 | }); 99 | 100 | describe('creates router', function () { 101 | it('without failure', function (done) { 102 | test.createSubGenerator(config, 'router', function () { 103 | assert.fileContent( 104 | 'app/scripts/routes/foo.js', /var FooRouter = Backbone.Router.extend\(\{/ 105 | ); 106 | done(); 107 | }); 108 | }); 109 | }); 110 | 111 | describe('creates backbone view', function () { 112 | it('without failure', function (done) { 113 | test.createSubGenerator(config, 'view', function () { 114 | assert.fileContent( 115 | 'app/scripts/views/foo.js', /var FooView = Backbone.View.extend\(\{(.|\n)*app\/scripts\/templates\/foo.ejs/ 116 | ); 117 | assert.file('app/scripts/templates/foo.ejs'); 118 | done(); 119 | }); 120 | }); 121 | }); 122 | 123 | describe('creates script tag without usemin block', function() { 124 | it('without failure', function() { 125 | var unexpectedContent = [ 126 | ['app/index.html', /(((?!build).|\n)*requirejs)+((?!build).|\n)*?/i], 127 | ]; 128 | assert.noFileContent(unexpectedContent); 129 | }); 130 | }); 131 | }); 132 | -------------------------------------------------------------------------------- /util.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | 5 | function escapeRegExp(str) { 6 | return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); 7 | } 8 | 9 | function rewrite(args) { 10 | // check if splicable is already in the body text 11 | var re = new RegExp(args.splicable.map(function (line) { 12 | return '\s*' + escapeRegExp(line); 13 | }) 14 | .join('\n')); 15 | 16 | if (re.test(args.haystack)) { 17 | return args.haystack; 18 | } 19 | 20 | var lines = args.haystack.split('\n'); 21 | 22 | var otherwiseLineIndex = 0; 23 | lines.forEach(function (line, i) { 24 | if (line.indexOf(args.needle) !== -1) { 25 | otherwiseLineIndex = i; 26 | } 27 | }); 28 | 29 | var spaces = 0; 30 | while (lines[otherwiseLineIndex].charAt(spaces) === ' ') { 31 | spaces += 1; 32 | } 33 | 34 | var spaceStr = ''; 35 | while ((spaces -= 1) >= 0) { 36 | spaceStr += ' '; 37 | } 38 | 39 | lines.splice(otherwiseLineIndex, 0, args.splicable.map(function (line) { 40 | return spaceStr + line; 41 | }).join('\n')); 42 | 43 | return lines.join('\n'); 44 | } 45 | 46 | function rewriteFile(args) { 47 | args.haystack = fs.readFileSync(args.file, 'utf8'); 48 | var body = rewrite(args); 49 | 50 | fs.writeFileSync(args.file, body); 51 | } 52 | 53 | module.exports = { 54 | rewrite: rewrite, 55 | rewriteFile: rewriteFile 56 | }; 57 | --------------------------------------------------------------------------------