├── .bowerrc ├── .editorconfig ├── .gitignore ├── .jshintrc ├── Gulpfile.js ├── LICENSE ├── README.md ├── app ├── index.html ├── scripts │ ├── app.js │ ├── controllers.js │ ├── services.js │ └── templates.js ├── stylesheets │ ├── fonts.styl │ ├── main.css │ └── main.styl └── views │ ├── post-detail.tpl.html │ └── post-list.tpl.html ├── bower.json └── package.json /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "app/lib" 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | indent_style = tab 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | .DS_Store 4 | app/lib 5 | dist 6 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 4, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "undef": true, 16 | "unused": true, 17 | "strict": true, 18 | "trailing": true, 19 | "smarttabs": true, 20 | "jquery": true, 21 | "globals": { 22 | "angular": false 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Gulpfile.js: -------------------------------------------------------------------------------- 1 | // File: Gulpfile.js 2 | 'use strict'; 3 | 4 | var gulp = require('gulp'), 5 | webserver = require('gulp-webserver'), 6 | connect = require('gulp-connect'), 7 | stylus = require('gulp-stylus'), 8 | nib = require('nib'), 9 | jshint = require('gulp-jshint'), 10 | stylish = require('jshint-stylish'), 11 | inject = require('gulp-inject'), 12 | wiredep = require('wiredep').stream, 13 | gulpif = require('gulp-if'), 14 | minifyCss = require('gulp-minify-css'), 15 | useref = require('gulp-useref'), 16 | uglify = require('gulp-uglify'), 17 | uncss = require('gulp-uncss'), 18 | angularFilesort = require('gulp-angular-filesort'), 19 | templateCache = require('gulp-angular-templatecache'), 20 | historyApiFallback = require('connect-history-api-fallback'); 21 | 22 | 23 | var path = { 24 | root : './app/', 25 | dist : './dist/' 26 | } 27 | 28 | // Servidor web de desarrollo 29 | gulp.task('server', function(){ 30 | gulp.src( path.root ) 31 | .pipe(webserver({ 32 | host : "0.0.0.0", 33 | port : 8080, 34 | livereload : true, 35 | fallback : 'index.html', 36 | middleware : [ historyApiFallback ] 37 | })); 38 | }); 39 | 40 | // Servidor web para probar el entorno de producción 41 | gulp.task('server-dist', function() { 42 | gulp.src( path.dist ) 43 | .pipe(webserver({ 44 | host : "0.0.0.0", 45 | port : 8080, 46 | livereload : true, 47 | fallback : 'index.html', 48 | middleware : [ historyApiFallback ] 49 | })); 50 | }); 51 | 52 | // Busca errores en el JS y nos los muestra por pantalla 53 | gulp.task('jshint', function() { 54 | return gulp.src('./app/scripts/**/*.js') 55 | .pipe(jshint('.jshintrc')) 56 | .pipe(jshint.reporter('jshint-stylish')) 57 | .pipe(jshint.reporter('fail')); 58 | }); 59 | 60 | // Preprocesa archivos Stylus a CSS y recarga los cambios 61 | gulp.task('css', function() { 62 | gulp.src('./app/stylesheets/main.styl') 63 | .pipe(stylus({ use: nib() })) 64 | .pipe(gulp.dest('./app/stylesheets')) 65 | .pipe(connect.reload()); 66 | }); 67 | 68 | // Recarga el navegador cuando hay cambios en el HTML 69 | gulp.task('html', function() { 70 | gulp.src('./app/**/*.html') 71 | .pipe(connect.reload()); 72 | }); 73 | 74 | // Busca en las carpetas de estilos y javascript los archivos 75 | // para inyectarlos en el index.html 76 | gulp.task('inject', function() { 77 | return gulp.src('index.html', {cwd: './app'}) 78 | .pipe(inject( 79 | gulp.src(['./app/scripts/**/*.js']).pipe(angularFilesort()), { 80 | read: false, 81 | ignorePath: '/app' 82 | })) 83 | .pipe(inject( 84 | gulp.src(['./app/stylesheets/**/*.css']), { 85 | read: false, 86 | ignorePath: '/app' 87 | } 88 | )) 89 | .pipe(gulp.dest('./app')); 90 | }); 91 | 92 | // Inyecta las librerias que instalemos vía Bower 93 | gulp.task('wiredep', function () { 94 | gulp.src('./app/index.html') 95 | .pipe(wiredep({ 96 | directory: './app/lib' 97 | })) 98 | .pipe(gulp.dest('./app')); 99 | }); 100 | 101 | // Compila las plantillas HTML parciales a JavaScript 102 | // para ser inyectadas por AngularJS y minificar el código 103 | gulp.task('templates', function() { 104 | gulp.src('./app/views/**/*.tpl.html') 105 | .pipe(templateCache({ 106 | root: 'views/', 107 | module: 'blog.templates', 108 | standalone: true 109 | })) 110 | .pipe(gulp.dest('./app/scripts')); 111 | }); 112 | 113 | // Comprime los archivos CSS y JS enlazados en el index.html 114 | // y los minifica. 115 | gulp.task('compress', function() { 116 | gulp.src('./app/index.html') 117 | .pipe(useref()) 118 | .pipe(gulpif('*.js', uglify({mangle: false }))) 119 | .pipe(gulpif('*.css', minifyCss())) 120 | .pipe(gulp.dest('./dist')); 121 | }); 122 | 123 | // Elimina el CSS que no es utilizado para reducir el peso del archivo 124 | gulp.task('uncss', function() { 125 | gulp.src('./dist/css/style.min.css') 126 | .pipe(uncss({ 127 | html: ['./app/index.html', './app/views/post-list.tpl.html', './app/views/post-detail.tpl.html'] 128 | })) 129 | .pipe(gulp.dest('./dist/css')); 130 | }); 131 | 132 | // Copia el contenido de los estáticos e index.html al directorio 133 | // de producción sin tags de comentarios 134 | gulp.task('copy', function() { 135 | gulp.src('./app/index.html') 136 | .pipe(useref()) 137 | .pipe(gulp.dest('./dist')); 138 | gulp.src('./app/lib/fontawesome/fonts/**') 139 | .pipe(gulp.dest('./dist/fonts')); 140 | }); 141 | 142 | // Vigila cambios que se produzcan en el código 143 | // y lanza las tareas relacionadas 144 | gulp.task('watch', function() { 145 | gulp.watch(['./app/**/*.html'], ['html', 'templates']); 146 | gulp.watch(['./app/stylesheets/**/*.styl'], ['css', 'inject']); 147 | gulp.watch(['./app/scripts/**/*.js', './Gulpfile.js'], ['jshint', 'inject']); 148 | gulp.watch(['./bower.json'], ['wiredep']); 149 | }); 150 | 151 | gulp.task('default', ['server', 'templates', 'inject', 'wiredep', 'watch']); 152 | gulp.task('build', ['templates', 'compress', 'copy', 'uncss']); 153 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Carlos Azaustre 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 | # angularapp-gulp-boilerplate 2 | 3 | A boilerplate to start an Angular webapp with Gulp automated tasks. 4 | 5 | ## Dependencies 6 | - Angular.js 7 | - Angular-Route 8 | - Angular-Resource 9 | - Bootstrap 10 | - Fontawesome 11 | 12 | ## Usage 13 | ### Install global 14 | ``` 15 | $ npm install -g stylus 16 | $ npm install -g gulp 17 | $ npm install -g bower 18 | ``` 19 | 20 | ### Install local 21 | ``` 22 | $ npm install 23 | $ bower install 24 | ``` 25 | ### Development mode 26 | ``` 27 | $ gulp 28 | ``` 29 | ### Go-to-production 30 | ``` 31 | $ gulp build 32 | $ gulp server-dist 33 | ``` 34 | 35 | ## License 36 | 37 | **The MIT License (MIT)** 38 | 39 | Copyright (c) 2014 Carlos Azaustre 40 | 41 | Permission is hereby granted, free of charge, to any person obtaining a copy 42 | of this software and associated documentation files (the "Software"), to deal 43 | in the Software without restriction, including without limitation the rights 44 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 45 | copies of the Software, and to permit persons to whom the Software is 46 | furnished to do so, subject to the following conditions: 47 | 48 | The above copyright notice and this permission notice shall be included in all 49 | copies or substantial portions of the Software. 50 | 51 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 52 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 53 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 54 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 55 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 56 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 57 | SOFTWARE. 58 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | AngularApp Boilerplate 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 30 |
31 |
32 | 35 |
36 |
37 | 38 |
39 | 40 | 41 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /app/scripts/app.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | /* @ngInject */ 5 | angular 6 | .module('app', ['ngRoute', 'app.controllers', 'app.services', 'blog.templates']) 7 | .config(config); 8 | 9 | /* @ngInject */ 10 | function config ($locationProvider, $routeProvider) { 11 | $locationProvider.html5Mode(true); 12 | 13 | $routeProvider 14 | .when('/', { 15 | templateUrl: 'views/post-list.tpl.html', 16 | controller: 'PostListCtrl', 17 | controllerAs: 'postlist' 18 | }) 19 | .when('/:postId', { 20 | templateUrl: 'views/post-detail.tpl.html', 21 | controller: 'PostDetailCtrl', 22 | controllerAs: 'postdetail' 23 | }); 24 | } 25 | 26 | })(); 27 | -------------------------------------------------------------------------------- /app/scripts/controllers.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | /* @ngInject */ 5 | angular 6 | .module('app.controllers', ['app.services']) 7 | .controller('PostListCtrl', PostListCtrl) 8 | .controller('PostDetailCtrl', PostDetailCtrl); 9 | 10 | /* @ngInject */ 11 | function PostListCtrl (Post) { 12 | this.posts = Post.query(); 13 | } 14 | 15 | /* @ngInject */ 16 | function PostDetailCtrl ($routeParams, Post, Comment, User) { 17 | this.user = {}; 18 | this.post = {}; 19 | this.comments = {}; 20 | 21 | var self = this; 22 | 23 | Post.query({ id: $routeParams.postId }) 24 | .$promise.then( 25 | function (value) { 26 | self.post = value[0]; 27 | self.user = User.query({ id: self.post.userId }); 28 | }, 29 | function (error) { 30 | console.log('ERROR: ' + error); 31 | } 32 | ); 33 | 34 | this.comments = Comment.query({ postId: $routeParams.postId }); 35 | 36 | } 37 | 38 | })(); 39 | -------------------------------------------------------------------------------- /app/scripts/services.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | /* @ngInject */ 5 | angular 6 | .module('app.services', ['ngResource']) 7 | .constant('BaseUrl', 'http://jsonplaceholder.typicode.com') 8 | .factory('Post', Post) 9 | .factory('Comment', Comment) 10 | .factory('User', User); 11 | 12 | /* @ngInject */ 13 | function Post ($resource, BaseUrl) { 14 | return $resource(BaseUrl + '/posts/:postId', 15 | { postId: '@_id' } 16 | ); 17 | } 18 | 19 | /* @ngInject */ 20 | function Comment ($resource, BaseUrl) { 21 | return $resource(BaseUrl + '/comments/:commentId', 22 | { commentId: '@_id' } 23 | ); 24 | } 25 | 26 | /* @ngInject */ 27 | function User ($resource, BaseUrl) { 28 | return $resource(BaseUrl + '/users/:userId', 29 | { userId: '@_id' } 30 | ); 31 | } 32 | 33 | })(); 34 | -------------------------------------------------------------------------------- /app/scripts/templates.js: -------------------------------------------------------------------------------- 1 | angular.module("blog.templates", []).run(["$templateCache", function($templateCache) {$templateCache.put("views/post-detail.tpl.html","
\r\n
\r\n

{{ postdetail.post.title }}

\r\n
\r\n

\r\n {{ postdetail.post.body }}\r\n

\r\n

\r\n Escrito por: {{ postdetail.user[0].name }}\r\n {{ postdetail.user[0].email }}\r\n

\r\n
\r\n
\r\n\r\n"); 2 | $templateCache.put("views/post-list.tpl.html","\r\n");}]); -------------------------------------------------------------------------------- /app/stylesheets/fonts.styl: -------------------------------------------------------------------------------- 1 | @font-face 2 | font-family 'Raleway-Light' 3 | font-style normal 4 | font-weight 300 5 | src local('Raleway Light'), local('Raleway-Light'), url('//themes.googleusercontent.com/static/fonts/raleway/v7/-_Ctzj9b56b8RgXW8FArib3hpw3pgy2gAi-Ip7WPMi0.woff') format('woff'); 6 | 7 | @font-face 8 | font-family 'Raleway' 9 | font-style normal 10 | font-weight 400 11 | src local('Raleway'), url('//themes.googleusercontent.com/static/fonts/raleway/v7/cIFypx4yrWPDz3zOxk7hIQLUuEpTyoUstqEm5AMlJo4.woff') format('woff'); 12 | -------------------------------------------------------------------------------- /app/stylesheets/main.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Raleway-Light'; 3 | font-style: normal; 4 | font-weight: 300; 5 | src: local('Raleway Light'), local('Raleway-Light'), url("//themes.googleusercontent.com/static/fonts/raleway/v7/-_Ctzj9b56b8RgXW8FArib3hpw3pgy2gAi-Ip7WPMi0.woff") format('woff'); 6 | } 7 | @font-face { 8 | font-family: 'Raleway'; 9 | font-style: normal; 10 | font-weight: 400; 11 | src: local('Raleway'), url("//themes.googleusercontent.com/static/fonts/raleway/v7/cIFypx4yrWPDz3zOxk7hIQLUuEpTyoUstqEm5AMlJo4.woff") format('woff'); 12 | } 13 | body { 14 | font-family: 'Raleway-Light'; 15 | font-size: 16px; 16 | } 17 | .blog-post-list { 18 | list-style-type: none; 19 | } 20 | .blog-post-list .blog-post-link { 21 | font-family: 'Raleway'; 22 | margin: 0.5em 0 1em 0; 23 | padding: 0 0 0.25em 0; 24 | border-bottom: 1px solid #ddd; 25 | } 26 | .blog-post .blog-post-header h1 { 27 | font-family: 'Raleway'; 28 | } 29 | .comments .comments-list { 30 | list-style-type: none; 31 | } 32 | .comments .comments-list .comment-item { 33 | border: 1px solid #ddd; 34 | border-radius: 5px; 35 | padding: 0.5em; 36 | margin: 0.5em; 37 | } 38 | .comments .comments-list .comment-item .comment-author { 39 | font-family: 'Raleway'; 40 | font-weight: bold; 41 | } 42 | .comments .comments-list .comment-item .comment-body { 43 | font-style: italic; 44 | } 45 | -------------------------------------------------------------------------------- /app/stylesheets/main.styl: -------------------------------------------------------------------------------- 1 | @import 'fonts' 2 | 3 | // colores 4 | b_silver = #ddd 5 | 6 | body 7 | font-family 'Raleway-Light' 8 | font-size 16px 9 | 10 | .blog-post-list 11 | list-style-type none 12 | 13 | .blog-post-link 14 | font-family 'Raleway' 15 | margin .5em 0 1em 0 16 | padding 0 0 .25em 0 17 | border-bottom 1px solid b_silver 18 | 19 | 20 | .blog-post 21 | 22 | .blog-post-header 23 | h1 24 | font-family 'Raleway' 25 | 26 | .comments 27 | 28 | .comments-list 29 | list-style-type none 30 | 31 | .comment-item 32 | border 1px solid b_silver 33 | border-radius 5px 34 | padding .5em 35 | margin .5em 36 | 37 | .comment-author 38 | font-family 'Raleway' 39 | font-weight bold 40 | 41 | .comment-body 42 | font-style italic 43 | -------------------------------------------------------------------------------- /app/views/post-detail.tpl.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{ postdetail.post.title }}

4 |
5 |

6 | {{ postdetail.post.body }} 7 |

8 |

9 | Escrito por: {{ postdetail.user[0].name }} 10 | {{ postdetail.user[0].email }} 11 |

12 |
13 |
14 | 27 | -------------------------------------------------------------------------------- /app/views/post-list.tpl.html: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angularapp-gulp-bp", 3 | "version": "0.0.1", 4 | "description": "", 5 | "dependencies": { 6 | "angular": "~1.2.21", 7 | "bootstrap": "~3.2.0", 8 | "fontawesome": "~4.1.0", 9 | "angular-route": "~1.2.21", 10 | "angular-resource": "~1.2.21" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angularapp-gulp-bp", 3 | "version": "0.0.1", 4 | "description": "A boilerplate to start Angular webapp with Gulp automated tasks", 5 | "author": "Carlos Azaustre http://carlosazaustre.es", 6 | "keywords": [ 7 | "angular", 8 | "webapp", 9 | "boilerplate", 10 | "gulp", 11 | "javascript" 12 | ], 13 | "license": { 14 | "type": "MIT", 15 | "url": "https://github.com/carlosazaustre/angularapp-gulp-boilerplate/blob/master/LICENSE" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/carlosazaustre/angularapp-gulp-boilerplate" 20 | }, 21 | "dependencies": {}, 22 | "devDependencies": { 23 | "connect-history-api-fallback": "0.0.4", 24 | "gulp": "^3.8.6", 25 | "gulp-angular-filesort": "^1.0.2", 26 | "gulp-angular-templatecache": "^1.2.1", 27 | "gulp-connect": "^2.0.6", 28 | "gulp-if": "^1.2.4", 29 | "gulp-inject": "^1.0.1", 30 | "gulp-jshint": "^1.8.0", 31 | "gulp-minify-css": "^0.3.7", 32 | "gulp-newer": "^0.3.0", 33 | "gulp-stylus": "^1.3.0", 34 | "gulp-uglify": "^0.3.1", 35 | "gulp-uncss": "^0.4.5", 36 | "gulp-useref": "^0.6.0", 37 | "gulp-webserver": "^0.9.1", 38 | "jshint-stylish": "^0.4.0", 39 | "nib": "^1.0.3", 40 | "wiredep": "^1.8.2" 41 | } 42 | } 43 | --------------------------------------------------------------------------------