├── .bowerrc ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .jscsrc ├── .jshintrc ├── .oliverc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bower.json ├── gulpfile.js ├── karma.conf.js ├── package.json └── src ├── app ├── config │ ├── constants.js │ ├── modules.js │ ├── routes.js │ └── run.js ├── directives │ └── repoinfo │ │ ├── repoinfo.directive.js │ │ ├── repoinfo.html │ │ └── repoinfo.scss ├── pods │ └── home │ │ ├── home.controller.js │ │ ├── home.controller.spec.js │ │ ├── home.html │ │ └── home.scss ├── services │ └── github │ │ └── github.service.js └── styles │ ├── _base.scss │ ├── _overrides.scss │ ├── _type.scss │ └── app.scss ├── assets ├── apple-touch-icon.png ├── favicon.ico ├── fonts │ ├── .gitkeep │ ├── roboto-regular-webfont.eot │ ├── roboto-regular-webfont.svg │ ├── roboto-regular-webfont.ttf │ ├── roboto-regular-webfont.woff │ └── roboto-regular-webfont.woff2 └── img │ ├── icons │ ├── fork.svg │ ├── menu.svg │ ├── more_vert.svg │ └── star.svg │ └── olive.svg └── index.html /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | indent_style = space 9 | indent_size = 2 10 | end_of_line = lf 11 | charset = utf-8 12 | trim_trailing_whitespace = true 13 | insert_final_newline = true 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | bower_components/ 3 | .tmp/ 4 | dist/ 5 | coverage/ 6 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "requireParenthesesAroundIIFE": true, 3 | "validateIndentation": 2, 4 | "requireCapitalizedConstructors": true, 5 | "validateQuoteMarks": "'" 6 | } 7 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "strict": true, 3 | "bitwise": true, 4 | "curly": true, 5 | "eqeqeq": true, 6 | "latedef": false, 7 | "noarg": true, 8 | "undef": true, 9 | "unused": true, 10 | "jasmine": true, 11 | "globals": { 12 | "angular": false, 13 | "module": false, 14 | "inject": false 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.oliverc: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.1", 3 | "ports": { 4 | "karma": 3002 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ### 0.2.0 4 | 5 | - Upgrade to Angular `1.4.6`. 6 | - Use `ngMaterial` instead of Bootstrap. 7 | - Introduce `jQuery` and `ngTouch`. 8 | - Upgrade Olive to `0.0.8` (use two separate tmp dir for serve and build/test). 9 | - Update `gulpfile` to comply with Olive 0.0.8. 10 | - Update `gulp-useref` search path in `index.html`. 11 | - Remove `layouts` and `partials` and use directives instead for the sake of simplicity of this boilerplate. 12 | - Include Roboto font locally (see in `assets/fonts` dir) 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Mohammad Islam 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AngularJS Seed 2 | 3 | > Olive seed for AngularJS - offers you quickly scaffold an AngularJS project with pragmatic defaults and best practices. It include all goodies you need all the way from testing to production with a faster, automated and productive development environment. 4 | 5 | ![tech-stack][tech-stack] 6 | 7 | ## Usage 8 | 9 | Note that, if you're just scaffolding an app you **DO NOT clone** this repo. Just follow the instruction below: 10 | 11 | ### Prepare 12 | 13 | Install `gulp`, `bower` and `olive` if you haven't already: 14 | ``` 15 | npm install -g olive gulp bower 16 | ``` 17 | Create your project from this Angular seed using Olive CLI: 18 | ``` 19 | mkdir myapp && cd myapp 20 | olive new angular 21 | ``` 22 | Install npm and bower dependencies: 23 | ``` 24 | npm install && bower install 25 | ``` 26 | 27 | ### Develop 28 | 29 | Start developing: 30 | ``` 31 | npm start 32 | ``` 33 | Run test once and generate code coverage reports in `coverage` directory. 34 | ``` 35 | npm test 36 | ``` 37 | Build for production: 38 | ``` 39 | npm run build 40 | ``` 41 | 42 | ## Features 43 | 44 | ### Test Driven Development 45 | 46 | While developing your app, keep an eye on all unit-test specs. 47 | 48 | ![reporter][reporter-image] 49 | 50 | ### Test coverage 51 | 52 | See how much of your code is covered by well-written test scripts. 53 | 54 | ![coverage][coverage-image] 55 | 56 | ## Directory Structure 57 | 58 | ``` 59 | ├─src/ 60 | │ ├─app/ 61 | │ │ ├─components/ 62 | │ │ │ └─navbar/ 63 | │ │ │ ├─repoinfo.directive.js 64 | │ │ │ ├─repoinfo.html 65 | │ │ │ └─repoinfo.scss 66 | │ │ ├─config/ 67 | │ │ │ ├─modules.js 68 | │ │ │ ├─routes.js 69 | │ │ │ └─run.js 70 | │ │ ├─layouts/ 71 | │ │ │ └─default/ 72 | │ │ │ ├─default.html 73 | │ │ │ └─default.scss 74 | │ │ ├─partials/ 75 | │ │ │ ├─footer/ 76 | │ │ │ │ ├─footer.html 77 | │ │ │ │ └─footer.scss 78 | │ │ │ └─header/ 79 | │ │ │ ├─header.html 80 | │ │ │ └─header.scss 81 | │ │ ├─pods/ 82 | │ │ │ └─home/ 83 | │ │ │ ├─home.controller.js 84 | │ │ │ ├─home.controller.spec.js 85 | │ │ │ ├─home.html 86 | │ │ │ └─home.scss 87 | │ │ ├─services/ 88 | │ │ │ └─github 89 | │ │ │ └─github.service.js 90 | │ │ ├─styles/ 91 | │ │ │ ├─_overrides.scss 92 | │ │ │ ├─_type.scss 93 | │ │ │ └─app.scss 94 | │ │ └─app.js 95 | │ ├─assets/ 96 | │ │ ├─img/ 97 | │ │ ├─fonts/ 98 | │ │ ├─apple-touch-icon.png 99 | │ │ └─favicon.ico 100 | │ └─index.html 101 | ├─dist/ 102 | ├─.tmp/ 103 | ├─bower_components/ 104 | ├─node_modules/ 105 | ├─.bowerrc 106 | ├─.editorconfig 107 | ├─.gitattributes 108 | ├─.gitignore 109 | ├─.jscsrc 110 | ├─.jshintrc 111 | ├─.oliverc 112 | ├─bower.json 113 | ├─gulpfile.js 114 | ├─karma.conf.js 115 | ├─package.json 116 | └─README.md 117 | ``` 118 | 119 | File/Directory | Purpose 120 | ------------------|--------- 121 | src/ | Contains your Angular application code. 122 | dist/ | Contains the distributable (that is, optimized and self-contained) output of your application. Deploy this to your server! 123 | .tmp/ | Various temporary output of build steps, as well as the debug output of your application. 124 | bower_components/ | Bower dependencies. 125 | node_modules | Node modules required for development purpose. 126 | .bowerrc | Bower configuration. 127 | .editorconfig | EditorConfig file. 128 | .oliverc | Olive configuration. 129 | .gitattributes | Definition for Git attributes. 130 | .gitignore | Git configuration for ignored files. 131 | .jscsrc | JavaScript code style configuration. 132 | .jshintrc | JSHint configuration. 133 | .oliverc | Olive configuration. 134 | bower.json | Bower configuration and dependency list. 135 | gulpfile.js | Contains build specification for Gulp. 136 | karma.conf.js | Karma configuration. 137 | package.json | NPM configuration. Mainly used to list the dependencies needed for asset compilation. 138 | README.md | This documentation. 139 | 140 | ## .oliverc 141 | 142 | Default options: 143 | ``` 144 | { 145 | "paths": { 146 | "src": "src", 147 | "tmp": ".tmp", 148 | "dist": "dist" 149 | } 150 | } 151 | ``` 152 | These default options can be overridden. You can also include additional options from the following: 153 | 154 | ### Ports 155 | 156 | For example: 157 | ``` 158 | { 159 | "ports": { 160 | "app": 3000, 161 | "bs": 3001, 162 | "karma": 3002 163 | } 164 | } 165 | ``` 166 | - `app`: The app will be served via this port (i.e. http://localhost:3000) 167 | - `bs`: BrowserSync UI control panel can be accessed via this port (i.e. http://localhost:3001) 168 | - `karma`: Karma tests report page is served via this port (i.e. http://localhost:3002/debug.html) 169 | 170 | ### Content Security Policy 171 | 172 | For example: 173 | ``` 174 | { 175 | "content-security-policy": { 176 | "development": { 177 | "default-src": "'unsafe-inline' 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'", 178 | "style-src": "'self' 'unsafe-inline'", 179 | "media-src": "*", 180 | ... 181 | "other-directive": "other-source" 182 | }, 183 | "production": { 184 | "default-src": "'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'", 185 | "style-src": "'self' 'unsafe-inline'", 186 | "media-src": "*" 187 | }, 188 | "other-env": { 189 | ... 190 | } 191 | } 192 | } 193 | ``` 194 | 195 | [tech-stack]: https://cloud.githubusercontent.com/assets/508043/9190800/b12eb888-3fc7-11e5-97a2-2b1f44a89315.png 196 | [reporter-image]: https://cloud.githubusercontent.com/assets/508043/9190160/172fefc4-3fc0-11e5-97a0-6d52fbb495c3.png 197 | [coverage-image]: https://cloud.githubusercontent.com/assets/508043/9190201/8013b372-3fc0-11e5-94c5-f8cdfcc78b2b.png 198 | [layouts-image]:https://cloud.githubusercontent.com/assets/508043/9190288/77dd3222-3fc1-11e5-891e-be8e1cbf26ed.png 199 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "seed-angular", 3 | "dependencies": { 4 | "angular": "~1.5.5", 5 | "angular-touch": "~1.5.5", 6 | "angular-ui-router": "^0.3.0", 7 | "jquery": "^2.2.3", 8 | "angular-material": "^1.0.8" 9 | }, 10 | "devDependencies": { 11 | "angular-mocks": "~1.5.5" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | /* jshint node:true, latedef:false */ 2 | 3 | 'use strict'; 4 | 5 | var pkg = require('./package.json'), 6 | fs = require('fs'), 7 | olive = require('olive'), 8 | path = require('path'), 9 | _ = require('lodash'), 10 | gulp = require('gulp'), 11 | gutil = require('gulp-util'), 12 | chokidar = require('chokidar'), 13 | wiredep = require('wiredep').stream, 14 | browserSync = require('browser-sync'), 15 | modRewrite = require('connect-modrewrite'), 16 | del = require('del'), 17 | mkdirp = require('mkdirp'), 18 | karma = require('karma'), 19 | tinylr = require('tiny-lr')(), 20 | opn = require('opn'), 21 | options, 22 | $ = require('gulp-load-plugins')({ 23 | pattern: ['gulp-*', 'main-bower-files'] 24 | }); 25 | 26 | /** 27 | * Get node environment 28 | * @return {String} Environment name 29 | */ 30 | function getenv() { 31 | return process.env.NODE_ENV; 32 | } 33 | 34 | /** 35 | * Set node environment 36 | * @param {String} env Environment name 37 | */ 38 | function setenv(env) { 39 | process.env.NODE_ENV = env; 40 | getOptions(); 41 | } 42 | 43 | /** 44 | * Retrieve olive options 45 | */ 46 | function getOptions() { 47 | options = olive.getOptions(); 48 | } 49 | 50 | /** 51 | * Inject Content-Security-Policy meta tag 52 | */ 53 | function injectCSP() { 54 | var env = getenv(); 55 | var injectCSPOptions = { 56 | starttag: '', 57 | transform: function() { 58 | if (!options.hasOwnProperty('content-security-policy')) { return; } 59 | var csp = options['content-security-policy']; 60 | if (!csp.hasOwnProperty(env)) { return; } 61 | var meta = ''; 68 | return meta; 69 | } 70 | }; 71 | return $.inject(gulp.src(''), injectCSPOptions); 72 | } 73 | 74 | /** 75 | * Inject appinfo script 76 | */ 77 | function injectAppInfo() { 78 | var injectAppInfoScriptTag = { 79 | starttag: '', 80 | transform: function() { 81 | var script = ''; 82 | return script; 83 | } 84 | }; 85 | return $.inject(gulp.src(''), injectAppInfoScriptTag); 86 | } 87 | 88 | /** 89 | * Run unit tests 90 | * @param {Boolean} singleRun If true, runs only once 91 | * @param {Function} done Callback function 92 | */ 93 | function runUnitTests(singleRun, done) { 94 | var server = new karma.Server({ 95 | configFile: path.join(__dirname, 'karma.conf.js'), 96 | singleRun: singleRun, 97 | autoWatch: !singleRun, 98 | reporters: singleRun ? ['mocha', 'coverage'] : ['html'] 99 | }, done); 100 | server.start(); 101 | } 102 | 103 | /** 104 | * Debounced method to reload the unit test report page 105 | * @param {String} path Relative path to the changed file 106 | */ 107 | var reloadTestReport = _.debounce(_reloadTestReport, 50); 108 | function _reloadTestReport(path) { 109 | tinylr.changed({ 110 | body: { 111 | files: [path] 112 | } 113 | }); 114 | } 115 | 116 | /** 117 | * Debounced method to reload the browser 118 | * @param {String} path Relative path to the changed file 119 | */ 120 | var reloadBrowser = _.debounce(_reloadBrowser, 50); 121 | function _reloadBrowser(path, event) { 122 | if (event) { 123 | gutil.log(gutil.colors.cyan('File changed:'), gutil.colors.magenta(path)); 124 | } 125 | gutil.log(gutil.colors.cyan('Reloading Browsers...')); 126 | browserSync.reload(path); 127 | } 128 | 129 | /** 130 | * Create `appinfo.js` file in `js` directory under tmp path 131 | */ 132 | gulp.task('appinfo', function() { 133 | var tmpJsDir = path.join(options.paths.tmp, 'js'); 134 | var appInfoFile = path.join(tmpJsDir, 'appinfo.js'); 135 | 136 | var appName = pkg.name, 137 | appVersion = pkg.version; 138 | 139 | var scriptContent = 'appInfo={name:"' + appName + '",version:"' + appVersion + '"};'; 140 | mkdirp.sync(tmpJsDir); 141 | fs.writeFileSync(appInfoFile, scriptContent); 142 | }); 143 | 144 | /** 145 | * Inject @imports and compile Sass 146 | */ 147 | gulp.task('styles', function() { 148 | var sassOptions = { 149 | outputStyle: 'expanded' 150 | }; 151 | 152 | var sassFiles = gulp.src([ 153 | path.join(options.paths.src, 'app/**/*.scss'), 154 | path.join('!' + options.paths.src, 'app/styles/**/*.scss') 155 | ], { read: false }); 156 | 157 | var injectOptions = { 158 | transform: function(filePath) { 159 | filePath = filePath.replace(options.paths.src + '/app/', '../'); 160 | return '@import "' + filePath + '";'; 161 | }, 162 | addRootSlash: false, 163 | starttag: '// inject:scss', 164 | endtag: '// endinject' 165 | }; 166 | 167 | return gulp.src( 168 | path.join(options.paths.src, 'app/styles/app.scss')) 169 | .pipe($.inject(sassFiles, injectOptions)) 170 | .pipe(wiredep()) 171 | .pipe($.sourcemaps.init()) 172 | .pipe($.sass(sassOptions)).on('error', errorHandler('Sass')) 173 | .pipe($.autoprefixer()).on('error', errorHandler('Autoprefixer')) 174 | .pipe($.sourcemaps.write()) 175 | .pipe(gulp.dest(path.join(options.paths.tmp, 'css'))); 176 | }); 177 | 178 | /** 179 | * Lint the scripts and report issues if any 180 | */ 181 | gulp.task('lint', function() { 182 | return gulp.src(path.join(options.paths.src, 'app/**/*.js')) 183 | .pipe($.jshint()) 184 | .pipe($.jscs()).on('error', _.noop) 185 | .pipe($.jscsStylish.combineWithHintResults()) 186 | .pipe($.jshint.reporter('jshint-stylish')); 187 | }); 188 | 189 | /** 190 | * Inject stylesheets into `bower:css` and `inject:css` 191 | * and scripts into `inject:js` 192 | */ 193 | gulp.task('inject', ['styles', 'lint'], function() { 194 | var cssFiles = gulp.src([ 195 | path.join(options.paths.tmp, 'css/**/*.css') 196 | ], { read: false }); 197 | 198 | var jsFiles = gulp.src([ 199 | path.join(options.paths.src, 'app/**/*.js'), 200 | path.join('!' + options.paths.src, 'app/**/*.spec.js') 201 | ]) 202 | .pipe($.angularFilesort()).on('error', errorHandler('AngularFilesort')); 203 | 204 | // make the path relative 205 | var injectOptions = { 206 | ignorePath: [path.join(options.paths.src, 'app'), options.paths.tmp], 207 | addRootSlash: false 208 | }; 209 | 210 | return gulp.src(path.join(options.paths.src, '*.html')) 211 | .pipe(injectCSP()) // inject:csp 212 | .pipe(injectAppInfo()) // inject:appinfo 213 | .pipe($.inject(cssFiles, injectOptions)) // inject:css 214 | .pipe($.inject(jsFiles, injectOptions)) // inject:js 215 | .pipe(wiredep()) // bower:css 216 | .pipe(gulp.dest(options.paths.tmp)); 217 | 218 | /* Important: While using both, `wiredep` should always run after `inject` in the pipe */ 219 | }); 220 | 221 | /** 222 | * Watch for changes 223 | */ 224 | gulp.task('watch', ['inject'], function() { 225 | 226 | // Watch for changes in the root htmls (i.e. index.html) and in bower.json 227 | chokidar.watch([path.join(options.paths.src, '*.html'), 'bower.json'], {ignoreInitial: true}).on('change', function(_path) { 228 | gulp.start('inject', function() { 229 | reloadBrowser(_path, 'change'); 230 | reloadTestReport(_path, 'change'); 231 | }); 232 | }); 233 | 234 | // Watch for changes in all files inside the assets directory 235 | chokidar.watch(path.join(options.paths.src, 'assets'), {ignoreInitial: true}).on('all', function(event, _path) { 236 | reloadBrowser(_path, event); 237 | }); 238 | 239 | // Watch for changes in scss/js/html files inside app directory 240 | chokidar.watch(path.join(options.paths.src, 'app'), {ignoreInitial: true}).on('all', function(event, _path) { 241 | var ext = path.extname(_path); 242 | 243 | // Watch for change in `app/**/*.scss` 244 | if (ext === '.scss') { 245 | if (event === 'change') { 246 | gulp.start('styles', function() { 247 | reloadBrowser(_path, event); 248 | }); 249 | } else { 250 | gulp.start('inject', function() { 251 | reloadBrowser(_path, event); 252 | }); 253 | } 254 | } 255 | 256 | // Watch for changes in `app/**/*.js` 257 | else if (ext === '.js') { 258 | if (event === 'change') { 259 | gulp.start('lint', function() { 260 | reloadBrowser(_path, event); 261 | reloadTestReport(_path); 262 | }); 263 | } else { 264 | gulp.start('inject', function() { 265 | reloadBrowser(_path, event); 266 | reloadTestReport(_path); 267 | }); 268 | } 269 | } 270 | 271 | // Watch for change in `app/**/*.html` 272 | else if (ext === '.html') { 273 | reloadBrowser(_path, event); 274 | reloadTestReport(_path); 275 | } 276 | 277 | }); 278 | 279 | // Listen to livereload update for html report page 280 | tinylr.listen(35729); 281 | 282 | // Run unit test continuously 283 | runUnitTests(false); 284 | 285 | // Open report page in browser 286 | opn('http://localhost:' + options.ports.karma + '/debug.html'); 287 | }); 288 | 289 | /** 290 | * Serve via Browsersync 291 | */ 292 | gulp.task('serve', ['setenv:development', 'clean:tmp', 'appinfo', 'watch'], function() { 293 | var ports = options.ports || {}; 294 | browserSync.init({ 295 | port: ports.app || 3000, 296 | ui: { 297 | port: ports.bs || 3001 298 | }, 299 | server: { 300 | baseDir: [options.paths.tmp, path.join(options.paths.src, 'assets'), path.join(options.paths.src, 'app')], 301 | routes: { 302 | '/bower_components': 'bower_components' 303 | }, 304 | middleware: [ 305 | modRewrite(['!\\.\\w+$ /index.html [L]']), 306 | function (req, res, next) { 307 | res.setHeader('X-UA-Compatible', 'IE=Edge'); 308 | next(); 309 | } 310 | ] 311 | }, 312 | notify: false, 313 | logLevel: 'silent' 314 | }); 315 | }); 316 | 317 | /** 318 | * Run unit tests once 319 | */ 320 | gulp.task('test', ['setenv:test', 'lint'], function(done) { 321 | runUnitTests(true, done); 322 | }); 323 | 324 | /** 325 | * Run unit tests continuously 326 | */ 327 | gulp.task('test:auto', ['setenv:test', 'watch'], function() { 328 | runUnitTests(false); 329 | }); 330 | 331 | /** 332 | * Delete tmp directory 333 | */ 334 | gulp.task('clean:tmp', function() { 335 | del.sync(options.paths.tmp); // delete tmp dir 336 | mkdirp.sync(options.paths.tmp); // recreate it (empty) 337 | }); 338 | 339 | /** 340 | * Delete dist directory 341 | */ 342 | gulp.task('clean:dist', function() { 343 | del.sync([ 344 | options.paths.dist + '/**/*', 345 | '!' + options.paths.dist + '/.gitkeep' 346 | ], { dot: true }); 347 | }); 348 | 349 | /** 350 | * Delete both tmp and dist directories 351 | */ 352 | gulp.task('clean', function() { 353 | gulp.start('clean:tmp'); 354 | gulp.start('clean:dist'); 355 | }); 356 | 357 | /** 358 | * Set environment to development 359 | */ 360 | gulp.task('setenv:development', function() { 361 | setenv('development'); 362 | }); 363 | 364 | /** 365 | * Set environment to production 366 | */ 367 | gulp.task('setenv:production', function() { 368 | setenv('production'); 369 | }); 370 | 371 | /** 372 | * Set environment to test 373 | */ 374 | gulp.task('setenv:test', function() { 375 | setenv('test'); 376 | }); 377 | 378 | /** 379 | * Convert all angular html templates into a javascript template cache 380 | */ 381 | gulp.task('templates', function() { 382 | return gulp.src(path.join(options.paths.src, 'app/**/*.html')) 383 | .pipe($.minifyHtml({ 384 | empty: true, 385 | spare: true, 386 | quotes: true 387 | })) 388 | .pipe($.angularTemplatecache({ 389 | module: 'app' 390 | })) 391 | .pipe(gulp.dest(path.join(options.paths.tmp, 'js'))); 392 | }); 393 | 394 | /** 395 | * Build app html, css and js files 396 | */ 397 | gulp.task('app', ['inject', 'templates'], function() { 398 | var templateFiles = gulp.src(path.join(options.paths.tmp, 'js/templates.js'), { read: false }); 399 | var injectOptions = { 400 | starttag: '', 401 | ignorePath: options.paths.tmp, 402 | addRootSlash: false 403 | }; 404 | 405 | var htmlFilter = $.filter('*.html'); 406 | var cssFilter = $.filter('**/*.css'); 407 | var jsFilter = $.filter('**/*.js'); 408 | var assets = $.useref.assets(); 409 | 410 | return gulp.src(path.join(options.paths.tmp, '*.html')) 411 | .pipe(injectCSP()) 412 | .pipe($.inject(templateFiles, injectOptions)) 413 | .pipe(assets) 414 | .pipe($.rev()) 415 | .pipe(jsFilter) 416 | .pipe($.ngAnnotate()) 417 | .pipe($.uglify().on('error', errorHandler('Uglify'))) 418 | .pipe(jsFilter.restore()) 419 | .pipe(cssFilter) 420 | .pipe($.cleanCss({ 421 | processImport: false, 422 | keepSpecialComments: false 423 | })) 424 | .pipe(cssFilter.restore()) 425 | .pipe(assets.restore()) 426 | .pipe($.useref()) 427 | .pipe($.revReplace()) 428 | .pipe(htmlFilter) 429 | .pipe($.minifyHtml({ 430 | empty: true, 431 | spare: true, 432 | quotes: true, 433 | conditionals: true 434 | })) 435 | .pipe(htmlFilter.restore()) 436 | .pipe(gulp.dest(options.paths.dist)) 437 | .pipe($.size({ 438 | title: options.paths.dist, 439 | showFiles: true 440 | })); 441 | }); 442 | 443 | /** 444 | * Copy fonts from bower dependencies to dist/fonts 445 | * Note: Custom fonts are handled by `assets` task 446 | */ 447 | gulp.task('fonts', function () { 448 | return gulp.src($.mainBowerFiles()) 449 | .pipe($.filter('**/*.{eot,svg,ttf,woff,woff2}')) 450 | .pipe($.flatten()) 451 | .pipe(gulp.dest(path.join(options.paths.dist, 'fonts'))); 452 | }); 453 | 454 | /** 455 | * Copy content of the assets directory 456 | */ 457 | gulp.task('assets', function() { 458 | return gulp.src(path.join(options.paths.src, 'assets/**/*')) 459 | .pipe(gulp.dest(options.paths.dist)); 460 | }); 461 | 462 | /** 463 | * Build task 464 | */ 465 | gulp.task('build', [ 466 | 'setenv:production', 467 | 'clean', 468 | 'appinfo', 469 | 'app', 470 | 'fonts', 471 | 'assets' 472 | ]); 473 | 474 | /** 475 | * Default task 476 | */ 477 | gulp.task('default', [ 478 | 'build' 479 | ]); 480 | 481 | /** 482 | * Common implementation for an error handler of a gulp plugin 483 | * @param {String} title Short description of the error 484 | * @return {Function} Callback function 485 | */ 486 | function errorHandler(title) { 487 | return function(err) { 488 | gutil.log(gutil.colors.red('[' + title + ']'), err.toString()); 489 | this.emit('end'); 490 | }; 491 | } 492 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | /* jshint node:true */ 2 | 3 | 'use strict'; 4 | 5 | var fs = require('fs'), 6 | path = require('path'), 7 | wiredep = require('wiredep'), 8 | olive = require('olive'), 9 | oliveOptions = olive.getOptions(); 10 | 11 | function getFiles() { 12 | var wiredepOptions = { 13 | dependencies: true, 14 | devDependencies: true, 15 | directory: JSON.parse(fs.readFileSync('.bowerrc')).directory 16 | }; 17 | 18 | // bower js files and application js file 19 | return wiredep(wiredepOptions).js 20 | .concat([ 21 | path.join(oliveOptions.paths.tmp, 'js/appinfo.js'), 22 | path.join(oliveOptions.paths.src, 'app/**/*.js'), 23 | path.join(oliveOptions.paths.src, '**/*.html') 24 | ]); 25 | } 26 | 27 | module.exports = function(config) { 28 | 29 | var karmaConfig = { 30 | 31 | // base path that will be used to resolve all patterns (eg. files, exclude) 32 | basePath: '', 33 | 34 | // frameworks to use 35 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 36 | frameworks: ['jasmine', 'angular-filesort'], 37 | 38 | // further narrow the subset of files via karma-angular-filesort 39 | angularFilesort: { 40 | whitelist: [path.join(oliveOptions.paths.src, '/**/!(*.html|*.spec|*.mock).js')] 41 | }, 42 | 43 | // list of files / patterns to load in the browser 44 | files: getFiles(), 45 | 46 | // list of files to exclude 47 | exclude: [ 48 | ], 49 | 50 | // preprocess matching files before serving them to the browser 51 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 52 | preprocessors: { 53 | }, 54 | 55 | ngHtml2JsPreprocessor: { 56 | stripPrefix: oliveOptions.paths.src + '/app/', 57 | moduleName: 'app' 58 | }, 59 | 60 | // test results reporter to use 61 | // possible values: 'dots', 'progress', 'mocha', 'html', 'coverage' 62 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 63 | reporters: ['mocha', 'coverage'], 64 | 65 | // generate code coverage using Istanbul 66 | // https://github.com/karma-runner/karma-coverage 67 | coverageReporter: { 68 | type : 'html', 69 | dir : 'coverage' 70 | }, 71 | 72 | // web server port 73 | port: oliveOptions.ports.karma, 74 | 75 | // enable / disable colors in the output (reporters and logs) 76 | colors: true, 77 | 78 | // level of logging 79 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 80 | logLevel: config.LOG_WARN, 81 | 82 | // enable / disable watching file and executing tests whenever any file changes 83 | autoWatch: false, 84 | 85 | // start these browsers 86 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 87 | browsers: ['PhantomJS'], 88 | 89 | // Continuous Integration mode 90 | // if true, Karma captures browsers, runs the tests and exits 91 | singleRun: true 92 | }; 93 | 94 | // preprocessor for converting HTML files to AngularJS templates. 95 | karmaConfig.preprocessors[oliveOptions.paths.src + '/**/*.html'] = ['ng-html2js']; 96 | 97 | // source files, that you wanna generate coverage for 98 | // do not include tests or libraries 99 | // (these files will be instrumented by Istanbul) 100 | karmaConfig.preprocessors[oliveOptions.paths.src + '/**/*.js'] = ['coverage']; 101 | 102 | config.set(karmaConfig); 103 | }; 104 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "seed-angular", 3 | "version": "0.0.0", 4 | "description": "Olive seed for AngularJS", 5 | "repository": "olivejs/seed-angular", 6 | "keywords": [ 7 | "olive-seed", 8 | "scaffold", 9 | "framework", 10 | "front-end", 11 | "app", 12 | "angular" 13 | ], 14 | "author": "The Olive Team", 15 | "scripts": { 16 | "test": "gulp test", 17 | "start": "gulp serve", 18 | "clean": "gulp clean", 19 | "build": "gulp build" 20 | }, 21 | "dependencies": {}, 22 | "devDependencies": { 23 | "browser-sync": "^2.7.13", 24 | "chokidar": "^1.0.5", 25 | "connect-modrewrite": "^0.8.2", 26 | "del": "^1.2.0", 27 | "gulp": "^3.9.0", 28 | "gulp-angular-filesort": "^1.1.1", 29 | "gulp-angular-templatecache": "^1.7.0", 30 | "gulp-autoprefixer": "^2.3.1", 31 | "gulp-clean-css": "^2.0.7", 32 | "gulp-filter": "^2.0.2", 33 | "gulp-flatten": "^0.1.1", 34 | "gulp-inject": "^1.3.1", 35 | "gulp-jscs": "^2.0.0", 36 | "gulp-jscs-stylish": "^1.1.1", 37 | "gulp-jshint": "^1.11.2", 38 | "gulp-load-plugins": "^1.0.0-rc.1", 39 | "gulp-minify-html": "^1.0.3", 40 | "gulp-ng-annotate": "^1.0.0", 41 | "gulp-replace": "^0.5.3", 42 | "gulp-rev": "^5.1.0", 43 | "gulp-rev-replace": "^0.4.2", 44 | "gulp-sass": "^2.0.3", 45 | "gulp-size": "^1.2.3", 46 | "gulp-sourcemaps": "^1.5.2", 47 | "gulp-uglify": "^1.2.0", 48 | "gulp-useref": "^1.2.0", 49 | "gulp-util": "^3.0.6", 50 | "jasmine-core": "^2.3.4", 51 | "jshint-stylish": "^2.0.1", 52 | "karma": "^0.13.22", 53 | "karma-angular-filesort": "^1.0.1", 54 | "karma-coverage": "^1.0.0", 55 | "karma-jasmine": "^1.0.2", 56 | "karma-jasmine-html-reporter-livereload": "^1.0.0", 57 | "karma-mocha-reporter": "^2.0.3", 58 | "karma-ng-html2js-preprocessor": "^1.0.0", 59 | "karma-phantomjs-launcher": "^1.0.0", 60 | "lodash": "^4.12.0", 61 | "main-bower-files": "^2.9.0", 62 | "mkdirp": "^0.5.1", 63 | "olive": "0.0.8", 64 | "opn": "^3.0.2", 65 | "phantomjs-prebuilt": "^2.1.7", 66 | "tiny-lr": "0.1.4", 67 | "wiredep": "^2.2.2" 68 | }, 69 | "engines": { 70 | "node": ">=0.12.0" 71 | }, 72 | "license": "MIT" 73 | } 74 | -------------------------------------------------------------------------------- /src/app/config/constants.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Libraries defined at global scope can be registered here as constant service. 3 | * App related constants can also be registered here. 4 | */ 5 | 6 | (function(window) { 7 | 8 | 'use strict'; 9 | 10 | angular 11 | .module('app') 12 | .constant('$appInfo', window.appInfo); 13 | 14 | })(this); 15 | -------------------------------------------------------------------------------- /src/app/config/modules.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | 'use strict'; 4 | 5 | angular 6 | .module('app', [ 7 | 'ngTouch', 8 | 'ui.router', 9 | 'ngMaterial' 10 | // Add your dependencies here 11 | ]); 12 | 13 | })(); 14 | -------------------------------------------------------------------------------- /src/app/config/routes.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | 'use strict'; 4 | 5 | angular 6 | .module('app') 7 | .config(routesConfig); 8 | 9 | function routesConfig($stateProvider, $urlRouterProvider, $locationProvider) { 10 | 11 | // Use html5Mode for modern browsers, 12 | // and fallback to #! for older browsers and search engine crawlers. 13 | $locationProvider.html5Mode(true).hashPrefix('!'); 14 | 15 | $stateProvider 16 | 17 | .state('home', { 18 | url: '/', 19 | templateUrl: 'pods/home/home.html', 20 | controller: 'HomeController' 21 | }); 22 | 23 | // For any unmatched url, redirect to / 24 | $urlRouterProvider.otherwise('/'); 25 | } 26 | 27 | })(); 28 | -------------------------------------------------------------------------------- /src/app/config/run.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | 'use strict'; 4 | 5 | angular 6 | .module('app') 7 | .run(runBlock); 8 | 9 | function runBlock($log, $appInfo) { 10 | 11 | //--> Do your magic here 12 | 13 | $log.debug($appInfo.name + ' ' + $appInfo.version); 14 | } 15 | 16 | })(); 17 | -------------------------------------------------------------------------------- /src/app/directives/repoinfo/repoinfo.directive.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | 'use strict'; 4 | 5 | angular 6 | .module('app') 7 | .directive('repoinfo', directive); 8 | 9 | function directive(Github, $log) { 10 | return { 11 | restrict: 'A', 12 | templateUrl: 'directives/repoinfo/repoinfo.html', 13 | scope: { 14 | repo: '=' 15 | }, 16 | controller: RepoinfoController 17 | }; 18 | 19 | function RepoinfoController($scope) { 20 | 21 | $scope.repo.starsCount = '- -'; 22 | $scope.repo.forksCount = '- -'; 23 | 24 | Github.getRepo($scope.repo.name) 25 | .then(function(data) { 26 | $scope.repo.url = data.html_url; 27 | $scope.repo.starsCount = data.stargazers_count; 28 | $scope.repo.forksCount = data.forks_count; 29 | }) 30 | .catch(function(data) { 31 | $log.warn('GitHub: ' + data.message); 32 | }); 33 | } 34 | } 35 | 36 | })(); 37 | -------------------------------------------------------------------------------- /src/app/directives/repoinfo/repoinfo.html: -------------------------------------------------------------------------------- 1 |
{{appInfo.name}}
29 |{{appInfo.version}}
35 |