├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Gruntfile.js ├── app ├── USAGE ├── index.js └── templates │ ├── .travis.yml │ ├── Gruntfile.js │ ├── _.gitignore │ ├── _package.json │ └── server │ ├── api │ ├── thing │ │ ├── index.js │ │ ├── thing.controller.js │ │ ├── thing.model(mongoose).js │ │ ├── thing.seed.json │ │ ├── thing.socket(socketio).js │ │ └── thing.spec.js │ └── user(auth) │ │ ├── index.js │ │ ├── user.controller.js │ │ ├── user.model.js │ │ ├── user.model.spec.js │ │ └── user.seed.json │ ├── app.js │ ├── auth(auth) │ ├── auth.service.js │ ├── facebook(facebookAuth) │ │ ├── index.js │ │ └── passport.js │ ├── google(googleAuth) │ │ ├── index.js │ │ └── passport.js │ ├── index.js │ ├── local │ │ ├── index.js │ │ └── passport.js │ └── twitter(twitterAuth) │ │ ├── index.js │ │ └── passport.js │ ├── config │ ├── _local.env.js │ ├── environment │ │ ├── development.js │ │ ├── index.js │ │ ├── production.js │ │ └── test.js │ ├── express.js │ ├── seed(mongoose).js │ └── socketio(socketio).js │ └── routes.js ├── endpoint ├── index.js └── templates │ ├── index.js │ ├── name.controller.js │ ├── name.model(mongoose).js │ ├── name.seed.json │ ├── name.socket(socketio).js │ └── name.spec.js ├── heroku ├── USAGE ├── index.js └── templates │ └── Procfile ├── package.json ├── readme.md ├── script-base.js └── util.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | demo 3 | .idea 4 | .DS_Store 5 | release.txt -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.12' 4 | before_install: 5 | - gem update --system 6 | - gem install sass --version "=3.3.7" 7 | - npm install -g bower grunt-cli 8 | services: mongodb 9 | notifications: 10 | webhooks: 11 | urls: 12 | - https://webhooks.gitter.im/e/911ed472ef19bcb27858 13 | on_success: change # options: [always|never|change] default: always 14 | on_failure: always # options: [always|never|change] default: always 15 | on_start: false # default: false 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ## v2.1.1 (2015-07-29) 3 | 4 | 5 | #### Bug Fixes 6 | 7 | * **app:** use 0.0.0.0 for default IP ([2cd1c24d](http://github.com/DaftMonk/generator-angular-fullstack/commit/2cd1c24d2224e46fa68f8da834e86ac3ed80de8e)) 8 | 9 | 10 | ## v2.1.0 (2015-07-15) 11 | 12 | 13 | #### Bug Fixes 14 | 15 | * **app:** missing event.preventDefault ([c90d7621](http://github.com/DaftMonk/generator-angular-fullstack/commit/c90d7621b8f17f6e74f8a59f02fd78a8fcf628aa)) 16 | * **demo:** include bootstrap in demo ([19e21331](http://github.com/DaftMonk/generator-angular-fullstack/commit/19e213319ecdf27041948133fe4c9642184133d8)) 17 | * **express:** support new options of updated connect-mongo ([727d6616](http://github.com/DaftMonk/generator-angular-fullstack/commit/727d661642441d5ee76e4fba9ebcbba134e43058)) 18 | * **jshint:** Removed 'regexp' from server and client jshintrc (I couldn't find it in the docs ([e02f0940](http://github.com/DaftMonk/generator-angular-fullstack/commit/e02f09405de2423d7be92e5ca0f9be740becb693)) 19 | * **travis:** 20 | * Remove unicode stuff from file creation test expectations, and add nodejs 0.12 t ([bf9a9737](http://github.com/DaftMonk/generator-angular-fullstack/commit/bf9a9737721d1ea72f3f7b9689d6781e78b4c606)) 21 | * Add nodejs 12 to travis.yml ([acecde9d](http://github.com/DaftMonk/generator-angular-fullstack/commit/acecde9d0e02b579d4b0a2a33d7c0f24067258ec)) 22 | 23 | 24 | #### Features 25 | 26 | * **app:** 27 | * additional app generator option for ES6 preprocessing using babel ([cbb06a48](http://github.com/DaftMonk/generator-angular-fullstack/commit/cbb06a48e28c594a53f61e49857cbb711bd74ce9)) 28 | * additional app generator option for ES6 preprocessing using babel ([bc03aba9](http://github.com/DaftMonk/generator-angular-fullstack/commit/bc03aba9ee82812f29f4ec9f86daf3b8d1c531b8)) 29 | * add mongodb error handling to quit app if unable to connect with mongodb server ([31bee73c](http://github.com/DaftMonk/generator-angular-fullstack/commit/31bee73c1085c3a3f3c8e4e6f1f45db28fa9f94b)) 30 | * **build:** add gitter webhook for travis ([6b88efdf](http://github.com/DaftMonk/generator-angular-fullstack/commit/6b88efdfb1d6b3b2dcbf47e0517bfc5117247d12)) 31 | * **gen:** Remove global jQuery dependency ([a9230ca2](http://github.com/DaftMonk/generator-angular-fullstack/commit/a9230ca2ba1b2727abdc466a11096ec513fd7085)) 32 | * **readme:** add david-dm badge & move badges to new line ([f8f32f4f](http://github.com/DaftMonk/generator-angular-fullstack/commit/f8f32f4f67938a5f7416e2a07d7b24d71a12585f)) 33 | 34 | 35 | ### v2.0.13 (2014-08-29) 36 | 37 | 38 | #### Bug Fixes 39 | 40 | * **gen:** 41 | * use bool for bootstrap filters ([a5decbc3](http://github.com/DaftMonk/generator-angular-fullstack/commit/a5decbc36e933f94c69d9e9bb58bd8e07148c34d), closes [#496](http://github.com/DaftMonk/generator-angular-fullstack/issues/496)) 42 | * fix build when not selecting socket.io ([fdf063c6](http://github.com/DaftMonk/generator-angular-fullstack/commit/fdf063c6cc2ec4eeef252f13b2e0d301931fa83c)) 43 | 44 | 45 | ### v2.0.12 (2014-08-25) 46 | 47 | #### Bug Fixes 48 | 49 | * revert multiple strategies per account, which shouldn't go in a patch release 50 | 51 | 52 | ### v2.0.11 (2014-08-25) 53 | 54 | 55 | #### Bug Fixes 56 | 57 | * **app:** Use parentheses to fix string concat in config ([c6a50ce7](http://github.com/DaftMonk/generator-angular-fullstack/commit/c6a50ce791ab633a17654ce9b0090007d7152463), closes [#466](http://github.com/DaftMonk/generator-angular-fullstack/issues/466)) 58 | * improve jshint usage ([35fcf490](http://github.com/DaftMonk/generator-angular-fullstack/commit/35fcf4902dbbdab2ca6b394ab87ef8e3cc3d052b), closes [#463](http://github.com/DaftMonk/generator-angular-fullstack/issues/463), [#486](http://github.com/DaftMonk/generator-angular-fullstack/issues/486)) 59 | * **gen:** use more restrictive version range for ng-component ([19698973](http://github.com/DaftMonk/generator-angular-fullstack/commit/196989730c8922fa5e1dc9caa45eb85052535e30)) 60 | 61 | 62 | #### Features 63 | 64 | * **socket.io:** build socket.io into vendor.js ([06f2e46e](http://github.com/DaftMonk/generator-angular-fullstack/commit/06f2e46ef382b5af1691f34b6cf504f1e5640b86)) 65 | * **docs:** Inform users/developers of the `canary` branch ([74693623](http://github.com/DaftMonk/generator-angular-fullstack/commit/74693623eb23c9399495a3baff7e3479a1d9f3ba)) 66 | * **gen:** make generator tests faster, and easier to run ([84acb744](http://github.com/DaftMonk/generator-angular-fullstack/commit/84acb7448ccc7c55b72bdd19bfae50c33d527296)) 67 | * **app:** add additional node version to travis.yml ([e4f00b08](http://github.com/DaftMonk/generator-angular-fullstack/commit/e4f00b083a880713ca563e3447b9fb3f56a54ebc)) 68 | * **uibootstrap:** add basic modal service and template when using uibootstrap ([7c14bed4](http://github.com/DaftMonk/generator-angular-fullstack/commit/7c14bed4873b92124bcbe422fed918836b8f5df5)) 69 | 70 | 71 | ### v2.0.10 (2014-08-16) 72 | 73 | 74 | #### Bug Fixes 75 | 76 | * **server:** undefined domain env variable causing issues ([cb683dde](http://github.com/DaftMonk/generator-angular-fullstack/commit/cb683dde6814959328a58267215ce477aa723e35)) 77 | 78 | 79 | ### v2.0.9 (2014-08-15) 80 | 81 | 82 | #### Bug Fixes 83 | 84 | * **app:** 85 | * add .idea folder to gitignore ([2e1f1182](http://github.com/DaftMonk/generator-angular-fullstack/commit/2e1f1182684594300ac5ca85ffab175bfcafd3ec)) 86 | * Missing user response code ([c1766604](http://github.com/DaftMonk/generator-angular-fullstack/commit/c1766604d7ae7ab1eb8713f37285d13341dc8ae1)) 87 | * use `''` instead `null` as URL to open ioSocket ([0f0d0fdc](http://github.com/DaftMonk/generator-angular-fullstack/commit/0f0d0fdce38d42f04f71d9e1174400adfb699061)) 88 | * save the version of the generator that was used ([2b76b17b](http://github.com/DaftMonk/generator-angular-fullstack/commit/2b76b17bb5fa1980b449498beec87ab58ceee012)) 89 | * **gruntfile:** incorrect path to index.html for cdnify ([0ad646cb](http://github.com/DaftMonk/generator-angular-fullstack/commit/0ad646cbd48dbb2f65fc00b930a9f243174611be)) 90 | * **openshift:** fix issues with openshift deployment ([ace07238](http://github.com/DaftMonk/generator-angular-fullstack/commit/ace07238e3299d6002337ba12f7862ce84beafd8)) 91 | 92 | 93 | #### Features 94 | 95 | * **gen:** add automatic demo releases with grunt task ([44852233](http://github.com/DaftMonk/generator-angular-fullstack/commit/44852233fcf28d5ff8681fcabc3bfb4130778a22)) 96 | * **gruntfile:** add grunt buildcontrol tasks to app, for easier deployment ([036478df](http://github.com/DaftMonk/generator-angular-fullstack/commit/036478dfd7067d38ab19ca86c0c5196678412799)) 97 | * **heroku:** provide prompt to set the deployment region ([13cd5e7d](http://github.com/DaftMonk/generator-angular-fullstack/commit/13cd5e7d42f2845268f38ba19e0d253ae675c594)) 98 | * **server:** add sample env config file that can be tracked by git ([c9f80bcd](http://github.com/DaftMonk/generator-angular-fullstack/commit/c9f80bcd67d6e3eef2c78ccbceff78f763ae88d1)) 99 | 100 | 101 | ### v2.0.8 (2014-07-31) 102 | 103 | 104 | #### Bug Fixes 105 | 106 | * **coffee:** update socket service to match javascript version ([c27cefe2](http://github.com/DaftMonk/generator-angular-fullstack/commit/c27cefe24d8ec64d905f908c66a56bf602303dce)) 107 | * **gen:** Fixed missing `oauth` property in `.yo-rc.json` after 2.0.5 update ([11d324b9](http://github.com/DaftMonk/generator-angular-fullstack/commit/11d324b95992b68bd19f402401e98f5936cdb343)) 108 | * **travis:** install sass gem if sass is enabled ([ceeac27b](http://github.com/DaftMonk/generator-angular-fullstack/commit/ceeac27b8f912aa6dec2caf3bf20dd7551f2d754)) 109 | * **twitter:** revert mongoose connection change ([8675a002](http://github.com/DaftMonk/generator-angular-fullstack/commit/8675a002e301957569374fdcad87aab0bff6b3b4)) 110 | 111 | 112 | #### Features 113 | 114 | * **user-management:** use the User $resource to populate users for the admin page ([708f0729](http://github.com/DaftMonk/generator-angular-fullstack/commit/708f07290d98d6bd73723f9db49cce7758c3d12b)) 115 | 116 | 117 | ### v2.0.7 (2014-07-27) 118 | 119 | 120 | #### Bug Fixes 121 | 122 | * **gruntfile:** grunt tasks should run if no local config exists ([422d6bca](http://github.com/DaftMonk/generator-angular-fullstack/commit/422d6bca07283057b0fa275dba0de447c9f4f167)) 123 | * **server:** fix setting TTL index on collection : sessions error ([0581ed09](http://github.com/DaftMonk/generator-angular-fullstack/commit/0581ed094b2c6141ab9e0c016eda22aa49e1d075)) 124 | 125 | 126 | ### v2.0.6 (2014-07-27) 127 | 128 | 129 | #### Bug Fixes 130 | 131 | * **app:** 132 | * `things` made a little bit more responsive ([58aa7a48](http://github.com/DaftMonk/generator-angular-fullstack/commit/58aa7a489ae28c22be59b3a61db027ccf2f1ae46)) 133 | * **dependencies:** change ngmin to ng-annotate ([dd023fa5](http://github.com/DaftMonk/generator-angular-fullstack/commit/dd023fa5fd90b8b541b8cc60c87186ee619e4844)) 134 | * **bootstrap:** removed styles breaking responsiveness for high-res screens ([053fedb8](http://github.com/DaftMonk/generator-angular-fullstack/commit/053fedb89f64294a55538ad9b806b2d7de4d1c7f)) 135 | * **socketio:** fallback for servers where `socket.handshake.address` is not provided ([f6a19348](http://github.com/DaftMonk/generator-angular-fullstack/commit/f6a19348ad404aa72c31eef8dc84aac8db0e904a)) 136 | * **stylus:** remove bootstrap css import in stylus when bootstrap is not selected ([f7c3d0ad](http://github.com/DaftMonk/generator-angular-fullstack/commit/f7c3d0ad41da5f0072c2cf64ff5c9a894052d194), closes [#368](http://github.com/DaftMonk/generator-angular-fullstack/issues/368)) 137 | 138 | 139 | #### Features 140 | 141 | * **oauth:** remove code according to user prompts ([316bd9dd](http://github.com/DaftMonk/generator-angular-fullstack/commit/316bd9dd3632622b0fb434cacfc4150f01d18e4c)) 142 | 143 | 144 | ### v2.0.5 (2014-07-17) 145 | 146 | #### Bug Fixes 147 | 148 | * **account:** add authentication requirement for settings view ([9105c0fd](http://github.com/DaftMonk/generator-angular-fullstack/commit/9105c0fdbabdbde68fb6cf0fe0d6993ead6e7095), closes [#327](http://github.com/DaftMonk/generator-angular-fullstack/issues/327)) 149 | * **app:** 150 | * use correct path for font awesome and glyphicons ([1917ba31](http://github.com/DaftMonk/generator-angular-fullstack/commit/1917ba31264fc90bea0fce36b8d144f897e8bf08)) 151 | * wait for currentUser to resolve before checking if logged in on route changes ([6d6090d9](http://github.com/DaftMonk/generator-angular-fullstack/commit/6d6090d9c4dcd5d8a1f6ecb2cf5dc0bb4c8598fe)) 152 | * bootstrap glyphicons not correctly linked on grunt build ([53d193d0](http://github.com/DaftMonk/generator-angular-fullstack/commit/53d193d011c7c1ea8c9477e8f17ad56cc4214362)) 153 | * **dependencies:** include certain dependencies only when answering yes to their respective prompts ([040c57de](http://github.com/DaftMonk/generator-angular-fullstack/commit/040c57de8689f2e0fc35410d0b6935363aaa8458)) 154 | * **server:** 155 | * fix seeding of db in test mode causing tests to randomly fail ([05f7f433](http://github.com/DaftMonk/generator-angular-fullstack/commit/05f7f43372bc3bd54bead811952b775adeec1f05)) 156 | * make user tests run more consistently ([addb5061](http://github.com/DaftMonk/generator-angular-fullstack/commit/addb5061f62696c7a0078a8d2c7443d428e69376)) 157 | * warnings that express was using deprecated features ([8dc2f1e4](http://github.com/DaftMonk/generator-angular-fullstack/commit/8dc2f1e48503c27cbd2aac3c455adac7781a6539)) 158 | * missing `res` param for handleError ([eb7d50c8](http://github.com/DaftMonk/generator-angular-fullstack/commit/eb7d50c8d27820a6b26caf2a1aaa0e4fa8eee367)) 159 | 160 | #### Features 161 | 162 | * **app:** 163 | * added oath buttons to signup page ([a408f58e](http://github.com/DaftMonk/generator-angular-fullstack/commit/a408f58edb923cd14bf7c7b3411b874dce5f0724)) 164 | * upgrade socket.io to use v1.0.6 165 | * **gen:** 166 | * add option for Stylus as a preprocessor ([1b90c448](http://github.com/DaftMonk/generator-angular-fullstack/commit/1b90c448fbf374287fe07f782f9788dfb9a23613)) 167 | * make bootstrap and bootstrap ui optional ([f50d0942](http://github.com/DaftMonk/generator-angular-fullstack/commit/f50d094226fdbf6a7e65ba3783a26efc8544ba08)) 168 | 169 | 170 | ### v2.0.4 (2014-07-08) 171 | 172 | 173 | #### Bug Fixes 174 | 175 | * **app:** fix dependency injection minsafe problem in auth service coffeescript. ([03742a80](http://github.com/DaftMonk/generator-angular-fullstack/commit/03742a8000f19efdf14791ff1aae52a90e86c149)) 176 | * **gen:** heroku and openshift generators requiring .yo-rc file to work ([88ebfc8c](http://github.com/DaftMonk/generator-angular-fullstack/commit/88ebfc8c835ac6ec04b6d42fcf9357cfb0bcc47d)) 177 | 178 | 179 | ### v2.0.3 (2014-07-04) 180 | 181 | 182 | #### Bug Fixes 183 | 184 | * **server:** only enable sessions if twitter oauth was selected ([bcd00dc0](http://github.com/DaftMonk/generator-angular-fullstack/commit/bcd00dc02d270486adafe6dbf973a4ec25499a5e)) 185 | 186 | 187 | ### v2.0.2 (2014-07-02) 188 | 189 | #### Bug Fixes 190 | 191 | * **gen:endpoint** 192 | * fix endpoint spec not properly adjusting to users route url 193 | * fix some valid route urls causing failing tests 194 | 195 | 196 | ## v2.0.0 (2014-07-02) 197 | 198 | #### Features 199 | 200 | * **app:** 201 | * Follow googles AngularJS project recommendations to make a very modular app structure. 202 | * New look for generated app 203 | * Add basic crud interface to app 204 | * Support for UI Router 205 | * Support for LESS 206 | * Built in support for protractor e2e tests 207 | * Add angular-bootstrap and lodash to default app 208 | * More consistent and understandable naming conventions for files 209 | * **server:** 210 | * Modular project structure for express server 211 | * Support for social auths with facebook/twitter/google 212 | * Role based authentication 213 | * Replace session based authentication with JWT authentication 214 | * Optional integration with socket.io 215 | * Added config file, ignored by git, for setting local environment variables, api keys, secrets.. etc. 216 | * **gruntfile:** 217 | * Optimizations to the gruntfile 218 | * Automate injection of new scripts into index file with grunt 219 | * Use ng-templates to concatenate all the html/jade views into the javascript payload 220 | * **gen:** 221 | * Abstract client-side generators into generator-ng-component, use new composition feature of yeoman to keep them available in the generator. 222 | * Add useful tests to the generator, start using travis CI 223 | * use .yo-rc file to keep track of generated configurations 224 | * Add endpoint generator to angular-fullstack, generates model / route / controller / test / socket updates 225 | 226 | #### Breaking Changes 227 | * New project structure 228 | * Deprecated value and constant sub generators 229 | * Sub-generators generate components in a single directory and don't inject themselves into the index file (this is done by a grunt task now) 230 | 231 | 232 | ### v1.4.3 (2014-05-25) 233 | 234 | 235 | #### Bug Fixes 236 | 237 | * **config:** fix issue where `config.ip` is undefined in non-production environments ([087f5bca](http://github.com/DaftMonk/generator-angular-fullstack/commit/087f5bca1610e8250de50ce11a16e879908e3177)) 238 | * **package:** update connect-mongo to correct version 239 | * **app:** add require attribute to login.html inputs so it validates on client side 240 | * **gen:** use lowercase filenames for scripts 241 | 242 | 243 | ### v1.4.2 (2014-04-16) 244 | 245 | 246 | #### Bug Fixes 247 | 248 | * **gen:** typo in heroku generator was preventing it from working on unix based systems ([9d3b5738](http://github.com/DaftMonk/generator-angular-fullstack/commit/9d3b5738528497f74d37d22c304b0d46cd5007fa)) 249 | 250 | 251 | ### v1.4.1 (2014-04-15) 252 | 253 | 254 | #### Bug Fixes 255 | 256 | * **server:** 257 | * grunt test was incorrectly using dev config, fixes #179 ([62d8492f](http://github.com/DaftMonk/generator-angular-fullstack/commit/62d8492fd9fcfde653bab0f65b46f9961b8016bc)) 258 | * emails are no longer case sensitive ([dafd8db1](https://github.com/DaftMonk/generator-angular-fullstack/commit/dafd8db1f529b86322ef60f65897761cef92841a)) 259 | 260 | 261 | ## v1.4.0 (2014-04-13) 262 | 263 | #### Features 264 | 265 | * **server:** updated Express to v4.x 266 | * **app:** matching angular dependencies to the latest verison, now that bower excludes pre-releases ([94c0c636](http://github.com/DaftMonk/generator-angular-fullstack/commit/94c0c63691976eaf7136c33365f611b465ba7f61)) 267 | * **gen:** 268 | * Added `angular-fullstack:openshift` generator, for deploying your app to OpenShift 269 | * Added `angular-fullstack:heroku` generator, which improves upon the former `:deploy` generator for deploying to Heroku 270 | 271 | #### Bug Fixes 272 | 273 | * **server:** fixed possible DB flushing when mochaTest is called by watch, first call 'env:test' task before 'mochaTest' 274 | ([2f0320fe](http://github.com/DaftMonk/generator-angular-fullstack/commit/2f0320feb89f3a5f1757f8adcae4b8c0d5599c95)) 275 | 276 | #### Breaking Changes 277 | 278 | * The `angular-fullstack:deploy` generator is deprecated. Instead use `angular-fullstack:heroku` or `angular-fullstack:openshift`. 279 | 280 | 281 | ### v1.3.3 (2014-03-29) 282 | 283 | #### Features 284 | 285 | * **server:** enable response compression ([1547ac6f](http://github.com/DaftMonk/generator-angular-fullstack/commit/1547ac6f794ce06d2a9329531bec5dae73441f04)) 286 | 287 | #### Bug Fixes 288 | 289 | * **config:** change default port in config to 9000 ([480515f6](http://github.com/DaftMonk/generator-angular-fullstack/commit/480515f6cc8d7600003a570f9b1f0530fd178ac5)) 290 | * **gruntfile:** 291 | * update gruntfile to use port from config ([c8aa2d5f](http://github.com/DaftMonk/generator-angular-fullstack/commit/c8aa2d5feda90a2c1e7528165b1bd22e9eab5e77)) 292 | * workaround imagemin bug by disabling caching ([3e435fa7](http://github.com/DaftMonk/generator-angular-fullstack/commit/3e435fa74b1574223f129867621a9a800cea2af9)) 293 | * **package:** update required generator-karma dependency to the correct version ([0c0e8a52](http://github.com/DaftMonk/generator-angular-fullstack/commit/0c0e8a522ffa94ea0bd9c0df9994c23340a957f7)) 294 | 295 | 296 | ### v1.3.2 (2014-03-01) 297 | 298 | 299 | #### Bug Fixes 300 | 301 | * **package.json:** updated dependencies that were causing issues with npm install ([1874cdf1](http://github.com/DaftMonk/generator-angular-fullstack/commit/1874cdf16c9d1670d0492db8db1be77e43222de4)) 302 | 303 | 304 | ### v1.3.1 (2014-03-01) 305 | 306 | 307 | #### Bug Fixes 308 | 309 | * **gruntfile:** configured jshint for client tests ([4ee92b9a](http://github.com/DaftMonk/generator-angular-fullstack/commit/4ee92b9a4c466982b171bc777c3ba6ba5a477633)) 310 | 311 | 312 | ## v1.3.0 (2014-02-27) 313 | 314 | 315 | #### Bug Fixes 316 | 317 | * **grunt:** 318 | * fixed clean:dist task ([e390cac0](http://github.com/DaftMonk/generator-angular-fullstack/commit/e390cac015974f691ab51261128b4215e878b25f)) 319 | * **server:** 320 | * config all and env specific are now correctly deep merged ([31039872](http://github.com/DaftMonk/generator-angular-fullstack/commit/31039872caec541847cb80da8edf3c7ffd83ef48)) 321 | * fix configuration so that (express) errorHandler works ([0116cb35](http://github.com/DaftMonk/generator-angular-fullstack/commit/0116cb35524afb2ee5b8a599f6bc76dbe04febc5)) 322 | 323 | 324 | #### Features 325 | 326 | * **app:** 327 | * added `grunt serve:debug` task that launches the server with a node-inspector tab ([de3e7a8b](http://github.com/DaftMonk/generator-angular-fullstack/commit/de3e7a8b7e63c54090c8fbc2f51998965b2e274f)) 328 | * update to bootstrap sass official ([3799c13c](http://github.com/DaftMonk/generator-angular-fullstack/commit/3799c13c3b65fcc2abfbacb5292b192543558d52)) 329 | * **server:** 330 | * added tests for user model ([4c894b65](http://github.com/DaftMonk/generator-angular-fullstack/commit/4c894b65ec6a6d8de2b7290521f25b134ac30f40)) 331 | * added mocha test configuration ([458a2f6a](http://github.com/DaftMonk/generator-angular-fullstack/commit/458a2f6a28485a8791815f8795e726af3c308efe)) 332 | 333 | 334 | ### v1.2.7 (2014-02-15) 335 | 336 | 337 | #### Features 338 | 339 | * **server:** undefined api routes now return a 404 ([ec829fe2](http://github.com/DaftMonk/generator-angular-fullstack/commit/ec829fe2221dbe001c12983c95576c20f0e63a30)) 340 | 341 | 342 | ### v1.2.6 (2014-02-14) 343 | 344 | 345 | #### Bug Fixes 346 | 347 | * **app:** 348 | * redirect to login only on 401s ([64b7bace](http://github.com/DaftMonk/generator-angular-fullstack/commit/64b7bacea98e59cb72a44627b57ca331d9bf051d)) 349 | * fixed incorrect css path for usemin in gruntfile ([46fca240](http://github.com/DaftMonk/generator-angular-fullstack/commit/46fca240009d2c61aa07b5cef2275e4095033a10)) 350 | * **grunt:** include partial sub-directories in htmlmin ([77564ba3](http://github.com/DaftMonk/generator-angular-fullstack/commit/77564ba3b59baa52546f3b1170ee9cad16b7d413)) 351 | * **server:** 352 | * fixed connect-mongo error ([c12db5b3](http://github.com/DaftMonk/generator-angular-fullstack/commit/c12db5b3e9b7475ba4581f23f9c20e4ce701b855)) 353 | * livereload now waits for server to finish restarting ([71d63f0a](http://github.com/DaftMonk/generator-angular-fullstack/commit/71d63f0a704a2773cee368b1af24c188e04d0ae3)) 354 | * exposed configured passport from passport module ([772133de](http://github.com/DaftMonk/generator-angular-fullstack/commit/772133de1f86c8a6a8c93179673deb4359e30c94)) 355 | * only require models if they are coffescript or js files ([ce2ee236](http://github.com/DaftMonk/generator-angular-fullstack/commit/ce2ee2369ff0c4aedc1a13d04359d918ea1b3d8d)) 356 | 357 | 358 | #### Features 359 | 360 | * **deps:** upgrade angular to 1.2.11, and jquery to 1.11.0 ([cd5c3030](http://github.com/DaftMonk/generator-angular-fullstack/commit/cd5c303023f57de423ca69067b1105db17d066e3)) 361 | * **app:** switched sass-bootstrap to offical bootstrap-sass ([024fee88](http://github.com/DaftMonk/generator-angular-fullstack/commit/024fee8831c4a32962283878b6b9dbd444874ec0)) 362 | 363 | 364 | ### v1.2.5 (2014-01-27) 365 | 366 | 367 | #### Bug Fixes 368 | 369 | * **app:** 370 | * fixed coffee service so it's min-safe ([c18c9da4](http://github.com/DaftMonk/generator-angular-fullstack/commit/c18c9da4475e8e48507746f441186edf9fde18b1)) 371 | * fixed bootstrap css being imported rather than compass bootstrap ([f2739987](http://github.com/DaftMonk/generator-angular-fullstack/commit/f27399879e84daf7230d9cd953c19e93bcd22746)) 372 | * **server:** 373 | * replaced deprecated bodyparser ([788fda04](http://github.com/DaftMonk/generator-angular-fullstack/commit/788fda04ebd1ed7d24190aacda44c252fd1ae002)) 374 | * updated node version dependency ([b19a0997](http://github.com/DaftMonk/generator-angular-fullstack/commit/b19a0997c6db08a47a56069621756129e07c5915)) 375 | * **gen:** updated generator dependencies ([115008d3](http://github.com/DaftMonk/generator-angular-fullstack/commit/115008d378a9fd9cc47561f451cd9153f4f2c566)) 376 | 377 | 378 | ### v1.2.4 (2014-01-16) 379 | 380 | 381 | #### Bug Fixes 382 | 383 | * **grunt:** fixed incorrect templating expression ([2a59e070](http://github.com/DaftMonk/generator-angular-fullstack/commit/2a59e070bb89abb4ea83e165f8a29b8de94621f1)) 384 | 385 | 386 | ### v1.2.3 (2014-01-16) 387 | 388 | 389 | #### Bug Fixes 390 | 391 | * **app:** fixed jshint warning in user model ([f668fdc7](http://github.com/DaftMonk/generator-angular-fullstack/commit/f668fdc7f798e2656a9576f249836f7c91d27f1a)) 392 | 393 | 394 | ### v1.2.2 (2014-01-16) 395 | 396 | 397 | #### Bug Fixes 398 | 399 | * **app:** 400 | * replaced bcrypt with crypto for windows users ([af20c3ab](http://github.com/DaftMonk/generator-angular-fullstack/commit/af20c3ab6fd63e41475175e333810d09b3e9c3ea)) 401 | * added karma dependencies directly to package template ([13ea60e7](http://github.com/DaftMonk/generator-angular-fullstack/commit/13ea60e7ec5763fb7f96900464df1bf26ee6912c)) 402 | 403 | 404 | ### v1.2.1 (2014-01-12) 405 | 406 | 407 | ## v1.2.0 (2014-01-11) 408 | 409 | #### Features 410 | 411 | * **app:** 412 | * restructured project for easier configuration ([0a2bf2ab](http://github.com/DaftMonk/generator-angular-fullstack/commit/0a2bf2abe04de834c786402b8945d247b4f951aa)) 413 | * grunt build now moves all files into dist folder ([e6eff5d5](http://github.com/DaftMonk/generator-angular-fullstack/commit/e6eff5d56bf2a784feb3de6218e74b5390df319f)) 414 | * **server:** added jshint error checking before livereload occurs ([7e001d31](http://github.com/DaftMonk/generator-angular-fullstack/commit/7e001d3156d778022e7b6847cc65934432fb9200)) 415 | * **gen:** added passport question for scaffolding out user account creation ([87841064](http://github.com/DaftMonk/generator-angular-fullstack/commit/8784106409e51cddf8fcdc6ab52b1e81137cda19)) 416 | 417 | #### Bug Fixes 418 | 419 | * **app:** removed async dependency ([d5636d71](http://github.com/DaftMonk/generator-angular-fullstack/commit/d5636d712a984948fb92b82794681c07d43d830d)) 420 | * **gitignore:** fix app/views being ignored by git ([7fa82ff9](http://github.com/DaftMonk/generator-angular-fullstack/commit/7fa82ff953e9f1368b8f9d6c3dadb5fe83bec002)) 421 | * **server:** 422 | * config wasn't added to default project ([79c5e027](http://github.com/DaftMonk/generator-angular-fullstack/commit/79c5e027719507a74497c2f6be77375a513316c4)) 423 | * removed typo and cleaned up extra whitespace ([1a132c28](http://github.com/DaftMonk/generator-angular-fullstack/commit/1a132c2822fd4973068b8beae075d0c8ec3efd42)) 424 | * fixed style issues that were tripping up jshint 425 | 426 | #### Breaking Changes 427 | 428 | * `grunt heroku` is deprecated. Use `grunt build` instead. 429 | 430 | 431 | ### v1.1.1 (2013-12-25) 432 | 433 | #### Bug Fixes 434 | 435 | * **views:** 436 | * Replaced deprecated jade tags. 437 | 438 | #### Features 439 | 440 | * **app:** 441 | * Updgrade to AngularJS 1.2.6 442 | 443 | 444 | ## v1.1.0 (2013-12-22) 445 | 446 | 447 | #### Bug Fixes 448 | 449 | * **app:** 450 | * only copy CSS if Compass is not installed ([7e586745](http://github.com/DaftMonk/generator-angular-fullstack/commit/7e58674585e138c0f2eb81f46ef2cc4f1b9a3bf8)) 451 | * services use classified names ([56a71a83](http://github.com/DaftMonk/generator-angular-fullstack/commit/56a71a83cdf90f81bb37b422ba4d40e75d28e1fe), closes [#484](http://github.com/DaftMonk/generator-angular-fullstack/issues/484)) 452 | * reload JS files in watch ([d20f5bd2](http://github.com/DaftMonk/generator-angular-fullstack/commit/d20f5bd20ba95d47447f8acceee491a0a0ba9724)) 453 | * **build:** deselecting ngRoute does remove route stuff ([a358c1ae](http://github.com/DaftMonk/generator-angular-fullstack/commit/a358c1ae69bff6a7708ea0a77248698f931f2e4d), closes [#486](http://github.com/DaftMonk/generator-angular-fullstack/issues/486)) 454 | * **gen:** 455 | * updated all conflicts, and fixed some bugs, from merging with upstream ([d07c829d](http://github.com/DaftMonk/generator-angular-fullstack/commit/d07c829db283eaa4986774f9664243b50b3b5171)) 456 | * fix bower install prompt during project gen ([706f1336](http://github.com/DaftMonk/generator-angular-fullstack/commit/706f1336852923e409d669ae6fc6faeda7bbb017), closes [#505](http://github.com/DaftMonk/generator-angular-fullstack/issues/505)) 457 | * **package:** fix imagemin for windows users ([b3cec228](http://github.com/DaftMonk/generator-angular-fullstack/commit/b3cec228b4354343929ca07fd7225526cdab74d9)) 458 | * **views:** 459 | * fix ng includes ([598c69a5](http://github.com/DaftMonk/generator-angular-fullstack/commit/598c69a594e00f598e0cbd435444bc8abaa0d4ee)) 460 | * add compiled views to gitignore ([087ede5f](http://github.com/DaftMonk/generator-angular-fullstack/commit/087ede5f8e2cef4c49f940ef922d71a51d110d51)) 461 | * fix incorrect build path for vendor css ([0ed2a200](http://github.com/DaftMonk/generator-angular-fullstack/commit/0ed2a20018086fa514846ad2503841f6d5b23e16)) 462 | 463 | 464 | #### Features 465 | 466 | * **app:** 467 | * add jasmine browser global to test jshintrc ([11b6ed42](http://github.com/DaftMonk/generator-angular-fullstack/commit/11b6ed42b5e941f25cc305eb5c4e8ba49586cf64)) 468 | * use lowercase file names ([23e5d772](http://github.com/DaftMonk/generator-angular-fullstack/commit/23e5d7724e7e02e4b974f4e804f35eca33a53aea), closes [#463](http://github.com/DaftMonk/generator-angular-fullstack/issues/463)) 469 | * use htmlmin for smaller HTML files ([2b85a52a](http://github.com/DaftMonk/generator-angular-fullstack/commit/2b85a52a054ac8cf1ab86ce1cd3de7819d30ea52), closes [#469](http://github.com/DaftMonk/generator-angular-fullstack/issues/469)) 470 | * use grunt-bower-install for dep management ([ba7b5051](http://github.com/DaftMonk/generator-angular-fullstack/commit/ba7b505117307059a6d013d838c8aeff6db0e452), closes [#497](http://github.com/DaftMonk/generator-angular-fullstack/issues/497)) 471 | * Enable Node debug mode ([83ae4a9e](http://github.com/DaftMonk/generator-angular-fullstack/commit/83ae4a9e328a388dd61414634ca5e10c8a0c819b)) 472 | * **gen:** 473 | * Added navbar to starting template ([b5e94749](http://github.com/DaftMonk/generator-angular-fullstack/commit/b5e94749384ab9a3305991df62d7ed9856bded83)) 474 | * additional work for compass support ([11cb9943](http://github.com/DaftMonk/generator-angular-fullstack/commit/11cb99437271b6e8f6cdaee8fd5fc9cda7a20d1d)) 475 | * add Compass support to the initialization process ([7fac1194](http://github.com/DaftMonk/generator-angular-fullstack/commit/7fac1194179df3181f52258b0aa7333799fec253)) 476 | * add welcome message and dep notice for minsafe ([f0bb8da2](http://github.com/DaftMonk/generator-angular-fullstack/commit/f0bb8da2d67c3f627bf775e2d4f53340b5c980c4), closes [#452](http://github.com/DaftMonk/generator-angular-fullstack/issues/452)) 477 | * **server:** 478 | * Added middleware for development mode that disables caching of script files ([c082c81c](http://github.com/DaftMonk/generator-angular-fullstack/commit/c082c81c21a9d8d6fd9fccd5001270759fb2a30f)) 479 | * Moved express configuration code out of server.js and into config folder to make it a more high level bootstrap. 480 | 481 | 482 | #### Breaking Changes 483 | 484 | * Deselecting ngRoute adds controller and 485 | ng-include to index.html 486 | ([a358c1ae](http://github.com/DaftMonk/generator-angular-fullstack/commit/a358c1ae69bff6a7708ea0a77248698f931f2e4d)) 487 | * `--minsafe` flag is now deprecated. 488 | * `grunt server` is now deprecated. Use `grunt serve` instead 489 | 490 | 491 | ### v1.0.1 (2013-11-27) 492 | 493 | 494 | #### Bug Fixes 495 | 496 | * **coffee:** updated coffescript templates to point to partials ([f98e84ef](http://github.com/DaftMonk/generator-angular-fullstack/commit/f98e84efdd88243cff1ea449dc3a8e9dbebb7ccc)) 497 | 498 | 499 | ## v1.0.0 (2013-11-26) 500 | 501 | 502 | #### Bug Fixes 503 | 504 | * **build:** 505 | * use test-specifc jshintrc ([c00c091b](http://github.com/DaftMonk/generator-angular-fullstack/commit/c00c091bdca2b55685d81a2b84b002d73aacbdcc)) 506 | * add webapp upstream features and better coffee ([c23acebb](http://github.com/DaftMonk/generator-angular-fullstack/commit/c23acebbd8fabd391bfeee0d424f26e59f756a03)) 507 | * use grunt-newer for styles and jshint ([b1eeb68a](http://github.com/DaftMonk/generator-angular-fullstack/commit/b1eeb68a8290aee930887fc473034ee7f8e2ccc3)) 508 | * standardize comments and comment out uglify:dist ([d5d3e458](http://github.com/DaftMonk/generator-angular-fullstack/commit/d5d3e458e70d054707c70d058454fdd3d94070fe), closes [#455](http://github.com/DaftMonk/generator-angular-fullstack/issues/455)) 509 | * **deps:** upgrade dependencies ([3a57216f](http://github.com/DaftMonk/generator-angular-fullstack/commit/3a57216ff9e3192db3804634f360253e9fcce69d)) 510 | * **gen:** 511 | * Fixed jshint errors that were breaking grunt task ([c6ae81c8](http://github.com/DaftMonk/generator-angular-fullstack/commit/c6ae81c8110ee59c9099740ea2f90b0d08b810d3)) 512 | 513 | #### Features 514 | 515 | * **app:** 516 | * Separate client and server watchers ([0ff8ffb1](http://github.com/DaftMonk/generator-angular-fullstack/commit/0ff8ffb105a2eb1cd079fabafc5a6517d62e861d)) 517 | * imagemin handles gifs ([9341eb9b](http://github.com/DaftMonk/generator-angular-fullstack/commit/9341eb9b710b95c95407dc54ed4af6aa4a496426)) 518 | * **gen:** 519 | * added support for jade templates ([24a13bfe](http://github.com/DaftMonk/generator-angular-fullstack/commit/24a13bfea0e4a9633f33e37df4a4710fecdea937)) 520 | * Support for server rendering and Angular's HTML5 mode ([5ccdeb7a](http://github.com/DaftMonk/generator-angular-fullstack/commit/5ccdeb7a5543e35c000a54dfc15289004e406866), closes [#18](http://github.com/DaftMonk/generator-angular-fullstack/issues/18), [#17](http://github.com/DaftMonk/generator-angular-fullstack/issues/17)) 521 | * add image file as example ([b161c298](http://github.com/DaftMonk/generator-angular-fullstack/commit/b161c2982d86df1bb3de44cd9fa8aee05fc66ff3)) 522 | * **build:** 523 | * compile only changed coffeescript files in watch task ([4196e379](http://github.com/DaftMonk/generator-angular-fullstack/commit/4196e37912993ae37812fa19d9378d8b8d2cc9da), closes [#425](http://github.com/DaftMonk/generator-angular-fullstack/issues/425)) 524 | * deprecate server in favor of serve ([ef056319](http://github.com/DaftMonk/generator-angular-fullstack/commit/ef0563192a9e3fc834ae97e7ec68470bcfdf56eb)) 525 | 526 | #### Breaking Changes 527 | 528 | * `angular-fullstack:route` 529 | * `angular-fullstack:view` 530 | 531 | Will now generate views and routes in the views/partials folder. 532 | 533 | **For existing projects:** 534 | 535 | For generating routes and views, install generator-angular and use it's sub-generators. 536 | 537 | They are exactly the same as the generators that you have been using. Example usage: `yo angular:route helloworld`. 538 | 539 | **For New projects:** 540 | 541 | Continue to use angular-fullstack route and view sub-generators. 542 | 543 | The reason for this change in folder structure was to support server page rendering. 544 | 545 | 546 | Closes #18, #17 547 | ([5ccdeb7a](http://github.com/DaftMonk/generator-angular-fullstack/commit/5ccdeb7a5543e35c000a54dfc15289004e406866)) 548 | 549 | * `grunt server` is being deprecated 550 | ([ef056319](http://github.com/DaftMonk/generator-angular-fullstack/commit/ef0563192a9e3fc834ae97e7ec68470bcfdf56eb)) 551 | 552 | 553 | ## v0.2.0 (2013-11-13) 554 | 555 | 556 | #### Bug Fixes 557 | 558 | * **bootstrap:** some plugins have ordering dependencies ([3da4a130](http://github.com/DaftMonk/generator-angular-fullstack/commit/3da4a1301e0b744c7a6054fafff26fff16b6442b)) 559 | * **build:** only include sass if sass is selected ([597b8b5c](http://github.com/DaftMonk/generator-angular-fullstack/commit/597b8b5cfab77b78e7f6091140beda2eeee0ed54), closes [#449](http://github.com/DaftMonk/generator-angular-fullstack/issues/449)) 560 | * **css:** remove merge conflicts ([d558af35](http://github.com/DaftMonk/generator-angular-fullstack/commit/d558af351c8a531132ce064a461bc038e0710b25)) 561 | * **gen:** 562 | * script paths use forward slashes ([40aa61dc](http://github.com/DaftMonk/generator-angular-fullstack/commit/40aa61dcc1bf31918bea3d2ce9a84c93554aa64a), closes [#410](http://github.com/DaftMonk/generator-angular-fullstack/issues/410)) 563 | * remove extra "App" from service spec files ([4053f11f](http://github.com/DaftMonk/generator-angular-fullstack/commit/4053f11f800280569f5b7396ad015f0a6bcc7b49)) 564 | * options should have descriptions ([da001832](http://github.com/DaftMonk/generator-angular-fullstack/commit/da001832dbdb268b3bf38f359c72b40c401273e4)) 565 | * **styles:** update path to icon images ([8daad4f2](http://github.com/DaftMonk/generator-angular-fullstack/commit/8daad4f2de9dbde4fcc810527da7c9607e1db8d4)) 566 | * **template:** remove redundant closing tag ([d1e560e0](http://github.com/DaftMonk/generator-angular-fullstack/commit/d1e560e0675ecb70e6c4b59cf4de9df461434a31), closes [#441](http://github.com/DaftMonk/generator-angular-fullstack/issues/441)) 567 | 568 | 569 | #### Features 570 | 571 | * **app:** 572 | * run unit tests when test scripts are changed ([94af0b51](http://github.com/DaftMonk/generator-angular-fullstack/commit/94af0b510982b05c5a1939966e96aeccce087500)) 573 | * update to angular 1.2.0 ([77082c6b](http://github.com/DaftMonk/generator-angular-fullstack/commit/77082c6b8d1dda76579f1970a270dffc359f027f)) 574 | * reload grunt server when gruntfile is updated ([50c6abb9](http://github.com/DaftMonk/generator-angular-fullstack/commit/50c6abb9cce09a149253ceb8496feca813a71136)) 575 | * upgrade to Bootstrap 3.0.1 ([59f4b1ba](http://github.com/DaftMonk/generator-angular-fullstack/commit/59f4b1ba73842b758174ad44a7da60af4f4db63f)) 576 | * **gen:** 577 | * allow app names to have custom suffix ([09f0f7b3](http://github.com/DaftMonk/generator-angular-fullstack/commit/09f0f7b3a8c3264b7527bc9fed8c709becec99eb)) 578 | 579 | 580 | 581 | ## v0.1.0 (2013-11-12) 582 | 583 | #### Features 584 | 585 | * **gen:** include MongoDB as an option When selected, sets up database with Mongoose. Repl ([280cc84d](http://github.com/DaftMonk/generator-angular-fullstack/commit/280cc84d735c60b1c261540dceda34dd7f91c93c), closes [#2](http://github.com/DaftMonk/generator-angular-fullstack/issues/2)) -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var markdown = require('marked'); 3 | var semver = require('semver'); 4 | var _s = require('underscore.string'); 5 | var shell = require('shelljs'); 6 | var process = require('child_process'); 7 | var Q = require('q'); 8 | var helpers = require('yeoman-generator').test; 9 | var fs = require('fs-extra'); 10 | var path = require('path'); 11 | 12 | module.exports = function (grunt) { 13 | require('load-grunt-tasks')(grunt); 14 | 15 | grunt.initConfig({ 16 | config: { 17 | demo: 'demo' 18 | }, 19 | pkg: grunt.file.readJSON('package.json'), 20 | changelog: { 21 | options: { 22 | dest: 'CHANGELOG.md', 23 | versionFile: 'package.json' 24 | } 25 | }, 26 | release: { 27 | options: { 28 | commitMessage: '<%= version %>', 29 | tagName: 'v<%= version %>', 30 | bump: false, // we have our own bump 31 | file: 'package.json' 32 | } 33 | }, 34 | stage: { 35 | options: { 36 | files: ['CHANGELOG.md'] 37 | } 38 | }, 39 | buildcontrol: { 40 | options: { 41 | dir: 'demo', 42 | commit: true, 43 | push: true, 44 | connectCommits: false, 45 | message: 'Built using Angular Fullstack v<%= pkg.version %> from commit %sourceCommit%' 46 | }, 47 | release: { 48 | options: { 49 | remote: 'origin', 50 | branch: 'master' 51 | } 52 | } 53 | }, 54 | jshint: { 55 | options: { 56 | curly: false, 57 | node: true 58 | }, 59 | all: ['Gruntfile.js', '*/index.js'] 60 | }, 61 | mochaTest: { 62 | test: { 63 | src: [ 64 | 'test/*.js' 65 | ], 66 | options: { 67 | reporter: 'spec', 68 | timeout: 120000 69 | } 70 | } 71 | }, 72 | clean: { 73 | demo: { 74 | files: [{ 75 | dot: true, 76 | src: [ 77 | '<%= config.demo %>/*', 78 | '!<%= config.demo %>/readme.md', 79 | '!<%= config.demo %>/node_modules', 80 | '!<%= config.demo %>/.git', 81 | '!<%= config.demo %>/dist' 82 | ] 83 | }] 84 | } 85 | } 86 | }); 87 | 88 | grunt.registerTask('bump', 'bump manifest version', function (type) { 89 | var options = this.options({ 90 | file: grunt.config('pkgFile') || 'package.json' 91 | }); 92 | 93 | function setup(file, type) { 94 | var pkg = grunt.file.readJSON(file); 95 | var newVersion = pkg.version = semver.inc(pkg.version, type || 'patch'); 96 | return { 97 | file: file, 98 | pkg: pkg, 99 | newVersion: newVersion 100 | }; 101 | } 102 | 103 | var config = setup(options.file, type); 104 | grunt.file.write(config.file, JSON.stringify(config.pkg, null, ' ') + '\n'); 105 | grunt.log.ok('Version bumped to ' + config.newVersion); 106 | }); 107 | 108 | grunt.registerTask('stage', 'git add files before running the release task', function () { 109 | var files = this.options().files; 110 | grunt.util.spawn({ 111 | cmd: process.platform === 'win32' ? 'git.cmd' : 'git', 112 | args: ['add'].concat(files) 113 | }, grunt.task.current.async()); 114 | }); 115 | 116 | grunt.registerTask('generateDemo', 'generate demo', function () { 117 | var done = this.async(); 118 | 119 | shell.mkdir(grunt.config('config').demo); 120 | shell.cd(grunt.config('config').demo); 121 | 122 | Q() 123 | .then(generateDemo) 124 | .then(function() { 125 | shell.cd('../'); 126 | }) 127 | .catch(function(msg){ 128 | grunt.fail.warn(msg || 'failed to generate demo') 129 | }) 130 | .finally(done); 131 | 132 | function generateDemo() { 133 | var deferred = Q.defer(); 134 | var options = { 135 | script: 'js', 136 | markup: 'html', 137 | stylesheet: 'sass', 138 | router: 'uirouter', 139 | bootstrap: true, 140 | uibootstrap: true, 141 | mongoose: true, 142 | auth: true, 143 | oauth: ['googleAuth', 'twitterAuth'], 144 | socketio: true 145 | }; 146 | 147 | var deps = [ 148 | '../app', 149 | [ 150 | helpers.createDummyGenerator(), 151 | 'ng-component:app' 152 | ] 153 | ]; 154 | 155 | var gen = helpers.createGenerator('node-express-mongo:app', deps); 156 | 157 | helpers.mockPrompt(gen, options); 158 | gen.run({}, function () { 159 | deferred.resolve(); 160 | }); 161 | 162 | return deferred.promise; 163 | } 164 | }); 165 | 166 | grunt.registerTask('releaseDemoBuild', 'builds and releases demo', function () { 167 | var done = this.async(); 168 | 169 | shell.cd(grunt.config('config').demo); 170 | 171 | Q() 172 | .then(gruntBuild) 173 | .then(gruntRelease) 174 | .then(function() { 175 | shell.cd('../'); 176 | }) 177 | .catch(function(msg){ 178 | grunt.fail.warn(msg || 'failed to release demo') 179 | }) 180 | .finally(done); 181 | 182 | function run(cmd) { 183 | var deferred = Q.defer(); 184 | var generator = shell.exec(cmd, {async:true}); 185 | generator.stdout.on('data', function (data) { 186 | grunt.verbose.writeln(data); 187 | }); 188 | generator.on('exit', function (code) { 189 | deferred.resolve(); 190 | }); 191 | 192 | return deferred.promise; 193 | } 194 | 195 | function gruntBuild() { 196 | return run('grunt'); 197 | } 198 | 199 | function gruntRelease() { 200 | return run('grunt buildcontrol:heroku'); 201 | } 202 | }); 203 | 204 | grunt.registerTask('updateFixtures', 'updates package and bower fixtures', function() { 205 | var done = this.async(); 206 | var packageJson = fs.readFileSync(path.resolve('app/templates/_package.json'), 'utf8'); 207 | var bowerJson = fs.readFileSync(path.resolve('app/templates/_bower.json'), 'utf8'); 208 | 209 | // replace package name 210 | packageJson = packageJson.replace(/"name": "<%(.*)%>"/g, '"name": "tempApp"'); 211 | packageJson = packageJson.replace(/<%(.*)%>/g, ''); 212 | 213 | // remove all ejs conditionals 214 | bowerJson = bowerJson.replace(/"name": "<%(.*)%>"/g, '"name": "tempApp"'); 215 | bowerJson = bowerJson.replace(/<%(.*)%>/g, ''); 216 | 217 | // save files 218 | fs.writeFile(path.resolve(__dirname + '/test/fixtures/package.json'), packageJson, function() { 219 | fs.writeFile(path.resolve(__dirname + '/test/fixtures/bower.json'), bowerJson, function() { 220 | done(); 221 | }); 222 | }); 223 | }); 224 | 225 | grunt.registerTask('installFixtures', 'install package and bower fixtures', function() { 226 | var done = this.async(); 227 | 228 | shell.cd('test/fixtures'); 229 | grunt.log.ok('installing npm dependencies for generated app'); 230 | process.exec('npm install --quiet', {cwd: '../fixtures'}, function (error, stdout, stderr) { 231 | 232 | grunt.log.ok('installing bower dependencies for generated app'); 233 | process.exec('bower install', {cwd: '../fixtures'}, function (error, stdout, stderr) { 234 | shell.cd('../../'); 235 | done(); 236 | }) 237 | }); 238 | }); 239 | 240 | grunt.registerTask('test', [ 241 | 'updateFixtures', 242 | 'installFixtures', 243 | 'mochaTest' 244 | ]); 245 | 246 | grunt.registerTask('demo', [ 247 | 'clean:demo', 248 | 'generateDemo' 249 | ]); 250 | 251 | grunt.registerTask('releaseDemo', [ 252 | 'demo', 253 | 'releaseDemoBuild', 254 | 'buildcontrol:release' 255 | ]); 256 | 257 | //grunt.registerTask('default', ['bump', 'changelog', 'stage', 'release']); 258 | }; 259 | -------------------------------------------------------------------------------- /app/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | Creates a MEN (Mongo, Express, Node) Stack app 3 | 4 | Example: 5 | yo node-express-mongo 6 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | var util = require('util'); 5 | var genUtils = require('../util.js'); 6 | var yeoman = require('yeoman-generator'); 7 | var chalk = require('chalk'); 8 | var wiredep = require('wiredep'); 9 | 10 | var AngularFullstackGenerator = yeoman.generators.Base.extend({ 11 | 12 | init: function () { 13 | this.argument('name', { type: String, required: false }); 14 | this.appname = this.name || path.basename(process.cwd()); 15 | this.appname = this._.camelize(this._.slugify(this._.humanize(this.appname))); 16 | 17 | this.option('app-suffix', { 18 | desc: 'Allow a custom suffix to be added to the module name', 19 | type: String, 20 | required: 'false' 21 | }); 22 | this.scriptAppName = this.appname + genUtils.appName(this); 23 | this.appPath = this.env.options.appPath; 24 | this.pkg = require('../package.json'); 25 | 26 | this.filters = {}; 27 | }, 28 | 29 | info: function () { 30 | this.log(this.yeoman); 31 | this.log('Out of the box I create an AngularJS app with an Express server.\n'); 32 | }, 33 | 34 | checkForConfig: function() { 35 | var cb = this.async(); 36 | 37 | if(this.config.get('filters')) { 38 | this.prompt([{ 39 | type: "confirm", 40 | name: "skipConfig", 41 | message: "Existing .yo-rc configuration found, would you like to use it?", 42 | default: true, 43 | }], function (answers) { 44 | this.skipConfig = answers.skipConfig; 45 | 46 | // NOTE: temp(?) fix for #403 47 | if(typeof this.oauth==='undefined') { 48 | var strategies = Object.keys(this.filters).filter(function(key) { 49 | return key.match(/Auth$/) && key; 50 | }); 51 | 52 | if(strategies.length) this.config.set('oauth', true); 53 | } 54 | 55 | cb(); 56 | }.bind(this)); 57 | } else { 58 | cb(); 59 | } 60 | }, 61 | 62 | clientPrompts: function() { 63 | var dontIncludeClient = true; 64 | if (dontIncludeClient) return; 65 | if(this.skipConfig) return; 66 | var cb = this.async(); 67 | 68 | this.log('# Client\n'); 69 | 70 | this.prompt([{ 71 | type: "list", 72 | name: "script", 73 | message: "What would you like to write scripts with?", 74 | choices: [ "JavaScript", "CoffeeScript"], 75 | filter: function( val ) { 76 | var filterMap = { 77 | 'JavaScript': 'js', 78 | 'CoffeeScript': 'coffee' 79 | }; 80 | 81 | return filterMap[val]; 82 | } 83 | }, { 84 | type: "confirm", 85 | name: "babel", 86 | message: "Would you like to use Javascript ES6 in your client by preprocessing it with Babel?", 87 | when: function (answers) { 88 | return answers.script === 'js'; 89 | } 90 | }, { 91 | type: "list", 92 | name: "markup", 93 | message: "What would you like to write markup with?", 94 | choices: [ "HTML", "Jade"], 95 | filter: function( val ) { return val.toLowerCase(); } 96 | }, { 97 | type: "list", 98 | name: "stylesheet", 99 | default: 1, 100 | message: "What would you like to write stylesheets with?", 101 | choices: [ "CSS", "Sass", "Stylus", "Less"], 102 | filter: function( val ) { return val.toLowerCase(); } 103 | }, { 104 | type: "list", 105 | name: "router", 106 | default: 1, 107 | message: "What Angular router would you like to use?", 108 | choices: [ "ngRoute", "uiRouter"], 109 | filter: function( val ) { return val.toLowerCase(); } 110 | }, { 111 | type: "confirm", 112 | name: "bootstrap", 113 | message: "Would you like to include Bootstrap?" 114 | }, { 115 | type: "confirm", 116 | name: "uibootstrap", 117 | message: "Would you like to include UI Bootstrap?", 118 | when: function (answers) { 119 | return answers.bootstrap; 120 | } 121 | }], function (answers) { 122 | 123 | this.filters.babel = !!answers.babel; 124 | if(this.filters.babel){ this.filters.js = true; } 125 | this.filters[answers.script] = true; 126 | this.filters[answers.markup] = true; 127 | this.filters[answers.stylesheet] = true; 128 | this.filters[answers.router] = true; 129 | this.filters.bootstrap = !!answers.bootstrap; 130 | this.filters.uibootstrap = !!answers.uibootstrap; 131 | cb(); 132 | }.bind(this)); 133 | }, 134 | 135 | serverPrompts: function() { 136 | if(this.skipConfig) return; 137 | var cb = this.async(); 138 | var self = this; 139 | 140 | this.log('\n# Server\n'); 141 | 142 | this.prompt([{ 143 | type: "confirm", 144 | name: "mongoose", 145 | message: "Would you like to use mongoDB with Mongoose for data modeling?" 146 | }, { 147 | type: "confirm", 148 | name: "auth", 149 | message: "Would you scaffold out an authentication boilerplate?", 150 | when: function (answers) { 151 | return answers.mongoose; 152 | } 153 | }, { 154 | type: 'checkbox', 155 | name: 'oauth', 156 | message: 'Would you like to include additional oAuth strategies?', 157 | when: function (answers) { 158 | return answers.auth; 159 | }, 160 | choices: [ 161 | { 162 | value: 'googleAuth', 163 | name: 'Google', 164 | checked: false 165 | }, 166 | { 167 | value: 'facebookAuth', 168 | name: 'Facebook', 169 | checked: false 170 | }, 171 | { 172 | value: 'twitterAuth', 173 | name: 'Twitter', 174 | checked: false 175 | } 176 | ] 177 | }, { 178 | type: "confirm", 179 | name: "socketio", 180 | message: "Would you like to use socket.io?", 181 | // to-do: should not be dependent on mongoose 182 | when: function (answers) { 183 | return answers.mongoose; 184 | }, 185 | default: true 186 | }], function (answers) { 187 | if(answers.socketio) this.filters.socketio = true; 188 | if(answers.mongoose) this.filters.mongoose = true; 189 | if(answers.auth) this.filters.auth = true; 190 | if(answers.oauth) { 191 | if(answers.oauth.length) this.filters.oauth = true; 192 | answers.oauth.forEach(function(oauthStrategy) { 193 | this.filters[oauthStrategy] = true; 194 | }.bind(this)); 195 | } 196 | 197 | cb(); 198 | }.bind(this)); 199 | }, 200 | 201 | saveSettings: function() { 202 | if(this.skipConfig) return; 203 | this.config.set('insertRoutes', true); 204 | this.config.set('registerRoutesFile', 'server/routes.js'); 205 | this.config.set('routesNeedle', '// Insert routes below'); 206 | 207 | this.config.set('insertSeed', true); 208 | this.config.set('registerSeedFile', 'server/config/seed.js'); 209 | this.config.set('seedModelNeedle', '// Insert seed models below'); 210 | this.config.set('seedDataNeedle', '// Insert seed data below'); 211 | this.config.set('seedInsertNeedle', '// Insert seed inserts below'); 212 | 213 | this.config.set('routesBase', '/api/'); 214 | this.config.set('pluralizeRoutes', true); 215 | 216 | this.config.set('insertSockets', true); 217 | this.config.set('registerSocketsFile', 'server/config/socketio.js'); 218 | this.config.set('socketsNeedle', '// Insert sockets below'); 219 | 220 | this.config.set('filters', this.filters); 221 | this.config.forceSave(); 222 | }, 223 | 224 | compose: function() { 225 | if(this.skipConfig) return; 226 | var appPath = 'client/app/'; 227 | var extensions = []; 228 | var filters = []; 229 | 230 | if(this.filters.ngroute) filters.push('ngroute'); 231 | if(this.filters.uirouter) filters.push('uirouter'); 232 | if(this.filters.babel) extensions.push('babel'); 233 | if(this.filters.coffee) extensions.push('coffee'); 234 | if(this.filters.js) extensions.push('js'); 235 | if(this.filters.html) extensions.push('html'); 236 | if(this.filters.jade) extensions.push('jade'); 237 | if(this.filters.css) extensions.push('css'); 238 | if(this.filters.stylus) extensions.push('styl'); 239 | if(this.filters.sass) extensions.push('scss'); 240 | if(this.filters.less) extensions.push('less'); 241 | 242 | this.composeWith('ng-component', { 243 | options: { 244 | 'routeDirectory': appPath, 245 | 'directiveDirectory': appPath, 246 | 'filterDirectory': appPath, 247 | 'serviceDirectory': appPath, 248 | 'filters': filters, 249 | 'extensions': extensions, 250 | 'basePath': 'client' 251 | } 252 | }, { local: require.resolve('generator-ng-component/app/index.js') }); 253 | }, 254 | 255 | ngModules: function() { 256 | this.filters = this._.defaults(this.config.get('filters'), { 257 | bootstrap: true, 258 | uibootstrap: true 259 | }); 260 | 261 | var angModules = [ 262 | "'ngCookies'", 263 | "'ngResource'", 264 | "'ngSanitize'" 265 | ]; 266 | if(this.filters.ngroute) angModules.push("'ngRoute'"); 267 | if(this.filters.socketio) angModules.push("'btford.socket-io'"); 268 | if(this.filters.uirouter) angModules.push("'ui.router'"); 269 | if(this.filters.uibootstrap) angModules.push("'ui.bootstrap'"); 270 | 271 | this.angularModules = "\n " + angModules.join(",\n ") +"\n"; 272 | }, 273 | 274 | generate: function() { 275 | this.sourceRoot(path.join(__dirname, './templates')); 276 | genUtils.processDirectory(this, '.', '.'); 277 | }, 278 | 279 | end: function() { 280 | this.installDependencies({ 281 | skipInstall: this.options['skip-install'] 282 | }); 283 | } 284 | }); 285 | 286 | module.exports = AngularFullstackGenerator; 287 | -------------------------------------------------------------------------------- /app/templates/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | - '0.11' 5 | before_script: 6 | - npm install -g bower grunt-cli<% if (filters.sass) { %> 7 | - gem install sass<% } %> 8 | - bower install 9 | services: mongodb -------------------------------------------------------------------------------- /app/templates/Gruntfile.js: -------------------------------------------------------------------------------- 1 | // Generated on <%= (new Date).toISOString().split('T')[0] %> using <%= pkg.name %> <%= pkg.version %> 2 | 'use strict'; 3 | 4 | module.exports = function (grunt) { 5 | var localConfig; 6 | try { 7 | localConfig = require('./server/config/local.env'); 8 | } catch(e) { 9 | localConfig = {}; 10 | } 11 | 12 | // Load grunt tasks automatically, when needed 13 | require('jit-grunt')(grunt, { 14 | express: 'grunt-express-server', 15 | protractor: 'grunt-protractor-runner', 16 | buildcontrol: 'grunt-build-control' 17 | }); 18 | 19 | // Time how long tasks take. Can help when optimizing build times 20 | require('time-grunt')(grunt); 21 | 22 | // Define the configuration for all the tasks 23 | grunt.initConfig({ 24 | 25 | // Project settings 26 | pkg: grunt.file.readJSON('package.json'), 27 | yeoman: { 28 | // configurable paths 29 | dist: 'dist' 30 | }, 31 | express: { 32 | options: { 33 | port: process.env.PORT || 9000 34 | }, 35 | dev: { 36 | options: { 37 | script: 'server/app.js', 38 | debug: true 39 | } 40 | }, 41 | prod: { 42 | options: { 43 | script: 'server/app.js' 44 | } 45 | } 46 | }, 47 | open: { 48 | server: { 49 | url: 'http://localhost:<%%= express.options.port %>' 50 | } 51 | }, 52 | watch: { 53 | mochaTest: { 54 | files: ['server/**/*.spec.js'], 55 | tasks: ['env:test', 'mochaTest'] 56 | }, 57 | jsTest: { 58 | files: [], 59 | tasks: ['newer:jshint:all', 'karma'] 60 | }, 61 | gruntfile: { 62 | files: ['Gruntfile.js'] 63 | }, 64 | livereload: { 65 | files: [ 66 | '*' 67 | ], 68 | options: { 69 | livereload: true 70 | } 71 | }, 72 | express: { 73 | files: [ 74 | 'server/**/*.{js,json}' 75 | ], 76 | tasks: ['express:dev', 'wait'], 77 | options: { 78 | livereload: true, 79 | nospawn: true //Without this option specified express won't be reloaded 80 | } 81 | } 82 | }, 83 | 84 | // Make sure code styles are up to par and there are no obvious mistakes 85 | jshint: { 86 | options: { 87 | reporter: require('jshint-stylish') 88 | }, 89 | server: { 90 | options: { 91 | jshintrc: 'server/.jshintrc' 92 | }, 93 | src: [ 94 | 'server/**/*.js', 95 | '!server/**/*.spec.js' 96 | ] 97 | }, 98 | serverTest: { 99 | options: { 100 | jshintrc: 'server/.jshintrc-spec' 101 | }, 102 | src: ['server/**/*.spec.js'] 103 | }, 104 | all: [], 105 | test: { 106 | src: [] 107 | } 108 | }, 109 | 110 | // Empties folders to start fresh 111 | clean: { 112 | dist: { 113 | files: [{ 114 | dot: true, 115 | src: [ 116 | '.tmp', 117 | '<%%= yeoman.dist %>/*', 118 | '!<%%= yeoman.dist %>/.git*', 119 | '!<%%= yeoman.dist %>/.openshift', 120 | '!<%%= yeoman.dist %>/Procfile' 121 | ] 122 | }] 123 | }, 124 | server: '.tmp' 125 | }, 126 | 127 | // Debugging with node inspector 128 | 'node-inspector': { 129 | custom: { 130 | options: { 131 | 'web-host': 'localhost' 132 | } 133 | } 134 | }, 135 | 136 | // Use nodemon to run server in debug mode with an initial breakpoint 137 | nodemon: { 138 | debug: { 139 | script: 'server/app.js', 140 | options: { 141 | nodeArgs: ['--debug-brk'], 142 | env: { 143 | PORT: process.env.PORT || 9000 144 | }, 145 | callback: function (nodemon) { 146 | nodemon.on('log', function (event) { 147 | console.log(event.colour); 148 | }); 149 | 150 | // opens browser on initial server start 151 | nodemon.on('config:update', function () { 152 | setTimeout(function () { 153 | require('open')('http://localhost:8080/debug?port=5858'); 154 | }, 500); 155 | }); 156 | } 157 | } 158 | } 159 | }, 160 | 161 | buildcontrol: { 162 | options: { 163 | dir: 'dist', 164 | commit: true, 165 | push: true, 166 | connectCommits: false, 167 | message: 'Built %sourceName% from commit %sourceCommit% on branch %sourceBranch%' 168 | }, 169 | heroku: { 170 | options: { 171 | remote: 'heroku', 172 | branch: 'master' 173 | } 174 | }, 175 | openshift: { 176 | options: { 177 | remote: 'openshift', 178 | branch: 'master' 179 | } 180 | } 181 | }, 182 | 183 | // Run some tasks in parallel to speed up the build process 184 | concurrent: { 185 | debug: { 186 | tasks: [ 187 | 'nodemon', 188 | 'node-inspector' 189 | ], 190 | options: { 191 | logConcurrentOutput: true 192 | } 193 | } 194 | }, 195 | 196 | // Test settings 197 | karma: { 198 | unit: { 199 | configFile: 'karma.conf.js', 200 | singleRun: true 201 | } 202 | }, 203 | 204 | mochaTest: { 205 | options: { 206 | reporter: 'spec' 207 | }, 208 | src: ['server/**/*.spec.js'] 209 | }, 210 | 211 | protractor: { 212 | options: { 213 | configFile: 'protractor.conf.js' 214 | }, 215 | chrome: { 216 | options: { 217 | args: { 218 | browser: 'chrome' 219 | } 220 | } 221 | } 222 | }, 223 | 224 | env: { 225 | test: { 226 | NODE_ENV: 'test' 227 | }, 228 | prod: { 229 | NODE_ENV: 'production' 230 | }, 231 | all: localConfig 232 | } 233 | }); 234 | 235 | // Used for delaying livereload until after server has restarted 236 | grunt.registerTask('wait', function () { 237 | grunt.log.ok('Waiting for server reload...'); 238 | 239 | var done = this.async(); 240 | 241 | setTimeout(function () { 242 | grunt.log.writeln('Done waiting!'); 243 | done(); 244 | }, 1500); 245 | }); 246 | 247 | grunt.registerTask('express-keepalive', 'Keep grunt running', function() { 248 | this.async(); 249 | }); 250 | 251 | grunt.registerTask('serve', function (target) { 252 | if (target === 'dist') { 253 | return grunt.task.run([ 254 | 'clean:server', 255 | 'env:all', 256 | 'env:prod', 257 | 'express:prod', 258 | 'wait', 259 | 'open', 260 | 'express-keepalive' 261 | ]); 262 | } 263 | 264 | if (target === 'debug') { 265 | return grunt.task.run([ 266 | 'clean:server', 267 | 'env:all', 268 | 'concurrent:debug' 269 | ]); 270 | } 271 | 272 | grunt.task.run([ 273 | 'clean:server', 274 | 'env:all', 275 | 'express:dev', 276 | 'wait', 277 | 'open', 278 | 'watch' 279 | ]); 280 | }); 281 | 282 | grunt.registerTask('server', function () { 283 | grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.'); 284 | grunt.task.run(['serve']); 285 | }); 286 | 287 | grunt.registerTask('test', function(target) { 288 | if (target === 'server') { 289 | return grunt.task.run([ 290 | 'env:all', 291 | 'env:test', 292 | 'mochaTest' 293 | ]); 294 | } 295 | 296 | else if (target === 'client') { 297 | return grunt.task.run([ 298 | 'clean:server', 299 | 'env:all', 300 | 'karma' 301 | ]); 302 | } 303 | 304 | else if (target === 'e2e') { 305 | return grunt.task.run([ 306 | 'clean:server', 307 | 'env:all', 308 | 'env:test', 309 | 'express:dev', 310 | 'protractor' 311 | ]); 312 | } 313 | 314 | else grunt.task.run([ 315 | 'test:server', 316 | 'test:client' 317 | ]); 318 | }); 319 | 320 | grunt.registerTask('build', [ 321 | 'clean:dist' 322 | ]); 323 | 324 | grunt.registerTask('default', [ 325 | 'newer:jshint', 326 | 'test', 327 | 'build' 328 | ]); 329 | }; 330 | -------------------------------------------------------------------------------- /app/templates/_.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | /server/config/local.env.js 4 | npm-debug.log 5 | -------------------------------------------------------------------------------- /app/templates/_package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= _.slugify(_.humanize(appname)) %>", 3 | "version": "0.0.0", 4 | "main": "server/app.js", 5 | "dependencies": { 6 | "express": "~4.9.0", 7 | "morgan": "~1.0.0", 8 | "body-parser": "~1.5.0", 9 | "method-override": "~1.0.0", 10 | "cookie-parser": "~1.0.1", 11 | "express-session": "~1.0.2", 12 | "errorhandler": "~1.0.0", 13 | "compression": "~1.0.1", 14 | "lodash": "~2.4.1",<% if(filters.mongoose) { %> 15 | "mongoose": "~4.0.3",<% } %><% if(filters.auth) { %> 16 | "jsonwebtoken": "^5.0.0", 17 | "express-jwt": "^3.0.0", 18 | "passport": "~0.2.0", 19 | "passport-local": "~0.1.6",<% } %><% if(filters.facebookAuth) { %> 20 | "passport-facebook": "latest",<% } %><% if(filters.twitterAuth) { %> 21 | "passport-twitter": "latest",<% } %><% if(filters.googleAuth) { %> 22 | "passport-google-oauth": "latest",<% } %> 23 | "composable-middleware": "^0.3.0", 24 | "connect-mongo": "^0.8.1"<% if(filters.socketio) { %>, 25 | "socket.io": "^1.0.6", 26 | "socket.io-client": "^1.0.6", 27 | "socketio-jwt": "^3.0.0"<% } %> 28 | }, 29 | "devDependencies": { 30 | "grunt": "~0.4.4", 31 | "grunt-autoprefixer": "~0.7.2", 32 | "grunt-wiredep": "~1.8.0", 33 | "grunt-concurrent": "~0.5.0", 34 | "grunt-contrib-clean": "~0.5.0", 35 | "grunt-contrib-concat": "~0.4.0", 36 | "grunt-contrib-copy": "~0.5.0", 37 | "grunt-contrib-watch": "~0.6.1", 38 | "grunt-google-cdn": "~0.4.0", 39 | "grunt-newer": "~0.7.0", 40 | "grunt-ng-annotate": "^0.2.3", 41 | "grunt-rev": "~0.1.0", 42 | "grunt-svgmin": "~0.4.0", 43 | "grunt-usemin": "~2.1.1", 44 | "grunt-env": "~0.4.1", 45 | "grunt-node-inspector": "~0.1.5", 46 | "grunt-nodemon": "~0.2.0", 47 | "grunt-angular-templates": "^0.5.4", 48 | "grunt-dom-munger": "^3.4.0", 49 | "grunt-protractor-runner": "^1.1.0", 50 | "grunt-injector": "~0.5.4", 51 | "grunt-build-control": "~0.4.0", 52 | "grunt-mocha-test": "~0.10.2", 53 | "jit-grunt": "^0.5.0", 54 | "time-grunt": "~0.3.1", 55 | "grunt-express-server": "~0.4.17", 56 | "grunt-open": "~0.2.3", 57 | "open": "~0.0.4", 58 | "jshint-stylish": "~0.1.5", 59 | "connect-livereload": "~0.4.0", 60 | "requirejs": "~2.1.11", 61 | "supertest": "~0.11.0", 62 | "should": "~3.3.1" 63 | }, 64 | "engines": { 65 | "node": ">=0.10.0" 66 | }, 67 | "scripts": { 68 | "start": "node server/app.js", 69 | "test": "grunt test" 70 | }, 71 | "private": true 72 | } 73 | -------------------------------------------------------------------------------- /app/templates/server/api/thing/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var express = require('express'); 4 | var controller = require('./thing.controller'); 5 | 6 | var router = express.Router(); 7 | 8 | router.get('/', controller.index);<% if(filters.mongoose) { %> 9 | router.get('/:id', controller.show); 10 | router.post('/', controller.create); 11 | router.put('/:id', controller.update); 12 | router.patch('/:id', controller.update); 13 | router.delete('/:id', controller.destroy);<% } %> 14 | 15 | module.exports = router; -------------------------------------------------------------------------------- /app/templates/server/api/thing/thing.controller.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Using Rails-like standard naming convention for endpoints. 3 | * GET /things -> index 4 | * POST /things -> create 5 | * GET /things/:id -> show 6 | * PUT /things/:id -> update 7 | * DELETE /things/:id -> destroy 8 | */ 9 | 10 | 'use strict'; 11 | 12 | var _ = require('lodash');<% if (filters.mongoose) { %> 13 | var Thing = require('./thing.model');<% } %> 14 | 15 | // Get list of things 16 | exports.index = function(req, res) {<% if (!filters.mongoose) { %> 17 | res.json([ 18 | { 19 | name : 'Development Tools', 20 | info : 'Integration with popular tools such as Bower, Grunt, Karma, Mocha, JSHint, Node Inspector, Livereload, Protractor, Jade, Stylus, Sass, CoffeeScript, and Less.' 21 | }, { 22 | name : 'Server and Client integration', 23 | info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' 24 | }, { 25 | name : 'Smart Build System', 26 | info : 'Build system ignores `spec` files, allowing you to keep tests alongside code. Automatic injection of scripts and styles into your index.html' 27 | }, { 28 | name : 'Modular Structure', 29 | info : 'Best practice client and server structures allow for more code reusability and maximum scalability' 30 | }, { 31 | name : 'Optimized Build', 32 | info : 'Build process packs up your templates as a single JavaScript payload, minifies your scripts/css/images, and rewrites asset names for caching.' 33 | },{ 34 | name : 'Deployment Ready', 35 | info : 'Easily deploy your app to Heroku or Openshift with the heroku and openshift subgenerators' 36 | } 37 | ]);<% } %><% if (filters.mongoose) { %> 38 | Thing.find(function (err, things) { 39 | if(err) { return handleError(res, err); } 40 | return res.status(200).json(things); 41 | });<% } %> 42 | };<% if (filters.mongoose) { %> 43 | 44 | // Get a single thing 45 | exports.show = function(req, res) { 46 | Thing.findById(req.params.id, function (err, thing) { 47 | if(err) { return handleError(res, err); } 48 | if(!thing) { return res.status(404).send('Not Found'); } 49 | return res.json(thing); 50 | }); 51 | }; 52 | 53 | // Creates a new thing in the DB. 54 | exports.create = function(req, res) { 55 | Thing.create(req.body, function(err, thing) { 56 | if(err) { return handleError(res, err); } 57 | return res.status(201).json(thing); 58 | }); 59 | }; 60 | 61 | // Updates an existing thing in the DB. 62 | exports.update = function(req, res) { 63 | if(req.body._id) { delete req.body._id; } 64 | Thing.findById(req.params.id, function (err, thing) { 65 | if (err) { return handleError(res, err); } 66 | if(!thing) { return res.status(404).send('Not Found'); } 67 | var updated = _.merge(thing, req.body); 68 | updated.save(function (err) { 69 | if (err) { return handleError(res, err); } 70 | return res.status(200).json(thing); 71 | }); 72 | }); 73 | }; 74 | 75 | // Deletes a thing from the DB. 76 | exports.destroy = function(req, res) { 77 | Thing.findById(req.params.id, function (err, thing) { 78 | if(err) { return handleError(res, err); } 79 | if(!thing) { return res.status(404).send('Not Found'); } 80 | thing.remove(function(err) { 81 | if(err) { return handleError(res, err); } 82 | return res.status(204).send('No Content'); 83 | }); 84 | }); 85 | }; 86 | 87 | function handleError(res, err) { 88 | return res.status(500).send(err); 89 | }<% } %> -------------------------------------------------------------------------------- /app/templates/server/api/thing/thing.model(mongoose).js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var mongoose = require('mongoose'), 4 | Schema = mongoose.Schema; 5 | 6 | var ThingSchema = new Schema({ 7 | name: String, 8 | info: String, 9 | active: Boolean 10 | }); 11 | 12 | module.exports = mongoose.model('Thing', ThingSchema); -------------------------------------------------------------------------------- /app/templates/server/api/thing/thing.seed.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name" : "Development Tools", 4 | "info" : "Integration with popular tools such as Bower, Grunt, Karma, Mocha, JSHint, Node Inspector, Livereload, Protractor, Jade, Stylus, Sass, CoffeeScript, and Less." 5 | }, { 6 | "name" : "Server and Client integration", 7 | "info" : "Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node." 8 | }, { 9 | "name" : "Smart Build System", 10 | "info" : "Build system ignores `spec` files, allowing you to keep tests alongside code. Automatic injection of scripts and styles into your index.html" 11 | }, { 12 | "name" : "Modular Structure", 13 | "info" : "Best practice client and server structures allow for more code reusability and maximum scalability" 14 | }, { 15 | "name" : "Optimized Build", 16 | "info" : "Build process packs up your templates as a single JavaScript payload, minifies your scripts/css/images, and rewrites asset names for caching." 17 | },{ 18 | "name" : "Deployment Ready", 19 | "info" : "Easily deploy your app to Heroku or Openshift with the heroku and openshift subgenerators" 20 | } 21 | ] -------------------------------------------------------------------------------- /app/templates/server/api/thing/thing.socket(socketio).js: -------------------------------------------------------------------------------- 1 | /** 2 | * Broadcast updates to client when the model changes 3 | */ 4 | 5 | 'use strict'; 6 | 7 | var thing = require('./thing.model'); 8 | 9 | exports.register = function(socket) { 10 | thing.schema.post('save', function (doc) { 11 | onSave(socket, doc); 12 | }); 13 | thing.schema.post('remove', function (doc) { 14 | onRemove(socket, doc); 15 | }); 16 | } 17 | 18 | function onSave(socket, doc, cb) { 19 | socket.emit('thing:save', doc); 20 | } 21 | 22 | function onRemove(socket, doc, cb) { 23 | socket.emit('thing:remove', doc); 24 | } -------------------------------------------------------------------------------- /app/templates/server/api/thing/thing.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var should = require('should'); 4 | var app = require('../../app'); 5 | var request = require('supertest'); 6 | 7 | describe('GET /api/things', function() { 8 | 9 | it('should respond with JSON array', function(done) { 10 | request(app) 11 | .get('/api/things') 12 | .expect(200) 13 | .expect('Content-Type', /json/) 14 | .end(function(err, res) { 15 | if (err) return done(err); 16 | res.body.should.be.instanceof(Array); 17 | done(); 18 | }); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /app/templates/server/api/user(auth)/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var express = require('express'); 4 | var controller = require('./user.controller'); 5 | var config = require('../../config/environment'); 6 | var auth = require('../../auth/auth.service'); 7 | 8 | var router = express.Router(); 9 | 10 | router.get('/', auth.hasRole('admin'), controller.index); 11 | router.delete('/:id', auth.hasRole('admin'), controller.destroy); 12 | router.get('/me', auth.isAuthenticated(), controller.me); 13 | router.put('/:id/password', auth.isAuthenticated(), controller.changePassword); 14 | router.get('/:id', auth.isAuthenticated(), controller.show); 15 | router.post('/', controller.create); 16 | 17 | module.exports = router; 18 | -------------------------------------------------------------------------------- /app/templates/server/api/user(auth)/user.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var User = require('./user.model'); 4 | var passport = require('passport'); 5 | var config = require('../../config/environment'); 6 | var jwt = require('jsonwebtoken'); 7 | 8 | var validationError = function(res, err) { 9 | return res.status(422).json(err); 10 | }; 11 | 12 | /** 13 | * Get list of users 14 | * restriction: 'admin' 15 | */ 16 | exports.index = function(req, res) { 17 | User.find({}, '-salt -hashedPassword', function (err, users) { 18 | if(err) return res.status(500).send(err); 19 | res.status(200).json(users); 20 | }); 21 | }; 22 | 23 | /** 24 | * Creates a new user 25 | */ 26 | exports.create = function (req, res, next) { 27 | var newUser = new User(req.body); 28 | newUser.provider = 'local'; 29 | newUser.role = 'user'; 30 | newUser.save(function(err, user) { 31 | if (err) return validationError(res, err); 32 | var token = jwt.sign({_id: user._id }, config.secrets.session, { expiresInMinutes: 60*5 }); 33 | res.json({ token: token }); 34 | }); 35 | }; 36 | 37 | /** 38 | * Get a single user 39 | */ 40 | exports.show = function (req, res, next) { 41 | var userId = req.params.id; 42 | 43 | User.findById(userId, function (err, user) { 44 | if (err) return next(err); 45 | if (!user) return res.status(401).send('Unauthorized'); 46 | res.json(user.profile); 47 | }); 48 | }; 49 | 50 | /** 51 | * Deletes a user 52 | * restriction: 'admin' 53 | */ 54 | exports.destroy = function(req, res) { 55 | User.findByIdAndRemove(req.params.id, function(err, user) { 56 | if(err) return res.status(500).send(err); 57 | return res.status(204).send('No Content'); 58 | }); 59 | }; 60 | 61 | /** 62 | * Change a users password 63 | */ 64 | exports.changePassword = function(req, res, next) { 65 | var userId = req.user._id; 66 | var oldPass = String(req.body.oldPassword); 67 | var newPass = String(req.body.newPassword); 68 | 69 | User.findById(userId, function (err, user) { 70 | if(user.authenticate(oldPass)) { 71 | user.password = newPass; 72 | user.save(function(err) { 73 | if (err) return validationError(res, err); 74 | res.status(200).send('OK'); 75 | }); 76 | } else { 77 | res.status(403).send('Forbidden'); 78 | } 79 | }); 80 | }; 81 | 82 | /** 83 | * Get my info 84 | */ 85 | exports.me = function(req, res, next) { 86 | var userId = req.user._id; 87 | User.findOne({ 88 | _id: userId 89 | }, '-salt -hashedPassword', function(err, user) { // don't ever give out the password or salt 90 | if (err) return next(err); 91 | if (!user) return res.status(401).send('Unauthorized'); 92 | res.json(user); 93 | }); 94 | }; 95 | 96 | /** 97 | * Authentication callback 98 | */ 99 | exports.authCallback = function(req, res, next) { 100 | res.redirect('/'); 101 | }; 102 | -------------------------------------------------------------------------------- /app/templates/server/api/user(auth)/user.model.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var mongoose = require('mongoose'); 4 | var Schema = mongoose.Schema; 5 | var crypto = require('crypto');<% if(filters.oauth) { %> 6 | var authTypes = ['github', 'twitter', 'facebook', 'google'];<% } %> 7 | 8 | var UserSchema = new Schema({ 9 | name: String, 10 | email: { type: String, lowercase: true }, 11 | role: { 12 | type: String, 13 | default: 'user' 14 | }, 15 | hashedPassword: String, 16 | provider: String, 17 | salt: String<% if (filters.oauth) { %>,<% if (filters.facebookAuth) { %> 18 | facebook: {},<% } %><% if (filters.twitterAuth) { %> 19 | twitter: {},<% } %><% if (filters.googleAuth) { %> 20 | google: {},<% } %> 21 | github: {}<% } %> 22 | }); 23 | 24 | /** 25 | * Virtuals 26 | */ 27 | UserSchema 28 | .virtual('password') 29 | .set(function(password) { 30 | this._password = password; 31 | this.salt = this.makeSalt(); 32 | this.hashedPassword = this.encryptPassword(password); 33 | }) 34 | .get(function() { 35 | return this._password; 36 | }); 37 | 38 | // Public profile information 39 | UserSchema 40 | .virtual('profile') 41 | .get(function() { 42 | return { 43 | 'name': this.name, 44 | 'role': this.role 45 | }; 46 | }); 47 | 48 | // Non-sensitive info we'll be putting in the token 49 | UserSchema 50 | .virtual('token') 51 | .get(function() { 52 | return { 53 | '_id': this._id, 54 | 'role': this.role 55 | }; 56 | }); 57 | 58 | /** 59 | * Validations 60 | */ 61 | 62 | // Validate empty email 63 | UserSchema 64 | .path('email') 65 | .validate(function(email) {<% if (filters.oauth) { %> 66 | if (authTypes.indexOf(this.provider) !== -1) return true;<% } %> 67 | return email.length; 68 | }, 'Email cannot be blank'); 69 | 70 | // Validate empty password 71 | UserSchema 72 | .path('hashedPassword') 73 | .validate(function(hashedPassword) {<% if (filters.oauth) { %> 74 | if (authTypes.indexOf(this.provider) !== -1) return true;<% } %> 75 | return hashedPassword.length; 76 | }, 'Password cannot be blank'); 77 | 78 | // Validate email is not taken 79 | UserSchema 80 | .path('email') 81 | .validate(function(value, respond) { 82 | var self = this; 83 | this.constructor.findOne({email: value}, function(err, user) { 84 | if(err) throw err; 85 | if(user) { 86 | if(self.id === user.id) return respond(true); 87 | return respond(false); 88 | } 89 | respond(true); 90 | }); 91 | }, 'The specified email address is already in use.'); 92 | 93 | var validatePresenceOf = function(value) { 94 | return value && value.length; 95 | }; 96 | 97 | /** 98 | * Pre-save hook 99 | */ 100 | UserSchema 101 | .pre('save', function(next) { 102 | if (!this.isNew) return next(); 103 | 104 | if (!validatePresenceOf(this.hashedPassword)<% if (filters.oauth) { %> && authTypes.indexOf(this.provider) === -1<% } %>) 105 | next(new Error('Invalid password')); 106 | else 107 | next(); 108 | }); 109 | 110 | /** 111 | * Methods 112 | */ 113 | UserSchema.methods = { 114 | /** 115 | * Authenticate - check if the passwords are the same 116 | * 117 | * @param {String} plainText 118 | * @return {Boolean} 119 | * @api public 120 | */ 121 | authenticate: function(plainText) { 122 | return this.encryptPassword(plainText) === this.hashedPassword; 123 | }, 124 | 125 | /** 126 | * Make salt 127 | * 128 | * @return {String} 129 | * @api public 130 | */ 131 | makeSalt: function() { 132 | return crypto.randomBytes(16).toString('base64'); 133 | }, 134 | 135 | /** 136 | * Encrypt password 137 | * 138 | * @param {String} password 139 | * @return {String} 140 | * @api public 141 | */ 142 | encryptPassword: function(password) { 143 | if (!password || !this.salt) return ''; 144 | var salt = new Buffer(this.salt, 'base64'); 145 | return crypto.pbkdf2Sync(password, salt, 10000, 64).toString('base64'); 146 | } 147 | }; 148 | 149 | module.exports = mongoose.model('User', UserSchema); 150 | -------------------------------------------------------------------------------- /app/templates/server/api/user(auth)/user.model.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var should = require('should'); 4 | var app = require('../../app'); 5 | var User = require('./user.model'); 6 | 7 | var user = new User({ 8 | provider: 'local', 9 | name: 'Fake User', 10 | email: 'test@test.com', 11 | password: 'password' 12 | }); 13 | 14 | describe('User Model', function() { 15 | before(function(done) { 16 | // Clear users before testing 17 | User.remove().exec().then(function() { 18 | done(); 19 | }); 20 | }); 21 | 22 | afterEach(function(done) { 23 | User.remove().exec().then(function() { 24 | done(); 25 | }); 26 | }); 27 | 28 | it('should begin with no users', function(done) { 29 | User.find({}, function(err, users) { 30 | users.should.have.length(0); 31 | done(); 32 | }); 33 | }); 34 | 35 | it('should fail when saving a duplicate user', function(done) { 36 | user.save(function() { 37 | var userDup = new User(user); 38 | userDup.save(function(err) { 39 | should.exist(err); 40 | done(); 41 | }); 42 | }); 43 | }); 44 | 45 | it('should fail when saving without an email', function(done) { 46 | user.email = ''; 47 | user.save(function(err) { 48 | should.exist(err); 49 | done(); 50 | }); 51 | }); 52 | 53 | it("should authenticate user if password is valid", function() { 54 | return user.authenticate('password').should.be.true; 55 | }); 56 | 57 | it("should not authenticate user if password is invalid", function() { 58 | return user.authenticate('blah').should.not.be.true; 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /app/templates/server/api/user(auth)/user.seed.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "provider": "local", 4 | "name": "Test User", 5 | "email": "test@test.com", 6 | "password": "test" 7 | }, { 8 | "provider": "local", 9 | "role": "admin", 10 | "name": "Admin", 11 | "email": "admin@admin.com", 12 | "password": "admin" 13 | } 14 | ] -------------------------------------------------------------------------------- /app/templates/server/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Main application file 3 | */ 4 | 5 | 'use strict'; 6 | 7 | // Set default node environment to development 8 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'; 9 | 10 | var express = require('express');<% if (filters.mongoose) { %> 11 | var mongoose = require('mongoose');<% } %> 12 | var config = require('./config/environment'); 13 | <% if (filters.mongoose) { %> 14 | // Connect to database 15 | mongoose.connect(config.mongo.uri, config.mongo.options); 16 | mongoose.connection.on('error', function(err) { 17 | console.error('MongoDB connection error: ' + err); 18 | process.exit(-1); 19 | } 20 | ); 21 | // Populate DB with sample data 22 | if(config.seedDB) { require('./config/seed'); } 23 | 24 | <% } %>// Setup server 25 | var app = express(); 26 | var server = require('http').createServer(app);<% if (filters.socketio) { %> 27 | var socketio = require('socket.io')(server, { 28 | serveClient: config.env !== 'production', 29 | path: '/socket.io-client' 30 | }); 31 | require('./config/socketio')(socketio);<% } %> 32 | require('./config/express')(app); 33 | require('./routes')(app); 34 | 35 | // Start server 36 | server.listen(config.port, config.ip, function () { 37 | console.log('Express server listening on %d, in %s mode', config.port, app.get('env')); 38 | }); 39 | 40 | // Expose app 41 | exports = module.exports = app; 42 | -------------------------------------------------------------------------------- /app/templates/server/auth(auth)/auth.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var mongoose = require('mongoose'); 4 | var passport = require('passport'); 5 | var config = require('../config/environment'); 6 | var jwt = require('jsonwebtoken'); 7 | var expressJwt = require('express-jwt'); 8 | var compose = require('composable-middleware'); 9 | var User = require('../api/user/user.model'); 10 | var validateJwt = expressJwt({ secret: config.secrets.session }); 11 | 12 | /** 13 | * Attaches the user object to the request if authenticated 14 | * Otherwise returns 403 15 | */ 16 | function isAuthenticated() { 17 | return compose() 18 | // Validate jwt 19 | .use(function(req, res, next) { 20 | // allow access_token to be passed through query parameter as well 21 | if(req.query && req.query.hasOwnProperty('access_token')) { 22 | req.headers.authorization = 'Bearer ' + req.query.access_token; 23 | } 24 | validateJwt(req, res, next); 25 | }) 26 | // Attach user to request 27 | .use(function(req, res, next) { 28 | User.findById(req.user._id, function (err, user) { 29 | if (err) return next(err); 30 | if (!user) return res.status(401).send('Unauthorized'); 31 | 32 | req.user = user; 33 | next(); 34 | }); 35 | }); 36 | } 37 | 38 | /** 39 | * Checks if the user role meets the minimum requirements of the route 40 | */ 41 | function hasRole(roleRequired) { 42 | if (!roleRequired) throw new Error('Required role needs to be set'); 43 | 44 | return compose() 45 | .use(isAuthenticated()) 46 | .use(function meetsRequirements(req, res, next) { 47 | if (config.userRoles.indexOf(req.user.role) >= config.userRoles.indexOf(roleRequired)) { 48 | next(); 49 | } 50 | else { 51 | res.status(403).send('Forbidden'); 52 | } 53 | }); 54 | } 55 | 56 | /** 57 | * Returns a jwt token signed by the app secret 58 | */ 59 | function signToken(id) { 60 | return jwt.sign({ _id: id }, config.secrets.session, { expiresIn: 60 * 60 * 5 }); 61 | } 62 | 63 | /** 64 | * Set token cookie directly for oAuth strategies 65 | */ 66 | function setTokenCookie(req, res) { 67 | if (!req.user) return res.status(404).json({ message: 'Something went wrong, please try again.'}); 68 | var token = signToken(req.user._id, req.user.role); 69 | res.cookie('token', JSON.stringify(token)); 70 | res.redirect('/'); 71 | } 72 | 73 | exports.isAuthenticated = isAuthenticated; 74 | exports.hasRole = hasRole; 75 | exports.signToken = signToken; 76 | exports.setTokenCookie = setTokenCookie; 77 | -------------------------------------------------------------------------------- /app/templates/server/auth(auth)/facebook(facebookAuth)/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var express = require('express'); 4 | var passport = require('passport'); 5 | var auth = require('../auth.service'); 6 | 7 | var router = express.Router(); 8 | 9 | router 10 | .get('/', passport.authenticate('facebook', { 11 | scope: ['email', 'user_about_me'], 12 | failureRedirect: '/signup', 13 | session: false 14 | })) 15 | 16 | .get('/callback', passport.authenticate('facebook', { 17 | failureRedirect: '/signup', 18 | session: false 19 | }), auth.setTokenCookie); 20 | 21 | module.exports = router; -------------------------------------------------------------------------------- /app/templates/server/auth(auth)/facebook(facebookAuth)/passport.js: -------------------------------------------------------------------------------- 1 | var passport = require('passport'); 2 | var FacebookStrategy = require('passport-facebook').Strategy; 3 | 4 | exports.setup = function (User, config) { 5 | passport.use(new FacebookStrategy({ 6 | clientID: config.facebook.clientID, 7 | clientSecret: config.facebook.clientSecret, 8 | callbackURL: config.facebook.callbackURL 9 | }, 10 | function(accessToken, refreshToken, profile, done) { 11 | User.findOne({ 12 | 'facebook.id': profile.id 13 | }, 14 | function(err, user) { 15 | if (err) { 16 | return done(err); 17 | } 18 | if (!user) { 19 | user = new User({ 20 | name: profile.displayName, 21 | email: profile.emails[0].value, 22 | role: 'user', 23 | username: profile.username, 24 | provider: 'facebook', 25 | facebook: profile._json 26 | }); 27 | user.save(function(err) { 28 | if (err) return done(err); 29 | done(err, user); 30 | }); 31 | } else { 32 | return done(err, user); 33 | } 34 | }) 35 | } 36 | )); 37 | }; 38 | -------------------------------------------------------------------------------- /app/templates/server/auth(auth)/google(googleAuth)/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var express = require('express'); 4 | var passport = require('passport'); 5 | var auth = require('../auth.service'); 6 | 7 | var router = express.Router(); 8 | 9 | router 10 | .get('/', passport.authenticate('google', { 11 | failureRedirect: '/signup', 12 | scope: [ 13 | 'https://www.googleapis.com/auth/userinfo.profile', 14 | 'https://www.googleapis.com/auth/userinfo.email' 15 | ], 16 | session: false 17 | })) 18 | 19 | .get('/callback', passport.authenticate('google', { 20 | failureRedirect: '/signup', 21 | session: false 22 | }), auth.setTokenCookie); 23 | 24 | module.exports = router; -------------------------------------------------------------------------------- /app/templates/server/auth(auth)/google(googleAuth)/passport.js: -------------------------------------------------------------------------------- 1 | var passport = require('passport'); 2 | var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy; 3 | 4 | exports.setup = function (User, config) { 5 | passport.use(new GoogleStrategy({ 6 | clientID: config.google.clientID, 7 | clientSecret: config.google.clientSecret, 8 | callbackURL: config.google.callbackURL 9 | }, 10 | function(accessToken, refreshToken, profile, done) { 11 | User.findOne({ 12 | 'google.id': profile.id 13 | }, function(err, user) { 14 | if (!user) { 15 | user = new User({ 16 | name: profile.displayName, 17 | email: profile.emails[0].value, 18 | role: 'user', 19 | username: profile.username, 20 | provider: 'google', 21 | google: profile._json 22 | }); 23 | user.save(function(err) { 24 | if (err) return done(err); 25 | done(err, user); 26 | }); 27 | } else { 28 | return done(err, user); 29 | } 30 | }); 31 | } 32 | )); 33 | }; 34 | -------------------------------------------------------------------------------- /app/templates/server/auth(auth)/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var express = require('express'); 4 | var passport = require('passport'); 5 | var config = require('../config/environment'); 6 | var User = require('../api/user/user.model'); 7 | 8 | // Passport Configuration 9 | require('./local/passport').setup(User, config);<% if (filters.facebookAuth) { %> 10 | require('./facebook/passport').setup(User, config);<% } %><% if (filters.googleAuth) { %> 11 | require('./google/passport').setup(User, config);<% } %><% if (filters.twitterAuth) { %> 12 | require('./twitter/passport').setup(User, config);<% } %> 13 | 14 | var router = express.Router(); 15 | 16 | router.use('/local', require('./local'));<% if (filters.facebookAuth) { %> 17 | router.use('/facebook', require('./facebook'));<% } %><% if (filters.twitterAuth) { %> 18 | router.use('/twitter', require('./twitter'));<% } %><% if (filters.googleAuth) { %> 19 | router.use('/google', require('./google'));<% } %> 20 | 21 | module.exports = router; -------------------------------------------------------------------------------- /app/templates/server/auth(auth)/local/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var express = require('express'); 4 | var passport = require('passport'); 5 | var auth = require('../auth.service'); 6 | 7 | var router = express.Router(); 8 | 9 | router.post('/', function(req, res, next) { 10 | passport.authenticate('local', function (err, user, info) { 11 | var error = err || info; 12 | if (error) return res.status(401).json(error); 13 | if (!user) return res.status(404).json({message: 'Something went wrong, please try again.'}); 14 | 15 | var token = auth.signToken(user._id, user.role); 16 | res.json({token: token}); 17 | })(req, res, next) 18 | }); 19 | 20 | module.exports = router; -------------------------------------------------------------------------------- /app/templates/server/auth(auth)/local/passport.js: -------------------------------------------------------------------------------- 1 | var passport = require('passport'); 2 | var LocalStrategy = require('passport-local').Strategy; 3 | 4 | exports.setup = function (User, config) { 5 | passport.use(new LocalStrategy({ 6 | usernameField: 'email', 7 | passwordField: 'password' // this is the virtual field on the model 8 | }, 9 | function(email, password, done) { 10 | User.findOne({ 11 | email: email.toLowerCase() 12 | }, function(err, user) { 13 | if (err) return done(err); 14 | 15 | if (!user) { 16 | return done(null, false, { message: 'This email is not registered.' }); 17 | } 18 | if (!user.authenticate(password)) { 19 | return done(null, false, { message: 'This password is not correct.' }); 20 | } 21 | return done(null, user); 22 | }); 23 | } 24 | )); 25 | }; -------------------------------------------------------------------------------- /app/templates/server/auth(auth)/twitter(twitterAuth)/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var express = require('express'); 4 | var passport = require('passport'); 5 | var auth = require('../auth.service'); 6 | 7 | var router = express.Router(); 8 | 9 | router 10 | .get('/', passport.authenticate('twitter', { 11 | failureRedirect: '/signup', 12 | session: false 13 | })) 14 | 15 | .get('/callback', passport.authenticate('twitter', { 16 | failureRedirect: '/signup', 17 | session: false 18 | }), auth.setTokenCookie); 19 | 20 | module.exports = router; -------------------------------------------------------------------------------- /app/templates/server/auth(auth)/twitter(twitterAuth)/passport.js: -------------------------------------------------------------------------------- 1 | exports.setup = function (User, config) { 2 | var passport = require('passport'); 3 | var TwitterStrategy = require('passport-twitter').Strategy; 4 | 5 | passport.use(new TwitterStrategy({ 6 | consumerKey: config.twitter.clientID, 7 | consumerSecret: config.twitter.clientSecret, 8 | callbackURL: config.twitter.callbackURL 9 | }, 10 | function(token, tokenSecret, profile, done) { 11 | User.findOne({ 12 | 'twitter.id_str': profile.id 13 | }, function(err, user) { 14 | if (err) { 15 | return done(err); 16 | } 17 | if (!user) { 18 | user = new User({ 19 | name: profile.displayName, 20 | username: profile.username, 21 | role: 'user', 22 | provider: 'twitter', 23 | twitter: profile._json 24 | }); 25 | user.save(function(err) { 26 | if (err) return done(err); 27 | done(err, user); 28 | }); 29 | } else { 30 | return done(err, user); 31 | } 32 | }); 33 | } 34 | )); 35 | }; 36 | -------------------------------------------------------------------------------- /app/templates/server/config/_local.env.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Use local.env.js for environment variables that grunt will set when the server starts locally. 4 | // Use for your api keys, secrets, etc. This file should not be tracked by git. 5 | // 6 | // You will need to set these on the server you deploy to. 7 | 8 | module.exports = { 9 | DOMAIN: 'http://localhost:9000', 10 | SESSION_SECRET: "<%= _.slugify(appname) + '-secret' %>",<% if (filters.facebookAuth) { %> 11 | 12 | FACEBOOK_ID: 'app-id', 13 | FACEBOOK_SECRET: 'secret',<% } if (filters.twitterAuth) { %> 14 | 15 | TWITTER_ID: 'app-id', 16 | TWITTER_SECRET: 'secret',<% } if (filters.googleAuth) { %> 17 | 18 | GOOGLE_ID: 'app-id', 19 | GOOGLE_SECRET: 'secret', 20 | <% } %> 21 | // Control debug level for modules using visionmedia/debug 22 | DEBUG: '' 23 | }; 24 | -------------------------------------------------------------------------------- /app/templates/server/config/environment/development.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Development specific configuration 4 | // ================================== 5 | module.exports = { 6 | // MongoDB connection options 7 | mongo: { 8 | uri: 'mongodb://localhost/<%= _.slugify(appname) %>-dev' 9 | }, 10 | 11 | seedDB: true 12 | }; 13 | -------------------------------------------------------------------------------- /app/templates/server/config/environment/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var _ = require('lodash'); 5 | 6 | function requiredProcessEnv(name) { 7 | if(!process.env[name]) { 8 | throw new Error('You must set the ' + name + ' environment variable'); 9 | } 10 | return process.env[name]; 11 | } 12 | 13 | // All configurations will extend these options 14 | // ============================================ 15 | var all = { 16 | env: process.env.NODE_ENV, 17 | 18 | // Root path of server 19 | root: path.normalize(__dirname + '/../../..'), 20 | 21 | // Server port 22 | port: process.env.PORT || 9000, 23 | 24 | // Server IP 25 | ip: process.env.IP || '0.0.0.0', 26 | 27 | // Should we populate the DB with sample data? 28 | seedDB: false, 29 | 30 | // Secret for session, you will want to change this and make it an environment variable 31 | secrets: { 32 | session: '<%= _.slugify(_.humanize(appname)) + '-secret' %>' 33 | }, 34 | 35 | // List of user roles 36 | userRoles: ['guest', 'user', 'admin'], 37 | 38 | // MongoDB connection options 39 | mongo: { 40 | options: { 41 | db: { 42 | safe: true 43 | } 44 | } 45 | }, 46 | <% if(filters.facebookAuth) { %> 47 | facebook: { 48 | clientID: process.env.FACEBOOK_ID || 'id', 49 | clientSecret: process.env.FACEBOOK_SECRET || 'secret', 50 | callbackURL: (process.env.DOMAIN || '') + '/auth/facebook/callback' 51 | }, 52 | <% } %><% if(filters.twitterAuth) { %> 53 | twitter: { 54 | clientID: process.env.TWITTER_ID || 'id', 55 | clientSecret: process.env.TWITTER_SECRET || 'secret', 56 | callbackURL: (process.env.DOMAIN || '') + '/auth/twitter/callback' 57 | }, 58 | <% } %><% if(filters.googleAuth) { %> 59 | google: { 60 | clientID: process.env.GOOGLE_ID || 'id', 61 | clientSecret: process.env.GOOGLE_SECRET || 'secret', 62 | callbackURL: (process.env.DOMAIN || '') + '/auth/google/callback' 63 | }<% } %> 64 | }; 65 | 66 | // Export the config object based on the NODE_ENV 67 | // ============================================== 68 | module.exports = _.merge( 69 | all, 70 | require('./' + process.env.NODE_ENV + '.js') || {}); 71 | -------------------------------------------------------------------------------- /app/templates/server/config/environment/production.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Production specific configuration 4 | // ================================= 5 | module.exports = { 6 | // Server IP 7 | ip: process.env.OPENSHIFT_NODEJS_IP || 8 | process.env.IP || 9 | undefined, 10 | 11 | // Server port 12 | port: process.env.OPENSHIFT_NODEJS_PORT || 13 | process.env.PORT || 14 | 8080, 15 | 16 | // MongoDB connection options 17 | mongo: { 18 | uri: process.env.MONGOLAB_URI || 19 | process.env.MONGOHQ_URL || 20 | process.env.OPENSHIFT_MONGODB_DB_URL+process.env.OPENSHIFT_APP_NAME || 21 | 'mongodb://localhost/<%= _.slugify(appname) %>' 22 | } 23 | }; -------------------------------------------------------------------------------- /app/templates/server/config/environment/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Test specific configuration 4 | // =========================== 5 | module.exports = { 6 | // MongoDB connection options 7 | mongo: { 8 | uri: 'mongodb://localhost/<%= _.slugify(appname) %>-test' 9 | } 10 | }; -------------------------------------------------------------------------------- /app/templates/server/config/express.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Express configuration 3 | */ 4 | 5 | 'use strict'; 6 | 7 | var express = require('express'); 8 | var morgan = require('morgan'); 9 | var compression = require('compression'); 10 | var bodyParser = require('body-parser'); 11 | var methodOverride = require('method-override'); 12 | var cookieParser = require('cookie-parser'); 13 | var errorHandler = require('errorhandler'); 14 | var path = require('path'); 15 | var config = require('./environment');<% if (filters.auth) { %> 16 | var passport = require('passport');<% } %><% if (filters.twitterAuth) { %> 17 | var session = require('express-session'); 18 | var mongoStore = require('connect-mongo')(session); 19 | var mongoose = require('mongoose');<% } %> 20 | 21 | module.exports = function(app) { 22 | var env = app.get('env'); 23 | 24 | app.use(compression()); 25 | app.use(bodyParser.urlencoded({ extended: false })); 26 | app.use(bodyParser.json()); 27 | app.use(methodOverride()); 28 | app.use(cookieParser()); 29 | <% if (filters.auth) { %>app.use(passport.initialize());<% } %><% if (filters.twitterAuth) { %> 30 | 31 | // Persist sessions with mongoStore 32 | // We need to enable sessions for passport twitter because its an oauth 1.0 strategy 33 | app.use(session({ 34 | secret: config.secrets.session, 35 | resave: true, 36 | saveUninitialized: true, 37 | store: new mongoStore({ 38 | mongooseConnection: mongoose.connection, 39 | db: '<%= _.slugify(_.humanize(appname)) %>' 40 | }) 41 | })); 42 | <% } %> 43 | if ('production' === env) { 44 | app.use(morgan('dev')); 45 | } 46 | 47 | if ('development' === env || 'test' === env) { 48 | app.use(require('connect-livereload')()); 49 | app.use(morgan('dev')); 50 | app.use(errorHandler()); // Error handler - has to be last 51 | } 52 | }; -------------------------------------------------------------------------------- /app/templates/server/config/seed(mongoose).js: -------------------------------------------------------------------------------- 1 | /** 2 | * Populate DB with sample data on server start 3 | * to disable, edit config/environment/index.js, and set `seedDB: false` 4 | */ 5 | 6 | 'use strict'; 7 | // Insert seed models below 8 | var Thing = require('../api/thing/thing.model'); 9 | <% if (filters.auth) { %>var User = require('../api/user/user.model');<% } %> 10 | 11 | // Insert seed data below 12 | var thingSeed = require('../api/thing/thing.seed.json'); 13 | 14 | // Insert seed inserts below 15 | Thing.find({}).remove(function() { 16 | Thing.create(thingSeed); 17 | }); -------------------------------------------------------------------------------- /app/templates/server/config/socketio(socketio).js: -------------------------------------------------------------------------------- 1 | /** 2 | * Socket.io configuration 3 | */ 4 | 5 | 'use strict'; 6 | 7 | var config = require('./environment'); 8 | 9 | // When the user disconnects.. perform this 10 | function onDisconnect(socket) { 11 | } 12 | 13 | // When the user connects.. perform this 14 | function onConnect(socket) { 15 | // When the client emits 'info', this listens and executes 16 | socket.on('info', function (data) { 17 | console.info('[%s] %s', socket.address, JSON.stringify(data, null, 2)); 18 | }); 19 | 20 | // Insert sockets below 21 | require('../api/thing/thing.socket').register(socket); 22 | } 23 | 24 | module.exports = function (socketio) { 25 | // socket.io (v1.x.x) is powered by debug. 26 | // In order to see all the debug output, set DEBUG (in server/config/local.env.js) to including the desired scope. 27 | // 28 | // ex: DEBUG: "http*,socket.io:socket" 29 | 30 | // We can authenticate socket.io users and access their token through socket.handshake.decoded_token 31 | // 32 | // 1. You will need to send the token in `client/components/socket/socket.service.js` 33 | // 34 | // 2. Require authentication here: 35 | // socketio.use(require('socketio-jwt').authorize({ 36 | // secret: config.secrets.session, 37 | // handshake: true 38 | // })); 39 | 40 | socketio.on('connection', function (socket) { 41 | socket.address = socket.handshake.address !== null ? 42 | socket.handshake.address.address + ':' + socket.handshake.address.port : 43 | process.env.DOMAIN; 44 | 45 | socket.connectedAt = new Date(); 46 | 47 | // Call onDisconnect. 48 | socket.on('disconnect', function () { 49 | onDisconnect(socket); 50 | console.info('[%s] DISCONNECTED', socket.address); 51 | }); 52 | 53 | // Call onConnect. 54 | onConnect(socket); 55 | console.info('[%s] CONNECTED', socket.address); 56 | }); 57 | }; -------------------------------------------------------------------------------- /app/templates/server/routes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Main application routes 3 | */ 4 | 5 | 'use strict'; 6 | 7 | var path = require('path'); 8 | 9 | module.exports = function(app) { 10 | 11 | // Insert routes below 12 | app.use('/api/things', require('./api/thing')); 13 | <% if (filters.auth) { %>app.use('/api/users', require('./api/user')); 14 | 15 | app.use('/auth', require('./auth')); 16 | <% } %> 17 | 18 | }; 19 | -------------------------------------------------------------------------------- /endpoint/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var yeoman = require('yeoman-generator'); 4 | var util = require('util'); 5 | var ngUtil = require('../util'); 6 | var ScriptBase = require('../script-base.js'); 7 | 8 | var Generator = module.exports = function Generator() { 9 | ScriptBase.apply(this, arguments); 10 | }; 11 | 12 | util.inherits(Generator, ScriptBase); 13 | 14 | Generator.prototype.askFor = function askFor() { 15 | var done = this.async(); 16 | var name = this.name; 17 | 18 | var base = this.config.get('routesBase') || '/api/'; 19 | if(base.charAt(base.length-1) !== '/') { 20 | base = base + '/'; 21 | } 22 | 23 | // pluralization defaults to true for backwards compat 24 | if (this.config.get('pluralizeRoutes') !== false) { 25 | name = name + 's'; 26 | } 27 | 28 | var prompts = [ 29 | { 30 | name: 'route', 31 | message: 'What will the url of your endpoint be?', 32 | default: base + name 33 | } 34 | ]; 35 | 36 | this.prompt(prompts, function (props) { 37 | if(props.route.charAt(0) !== '/') { 38 | props.route = '/' + props.route; 39 | } 40 | 41 | this.route = props.route; 42 | done(); 43 | }.bind(this)); 44 | }; 45 | 46 | Generator.prototype.registerEndpoint = function registerEndpoint() { 47 | if(this.config.get('insertRoutes')) { 48 | var routeConfig = { 49 | file: this.config.get('registerRoutesFile'), 50 | needle: this.config.get('routesNeedle'), 51 | splicable: [ 52 | "app.use(\'" + this.route +"\', require(\'./api/" + this.name + "\'));" 53 | ] 54 | }; 55 | ngUtil.rewriteFile(routeConfig); 56 | } 57 | 58 | if(this.config.get('insertSeed')) { 59 | var seedModelConfig = { 60 | file: this.config.get('registerSeedFile'), 61 | needle: this.config.get('seedModelNeedle'), 62 | splicable: [ 63 | "var " + ngUtil.capitalizeFirstLetter(this.name) + " = require(\'../api/" + this.name + "/" + this.name + ".model\');" 64 | ] 65 | }; 66 | ngUtil.rewriteFile(seedModelConfig); 67 | 68 | var seedDataConfig = { 69 | file: this.config.get('registerSeedFile'), 70 | needle: this.config.get('seedDataNeedle'), 71 | splicable: [ 72 | "var " + this.name + "Seed = require(\'../api/" + this.name + "/" + this.name + ".seed.json\');" 73 | ] 74 | }; 75 | ngUtil.rewriteFile(seedDataConfig); 76 | 77 | var seedInsertConfig = { 78 | file: this.config.get('registerSeedFile'), 79 | needle: this.config.get('seedInsertNeedle'), 80 | splicable: [ 81 | ngUtil.capitalizeFirstLetter(this.name) + ".find({}).remove(function() {\n\t" + ngUtil.capitalizeFirstLetter(this.name) + ".create(" + this.name + "Seed);\n});\n" 82 | ] 83 | }; 84 | ngUtil.rewriteFile(seedInsertConfig); 85 | } 86 | 87 | if (this.filters && this.filters.socketio) { 88 | if(this.config.get('insertSockets')) { 89 | var socketConfig = { 90 | file: this.config.get('registerSocketsFile'), 91 | needle: this.config.get('socketsNeedle'), 92 | splicable: [ 93 | "require(\'../api/" + this.name + '/' + this.name + ".socket\').register(socket);" 94 | ] 95 | }; 96 | ngUtil.rewriteFile(socketConfig); 97 | } 98 | } 99 | }; 100 | 101 | Generator.prototype.createFiles = function createFiles() { 102 | var dest = this.config.get('endpointDirectory') || 'server/api/' + this.name; 103 | this.sourceRoot(path.join(__dirname, './templates')); 104 | ngUtil.processDirectory(this, '.', dest); 105 | }; 106 | -------------------------------------------------------------------------------- /endpoint/templates/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var express = require('express'); 4 | var controller = require('./<%= name %>.controller'); 5 | 6 | var router = express.Router(); 7 | 8 | router.get('/', controller.index);<% if(filters.mongoose) { %> 9 | router.get('/:id', controller.show); 10 | router.post('/', controller.create); 11 | router.put('/:id', controller.update); 12 | router.patch('/:id', controller.update); 13 | router.delete('/:id', controller.destroy);<% } %> 14 | 15 | module.exports = router; -------------------------------------------------------------------------------- /endpoint/templates/name.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('lodash');<% if (filters.mongoose) { %> 4 | var <%= classedName %> = require('./<%= name %>.model');<% } %> 5 | 6 | // Get list of <%= name %>s 7 | exports.index = function(req, res) {<% if (!filters.mongoose) { %> 8 | res.json([]);<% } %><% if (filters.mongoose) { %> 9 | <%= classedName %>.find(function (err, <%= name %>s) { 10 | if(err) { return handleError(res, err); } 11 | return res.status(200).json(<%= name %>s); 12 | });<% } %> 13 | };<% if (filters.mongoose) { %> 14 | 15 | // Get a single <%= name %> 16 | exports.show = function(req, res) { 17 | <%= classedName %>.findById(req.params.id, function (err, <%= name %>) { 18 | if(err) { return handleError(res, err); } 19 | if(!<%= name %>) { return res.status(404).send('Not Found'); } 20 | return res.json(<%= name %>); 21 | }); 22 | }; 23 | 24 | // Creates a new <%= name %> in the DB. 25 | exports.create = function(req, res) { 26 | <%= classedName %>.create(req.body, function(err, <%= name %>) { 27 | if(err) { return handleError(res, err); } 28 | return res.status(201).json(<%= name %>); 29 | }); 30 | }; 31 | 32 | // Updates an existing <%= name %> in the DB. 33 | exports.update = function(req, res) { 34 | if(req.body._id) { delete req.body._id; } 35 | <%= classedName %>.findById(req.params.id, function (err, <%= name %>) { 36 | if (err) { return handleError(res, err); } 37 | if(!<%= name %>) { return res.status(404).send('Not Found'); } 38 | var updated = _.merge(<%= name %>, req.body); 39 | updated.save(function (err) { 40 | if (err) { return handleError(res, err); } 41 | return res.status(200).json(<%= name %>); 42 | }); 43 | }); 44 | }; 45 | 46 | // Deletes a <%= name %> from the DB. 47 | exports.destroy = function(req, res) { 48 | <%= classedName %>.findById(req.params.id, function (err, <%= name %>) { 49 | if(err) { return handleError(res, err); } 50 | if(!<%= name %>) { return res.status(404).send('Not Found'); } 51 | <%= name %>.remove(function(err) { 52 | if(err) { return handleError(res, err); } 53 | return res.status(204).send('No Content'); 54 | }); 55 | }); 56 | }; 57 | 58 | function handleError(res, err) { 59 | return res.status(500).send(err); 60 | }<% } %> -------------------------------------------------------------------------------- /endpoint/templates/name.model(mongoose).js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var mongoose = require('mongoose'), 4 | Schema = mongoose.Schema; 5 | 6 | var <%= classedName %>Schema = new Schema({ 7 | name: String, 8 | info: String, 9 | active: Boolean 10 | }); 11 | 12 | module.exports = mongoose.model('<%= classedName %>', <%= classedName %>Schema); -------------------------------------------------------------------------------- /endpoint/templates/name.seed.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "the name", 4 | "info": "description", 5 | "active": true 6 | }, 7 | { 8 | "name": "the second name", 9 | "info": "description #2", 10 | "active": true 11 | } 12 | ] -------------------------------------------------------------------------------- /endpoint/templates/name.socket(socketio).js: -------------------------------------------------------------------------------- 1 | /** 2 | * Broadcast updates to client when the model changes 3 | */ 4 | 5 | 'use strict'; 6 | 7 | var <%= classedName %> = require('./<%= name %>.model'); 8 | 9 | exports.register = function(socket) { 10 | <%= classedName %>.schema.post('save', function (doc) { 11 | onSave(socket, doc); 12 | }); 13 | <%= classedName %>.schema.post('remove', function (doc) { 14 | onRemove(socket, doc); 15 | }); 16 | } 17 | 18 | function onSave(socket, doc, cb) { 19 | socket.emit('<%= name %>:save', doc); 20 | } 21 | 22 | function onRemove(socket, doc, cb) { 23 | socket.emit('<%= name %>:remove', doc); 24 | } -------------------------------------------------------------------------------- /endpoint/templates/name.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var should = require('should'); 4 | var app = require('../../app'); 5 | var request = require('supertest'); 6 | 7 | describe('GET <%= route %>', function() { 8 | 9 | it('should respond with JSON array', function(done) { 10 | request(app) 11 | .get('<%= route %>') 12 | .expect(200) 13 | .expect('Content-Type', /json/) 14 | .end(function(err, res) { 15 | if (err) return done(err); 16 | res.body.should.be.instanceof(Array); 17 | done(); 18 | }); 19 | }); 20 | }); -------------------------------------------------------------------------------- /heroku/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | Initalizes a heroku app and generates a `dist` folder which is ready to push to heroku. 3 | 4 | Example: 5 | yo angular-fullstack:heroku 6 | 7 | This will create: 8 | a dist folder and initialize a heroku app 9 | -------------------------------------------------------------------------------- /heroku/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var util = require('util'); 3 | var yeoman = require('yeoman-generator'); 4 | var exec = require('child_process').exec; 5 | var chalk = require('chalk'); 6 | var path = require('path'); 7 | 8 | var Generator = module.exports = function Generator() { 9 | yeoman.generators.Base.apply(this, arguments); 10 | this.sourceRoot(path.join(__dirname, './templates')); 11 | 12 | try { 13 | this.appname = require(path.join(process.cwd(), 'bower.json')).name; 14 | } catch (e) { 15 | this.appname = path.basename(process.cwd()); 16 | } 17 | this.appname = this._.slugify(this.appname); 18 | this.filters = this.config.get('filters') || {}; 19 | }; 20 | 21 | util.inherits(Generator, yeoman.generators.NamedBase); 22 | 23 | Generator.prototype.askForName = function askForName() { 24 | var done = this.async(); 25 | 26 | var prompts = [{ 27 | name: 'deployedName', 28 | message: 'Name to deploy as (Leave blank for a random name):' 29 | }]; 30 | 31 | this.prompt(prompts, function (props) { 32 | this.deployedName = this._.slugify(props.deployedName); 33 | done(); 34 | }.bind(this)); 35 | }; 36 | 37 | Generator.prototype.askForRegion = function askForRegion() { 38 | var done = this.async(); 39 | 40 | var prompts = [{ 41 | type: "list", 42 | name: 'region', 43 | message: 'On which region do you want to deploy ?', 44 | choices: [ "US", "EU"], 45 | default: 0 46 | }]; 47 | 48 | this.prompt(prompts, function (props) { 49 | this.region = props.region.toLowerCase(); 50 | done(); 51 | }.bind(this)); 52 | }; 53 | 54 | Generator.prototype.checkInstallation = function checkInstallation() { 55 | if(this.abort) return; 56 | var done = this.async(); 57 | 58 | exec('heroku --version', function (err) { 59 | if (err) { 60 | this.log.error('You don\'t have the Heroku Toolbelt installed. ' + 61 | 'Grab it from https://toolbelt.heroku.com/'); 62 | this.abort = true; 63 | } 64 | done(); 65 | }.bind(this)); 66 | }; 67 | 68 | Generator.prototype.gitInit = function gitInit() { 69 | if(this.abort) return; 70 | var done = this.async(); 71 | 72 | this.log(chalk.bold('\nInitializing deployment repo')); 73 | this.mkdir('dist'); 74 | var child = exec('git init', { cwd: 'dist' }, function (err, stdout, stderr) { 75 | done(); 76 | }.bind(this)); 77 | child.stdout.on('data', function(data) { 78 | console.log(data.toString()); 79 | }); 80 | }; 81 | 82 | Generator.prototype.herokuCreate = function herokuCreate() { 83 | if(this.abort) return; 84 | var done = this.async(); 85 | var regionParams = (this.region !== 'us') ? ' --region ' + this.region : ''; 86 | 87 | this.log(chalk.bold('Creating heroku app and setting node environment')); 88 | var child = exec('heroku apps:create ' + this.deployedName + regionParams + ' && heroku config:set NODE_ENV=production', { cwd: 'dist' }, function (err, stdout, stderr) { 89 | if (err) { 90 | this.abort = true; 91 | this.log.error(err); 92 | } else { 93 | this.log('stdout: ' + stdout); 94 | } 95 | done(); 96 | }.bind(this)); 97 | 98 | child.stdout.on('data', function(data) { 99 | var output = data.toString(); 100 | this.log(output); 101 | }.bind(this)); 102 | }; 103 | 104 | Generator.prototype.copyProcfile = function copyProcfile() { 105 | if(this.abort) return; 106 | var done = this.async(); 107 | this.log(chalk.bold('Creating Procfile')); 108 | this.copy('Procfile', 'dist/Procfile'); 109 | this.conflicter.resolve(function (err) { 110 | done(); 111 | }); 112 | }; 113 | 114 | Generator.prototype.gruntBuild = function gruntBuild() { 115 | if(this.abort) return; 116 | var done = this.async(); 117 | 118 | this.log(chalk.bold('\nBuilding dist folder, please wait...')); 119 | var child = exec('grunt build', function (err, stdout) { 120 | done(); 121 | }.bind(this)); 122 | child.stdout.on('data', function(data) { 123 | this.log(data.toString()); 124 | }.bind(this)); 125 | }; 126 | 127 | Generator.prototype.gitCommit = function gitInit() { 128 | if(this.abort) return; 129 | var done = this.async(); 130 | 131 | this.log(chalk.bold('Adding files for initial commit')); 132 | var child = exec('git add -A && git commit -m "Initial commit"', { cwd: 'dist' }, function (err, stdout, stderr) { 133 | if (stdout.search('nothing to commit') >= 0) { 134 | this.log('Re-pushing the existing "dist" build...'); 135 | } else if (err) { 136 | this.log.error(err); 137 | } else { 138 | this.log(chalk.green('Done, without errors.')); 139 | } 140 | done(); 141 | }.bind(this)); 142 | 143 | child.stdout.on('data', function(data) { 144 | this.log(data.toString()); 145 | }.bind(this)); 146 | }; 147 | 148 | Generator.prototype.gitForcePush = function gitForcePush() { 149 | if(this.abort) return; 150 | var done = this.async(); 151 | 152 | this.log(chalk.bold("\nUploading your initial application code.\n This may take "+chalk.cyan('several minutes')+" depending on your connection speed...")); 153 | 154 | var child = exec('git push -f heroku master', { cwd: 'dist' }, function (err, stdout, stderr) { 155 | if (err) { 156 | this.log.error(err); 157 | } else { 158 | var hasWarning = false; 159 | 160 | if(this.filters.mongoose) { 161 | this.log(chalk.yellow('\nBecause you\'re using mongoose, you must add mongoDB to your heroku app.\n\t' + 'from `/dist`: ' + chalk.bold('heroku addons:add mongohq') + '\n')); 162 | hasWarning = true; 163 | } 164 | 165 | if(this.filters.facebookAuth) { 166 | this.log(chalk.yellow('You will need to set environment variables for facebook auth. From `/dist`:\n\t' + 167 | chalk.bold('heroku config:set FACEBOOK_ID=appId\n\t') + 168 | chalk.bold('heroku config:set FACEBOOK_SECRET=secret\n'))); 169 | hasWarning = true; 170 | } 171 | if(this.filters.googleAuth) { 172 | this.log(chalk.yellow('You will need to set environment variables for google auth. From `/dist`:\n\t' + 173 | chalk.bold('heroku config:set GOOGLE_ID=appId\n\t') + 174 | chalk.bold('heroku config:set GOOGLE_SECRET=secret\n'))); 175 | hasWarning = true; 176 | } 177 | if(this.filters.twitterAuth) { 178 | this.log(chalk.yellow('You will need to set environment variables for twitter auth. From `/dist`:\n\t' + 179 | chalk.bold('heroku config:set TWITTER_ID=appId\n\t') + 180 | chalk.bold('heroku config:set TWITTER_SECRET=secret\n'))); 181 | hasWarning = true; 182 | } 183 | 184 | this.log(chalk.green('\nYour app should now be live. To view it run\n\t' + chalk.bold('cd dist && heroku open'))); 185 | if(hasWarning) { 186 | this.log(chalk.green('\nYou may need to address the issues mentioned above and restart the server for the app to work correctly.')); 187 | } 188 | 189 | this.log(chalk.yellow('After app modification run\n\t' + chalk.bold('grunt build') + 190 | '\nThen deploy with\n\t' + chalk.bold('grunt buildcontrol:heroku'))); 191 | } 192 | done(); 193 | }.bind(this)); 194 | 195 | child.stdout.on('data', function(data) { 196 | this.log(data.toString()); 197 | }.bind(this)); 198 | }; 199 | -------------------------------------------------------------------------------- /heroku/templates/Procfile: -------------------------------------------------------------------------------- 1 | web: node server/app.js 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-node-express-mongo", 3 | "version": "2.1.6", 4 | "description": "Yeoman generator for creating MEN stack applications, using MongoDB, Express, and Node", 5 | "keywords": [ 6 | "yeoman-generator", 7 | "mean", 8 | "mongodb", 9 | "express", 10 | "scaffold", 11 | "framework", 12 | "component", 13 | "app" 14 | ], 15 | "homepage": "https://github.com/Liam-Williams/generator-node-express-mongo", 16 | "bugs": "https://github.com/Liam-Williams/generator-node-express-mongo/issues", 17 | "author": "Liam Williams", 18 | "repository": { 19 | "type": "git", 20 | "url": "git://github.com/Liam-Williams/generator-node-express-mongo.git" 21 | }, 22 | "scripts": { 23 | "test": "grunt test" 24 | }, 25 | "dependencies": { 26 | "yeoman-generator": "~0.17.0", 27 | "chalk": "~0.4.0", 28 | "wiredep": "~0.4.2", 29 | "generator-ng-component": "~0.0.4" 30 | }, 31 | "peerDependencies": { 32 | "yo": ">=1.2.0" 33 | }, 34 | "devDependencies": { 35 | "chai": "^1.9.1", 36 | "fs-extra": "^0.9.1", 37 | "grunt": "~0.4.1", 38 | "grunt-build-control": "DaftMonk/grunt-build-control", 39 | "grunt-contrib-clean": "^0.6.0", 40 | "grunt-contrib-jshint": "^0.10.0", 41 | "grunt-conventional-changelog": "~1.0.0", 42 | "grunt-mocha-test": "^0.11.0", 43 | "grunt-release": "~0.6.0", 44 | "load-grunt-tasks": "~0.2.0", 45 | "marked": "~0.2.8", 46 | "mocha": "~1.21.0", 47 | "q": "^1.0.1", 48 | "semver": "~2.2.1", 49 | "shelljs": "^0.3.0", 50 | "underscore.string": "^2.3.3" 51 | }, 52 | "engines": { 53 | "node": ">=0.10.0", 54 | "npm": ">=1.2.10" 55 | }, 56 | "licenses": [ 57 | { 58 | "type": "BSD" 59 | } 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Node Express Mongo Stack generator 2 | 3 | > Yeoman generator for creating MEN stack applications, using MongoDB, Express, and Node - lets you quickly set up a project following best practices. 4 | 5 | ## Diff from [DaftMonk/generator-angular-fullstack](https://github.com/DaftMonk/generator-angular-fullstack) 6 | 7 | > generator-node-express-mongo originated from generator-angular-fullstack v2.1.1 8 | 9 | **Removed all client generation (Angular tier of the stack)** 10 | **Added seed files to the endpoint generator:** 11 | 12 | * model.seed.json to the new endpoint folder under api/model 13 | * addition to config/seed.js to load Model, Seed Data, clear any existing data in the DB, and loads the new seed data 14 | 15 | 16 | ## Example project 17 | 18 | Generated with defaults from parent repo (DaftMonk/generator-angular-fullstack): http://fullstack-demo.herokuapp.com/. 19 | 20 | Source code: https://github.com/Liam-Williams/generator-node-express-mongo 21 | 22 | ## Usage 23 | 24 | Install `generator-node-express-mongo`: 25 | ``` 26 | npm install -g generator-node-express-mongo 27 | ``` 28 | 29 | Make a new directory, and `cd` into it: 30 | ``` 31 | mkdir my-new-project && cd $_ 32 | ``` 33 | 34 | Run `yo node-express-mongo`, optionally passing an app name: 35 | ``` 36 | yo node-express-mongo [app-name] 37 | ``` 38 | 39 | Run `grunt` for building, `grunt serve` for preview, and `grunt serve:dist` for a preview of the built app. 40 | 41 | ## Prerequisites 42 | 43 | * MongoDB - Download and Install [MongoDB](http://www.mongodb.org/downloads) - If you plan on scaffolding your project with mongoose, you'll need mongoDB to be installed and have the `mongod` process running. 44 | 45 | ## Supported Configurations 46 | 47 | **Server** 48 | 49 | * Database: `None`, `MongoDB` 50 | * Authentication boilerplate: `Yes`, `No` 51 | * oAuth integrations: `Facebook` `Twitter` `Google` 52 | * Socket.io integration: `Yes`, `No` 53 | 54 | ## Generators 55 | 56 | Available generators: 57 | 58 | * App 59 | - [node-express-mongo](#app) (aka [node-express-mongo:app](#app)) 60 | * Server Side 61 | - [node-express-mongo:endpoint](#endpoint) 62 | * Deployment 63 | - [node-express-mongo:openshift](#openshift) 64 | - [node-express-mongo:heroku](#heroku) 65 | 66 | ### App 67 | Sets up a new Npde Express Mongo app, generating all the boilerplate you need to get started. 68 | 69 | Example: 70 | ```bash 71 | yo node-express-mongo 72 | ``` 73 | 74 | ### Endpoint 75 | Generates a new API endpoint. 76 | 77 | 78 | Example: 79 | ```bash 80 | yo node-express-mongo:endpoint message 81 | [?] What will the url of your endpoint be? /api/messages 82 | ``` 83 | 84 | Produces: 85 | 86 | server/api/message/index.js 87 | server/api/message/message.spec.js 88 | server/api/message/message.controller.js 89 | server/api/message/message.model.js (optional) 90 | server/api/message/message.seed.json (optional) 91 | server/api/message/message.socket.js (optional) 92 | 93 | 94 | ###Openshift 95 | 96 | Deploying to OpenShift can be done in just a few steps: 97 | 98 | yo node-express-mongo:openshift 99 | 100 | A live application URL will be available in the output. 101 | 102 | > **oAuth** 103 | > 104 | > If you're using any oAuth strategies, you must set environment variables for your selected oAuth. For example, if we're using Facebook oAuth we would do this : 105 | > 106 | > rhc set-env FACEBOOK_ID=id -a my-openshift-app 107 | > rhc set-env FACEBOOK_SECRET=secret -a my-openshift-app 108 | > 109 | > You will also need to set `DOMAIN` environment variable: 110 | > 111 | > rhc set-env DOMAIN=.rhcloud.com 112 | > 113 | > # or (if you're using it): 114 | > 115 | > rhc set-env DOMAIN= 116 | > 117 | > After you've set the required environment variables, restart the server: 118 | > 119 | > rhc app-restart -a my-openshift-app 120 | 121 | To make your deployment process easier consider using [grunt-build-control](https://github.com/robwierzbowski/grunt-build-control). 122 | 123 | **Pushing Updates** 124 | 125 | grunt 126 | 127 | Commit and push the resulting build, located in your dist folder: 128 | 129 | grunt buildcontrol:openshift 130 | 131 | ### Heroku 132 | 133 | Deploying to heroku only takes a few steps. 134 | 135 | yo node-express-mongo:heroku 136 | 137 | To work with your new heroku app using the command line, you will need to run any `heroku` commands from the `dist` folder. 138 | 139 | 140 | If you're using mongoDB you will need to add a database to your app: 141 | 142 | heroku addons:add mongolab 143 | 144 | Your app should now be live. To view it run `heroku open`. 145 | 146 | > 147 | > If you're using any oAuth strategies, you must set environment variables for your selected oAuth. For example, if we're using **Facebook** oAuth we would do this : 148 | > 149 | > heroku config:set FACEBOOK_ID=id 150 | > heroku config:set FACEBOOK_SECRET=secret 151 | > 152 | > You will also need to set `DOMAIN` environment variable: 153 | > 154 | > heroku config:set DOMAIN=.herokuapp.com 155 | > 156 | > # or (if you're using it): 157 | > 158 | > heroku config:set DOMAIN= 159 | > 160 | 161 | To make your deployment process easier consider using [grunt-build-control](https://github.com/robwierzbowski/grunt-build-control). 162 | 163 | #### Pushing Updates 164 | 165 | grunt 166 | 167 | Commit and push the resulting build, located in your dist folder: 168 | 169 | grunt buildcontrol:heroku 170 | 171 | 172 | All of these can be updated with `bower update` as new versions are released. 173 | 174 | ## Configuration 175 | Yeoman generated projects can be further tweaked according to your needs by modifying project files appropriately. 176 | 177 | A `.yo-rc` file is generated for helping you copy configuration across projects, and to allow you to keep track of your settings. You can change this as you see fit. 178 | 179 | ## Testing 180 | 181 | Running `grunt test` will run the client and server unit tests with karma and mocha. 182 | 183 | Use `grunt test:server` to only run server tests. 184 | 185 | ## Environment Variables 186 | 187 | Keeping your app secrets and other sensitive information in source control isn't a good idea. To have grunt launch your app with specific environment variables, add them to the git ignored environment config file: `server/config/local.env.js`. 188 | 189 | ## Project Structure 190 | 191 | Overview 192 | 193 | -── server 194 | ├── api - Our apps server api 195 | ├── auth - For handling authentication with different auth strategies 196 | ├── components - Our reusable or app-wide components 197 | ├── config - Where we do the bulk of our apps configuration 198 | │ └── local.env.js - Keep our environment variables out of source control 199 | │   └── environment - Configuration specific to the node environment 200 | 201 | 202 | An example server component in `server/api` 203 | 204 | thing 205 | ├── index.js - Routes 206 | ├── thing.controller.js - Controller for our `thing` endpoint 207 | ├── thing.model.js - Database model 208 | ├── thing.seed.json - JSON model seed 209 | ├── thing.socket.js - Register socket events 210 | └── thing.spec.js - Test 211 | 212 | ## Contribute 213 | 214 | 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. 215 | 216 | When submitting a bugfix, try to write a test that exposes the bug and fails before applying your fix. Submit the test alongside the fix. 217 | 218 | When submitting a new feature, add tests that cover the feature. 219 | 220 | See the `travis.yml` for configuration required to run tests. 221 | 222 | ## License 223 | 224 | [BSD license](http://opensource.org/licenses/bsd-license.php) 225 | -------------------------------------------------------------------------------- /script-base.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var util = require('util'); 3 | var path = require('path'); 4 | var yeoman = require('yeoman-generator'); 5 | var angularUtils = require('./util.js'); 6 | 7 | var Generator = module.exports = function Generator() { 8 | yeoman.generators.NamedBase.apply(this, arguments); 9 | 10 | this.appname = path.basename(process.cwd()); 11 | 12 | this.appname = this._.slugify(this._.humanize(this.appname)); 13 | this.scriptAppName = this._.camelize(this.appname) + angularUtils.appName(this); 14 | 15 | this.cameledName = this._.camelize(this.name); 16 | this.classedName = this._.classify(this.name); 17 | 18 | this.filters = this.config.get('filters'); 19 | this.sourceRoot(path.join(__dirname, '/templates')); 20 | }; 21 | 22 | util.inherits(Generator, yeoman.generators.NamedBase); -------------------------------------------------------------------------------- /util.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | 5 | module.exports = { 6 | rewrite: rewrite, 7 | rewriteFile: rewriteFile, 8 | appName: appName, 9 | processDirectory: processDirectory, 10 | capitalizeFirstLetter: capitalizeFirstLetter 11 | }; 12 | 13 | function rewriteFile (args) { 14 | args.path = args.path || process.cwd(); 15 | var fullPath = path.join(args.path, args.file); 16 | 17 | args.haystack = fs.readFileSync(fullPath, 'utf8'); 18 | var body = rewrite(args); 19 | 20 | fs.writeFileSync(fullPath, body); 21 | } 22 | 23 | function escapeRegExp (str) { 24 | return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); 25 | } 26 | 27 | function capitalizeFirstLetter(string) { 28 | return string.charAt(0).toUpperCase() + string.slice(1); 29 | } 30 | 31 | function rewrite (args) { 32 | // check if splicable is already in the body text 33 | var re = new RegExp(args.splicable.map(function (line) { 34 | return '\s*' + escapeRegExp(line); 35 | }).join('\n')); 36 | 37 | if (re.test(args.haystack)) { 38 | return args.haystack; 39 | } 40 | 41 | var lines = args.haystack.split('\n'); 42 | 43 | var otherwiseLineIndex = -1; 44 | lines.forEach(function (line, i) { 45 | if (line.indexOf(args.needle) !== -1) { 46 | otherwiseLineIndex = i; 47 | } 48 | }); 49 | if(otherwiseLineIndex === -1) return lines.join('\n'); 50 | 51 | var spaces = 0; 52 | while (lines[otherwiseLineIndex].charAt(spaces) === ' ') { 53 | spaces += 1; 54 | } 55 | 56 | var spaceStr = ''; 57 | while ((spaces -= 1) >= 0) { 58 | spaceStr += ' '; 59 | } 60 | 61 | lines.splice(otherwiseLineIndex + 1, 0, args.splicable.map(function (line) { 62 | return spaceStr + line; 63 | }).join('\n')); 64 | 65 | return lines.join('\n'); 66 | } 67 | 68 | function appName (self) { 69 | var counter = 0, suffix = self.options['app-suffix']; 70 | // Have to check this because of generator bug #386 71 | process.argv.forEach(function(val) { 72 | if (val.indexOf('--app-suffix') > -1) { 73 | counter++; 74 | } 75 | }); 76 | if (counter === 0 || (typeof suffix === 'boolean' && suffix)) { 77 | suffix = 'App'; 78 | } 79 | return suffix ? self._.classify(suffix) : ''; 80 | } 81 | 82 | function filterFile (template) { 83 | // Find matches for parans 84 | var filterMatches = template.match(/\(([^)]+)\)/g); 85 | var filters = []; 86 | if(filterMatches) { 87 | filterMatches.forEach(function(filter) { 88 | filters.push(filter.replace('(', '').replace(')', '')); 89 | template = template.replace(filter, ''); 90 | }); 91 | } 92 | 93 | return { name: template, filters: filters }; 94 | } 95 | 96 | function templateIsUsable (self, filteredFile) { 97 | var filters = self.config.get('filters'); 98 | var enabledFilters = []; 99 | for(var key in filters) { 100 | if(filters[key]) enabledFilters.push(key); 101 | } 102 | var matchedFilters = self._.intersection(filteredFile.filters, enabledFilters); 103 | // check that all filters on file are matched 104 | if(filteredFile.filters.length && matchedFilters.length !== filteredFile.filters.length) { 105 | return false; 106 | } 107 | return true; 108 | } 109 | 110 | function processDirectory (self, source, destination) { 111 | var root = self.isPathAbsolute(source) ? source : path.join(self.sourceRoot(), source); 112 | var files = self.expandFiles('**', { dot: true, cwd: root }); 113 | var dest, src; 114 | 115 | files.forEach(function(f) { 116 | var filteredFile = filterFile(f); 117 | if(self.name) { 118 | filteredFile.name = filteredFile.name.replace('name', self.name); 119 | } 120 | var name = filteredFile.name; 121 | var copy = false, stripped; 122 | 123 | src = path.join(root, f); 124 | dest = path.join(destination, name); 125 | 126 | if(path.basename(dest).indexOf('_') === 0) { 127 | stripped = path.basename(dest).replace(/^_/, ''); 128 | dest = path.join(path.dirname(dest), stripped); 129 | } 130 | 131 | if(path.basename(dest).indexOf('!') === 0) { 132 | stripped = path.basename(dest).replace(/^!/, ''); 133 | dest = path.join(path.dirname(dest), stripped); 134 | copy = true; 135 | } 136 | 137 | if(templateIsUsable(self, filteredFile)) { 138 | if(copy) { 139 | self.copy(src, dest); 140 | } else { 141 | self.template(src, dest); 142 | } 143 | } 144 | }); 145 | } --------------------------------------------------------------------------------