├── .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 | }
--------------------------------------------------------------------------------