├── .bowerrc ├── .editorconfig ├── .gitignore ├── .jshintrc ├── .travis.yml ├── .yo-rc.json ├── README.md ├── bower.json ├── e2e ├── .jshintrc ├── main.po.js └── main.spec.js ├── gulp ├── .jshintrc ├── build.js ├── conf.js ├── deploy.js ├── e2e-tests.js ├── inject.js ├── scripts.js ├── server.js ├── styles.js ├── unit-tests.js └── watch.js ├── gulpfile.js ├── karma.conf.js ├── package.json ├── protractor.conf.js ├── script ├── fixmyjs.sh └── formatCode.sh └── src ├── app ├── components │ └── navbar │ │ ├── navbar.directive.js │ │ ├── navbar.html │ │ └── navbar.scss ├── index.config.js ├── index.constants.js ├── index.module.js ├── index.route.js ├── index.run.js ├── index.scss └── main │ ├── main.controller.js │ ├── main.controller.spec.js │ └── main.html ├── assets └── images │ └── .gitkeep ├── favicon.ico └── index.html /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | bower_components/ 3 | .sass-cache/ 4 | .idea/ 5 | .tmp/ 6 | dist/ 7 | .fuse* -------------------------------------------------------------------------------- /.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 | "validthis": true, 11 | "jasmine": true, 12 | "globals": { 13 | "angular": false, 14 | "inject": false, 15 | "module": false 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.11" 4 | - "0.10" 5 | before_script: 6 | - npm install -g yo bower grunt-cli gulp 7 | - npm install 8 | - bower install 9 | script: gulp 10 | notifications: 11 | email: false 12 | after_script: NODE_ENV=test istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage -------------------------------------------------------------------------------- /.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-gulp-angular": { 3 | "version": "0.12.1", 4 | "props": { 5 | "angularVersion": "~1.4.0", 6 | "angularModules": [{ 7 | "key": "animate", 8 | "module": "ngAnimate" 9 | }, { 10 | "key": "cookies", 11 | "module": "ngCookies" 12 | }, { 13 | "key": "touch", 14 | "module": "ngTouch" 15 | }, { 16 | "key": "sanitize", 17 | "module": "ngSanitize" 18 | }], 19 | "jQuery": { 20 | "key": "jquery2" 21 | }, 22 | "resource": { 23 | "key": "angular-resource", 24 | "module": "ngResource" 25 | }, 26 | "router": { 27 | "key": "ui-router", 28 | "module": "ui.router" 29 | }, 30 | "ui": { 31 | "key": "bootstrap", 32 | "module": null 33 | }, 34 | "bootstrapComponents": { 35 | "key": "ui-bootstrap", 36 | "module": "ui.bootstrap" 37 | }, 38 | "cssPreprocessor": { 39 | "key": "node-sass", 40 | "extension": "scss" 41 | }, 42 | "jsPreprocessor": { 43 | "key": "none", 44 | "extension": "js", 45 | "srcExtension": "js" 46 | }, 47 | "htmlPreprocessor": { 48 | "key": "none", 49 | "extension": "html" 50 | }, 51 | "foundationComponents": { 52 | "name": null, 53 | "version": null, 54 | "key": null, 55 | "module": null 56 | }, 57 | "paths": { 58 | "src": "src", 59 | "dist": "dist", 60 | "e2e": "e2e", 61 | "tmp": ".tmp" 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Angular Seed App 2 | 3 | [](https://travis-ci.org/PauloLuan/angular-gulp-seed) 4 | [](https://coveralls.io/r/PauloLuan/angular-gulp-seed) 5 | [](https://www.bithound.io/github/PauloLuan/angular-gulp-seed/master) 6 | [")](https://david-dm.org/PauloLuan/angular-gulp-seed) 7 | 8 | 9 | This project is an application skeleton for a typical AngularJS web app. You can use it to quickly bootstrap your angular webapp projects and dev environment for these projects. 10 | 11 | The seed contains a sample AngularJS application and is preconfigured to install the Angular framework and a bunch of development and testing tools for instant web development gratification. 12 | 13 | **In This Documentation** 14 | 15 | 1. [Getting Started](#getting-started) 16 | 2. [Gulp Tasks](#gulp-tasks) 17 | 3. [File Structure](#file-structure) 18 | 4. [License](#license) 19 | 20 | ## Getting Started 21 | 22 | ### Dependencies 23 | Make sure these are installed first. 24 | 25 | * [Node.js](http://nodejs.org) 26 | * [Gulp](http://gulpjs.com) 27 | 28 | ### Quick Start 29 | 30 | 1. In bash/terminal/command line, `cd` into your project directory. 31 | 2. Run `npm install -g yo bower grunt-cli gulp && npm install && bower install` to install required dependencies. 32 | 3. When it's done installing, run one of the task runners to get going: 33 | 34 | ## Gulp tasks 35 | 36 | * `gulp` or `gulp build` to build an optimized version of your application in `/dist` 37 | * `gulp serve` to launch a browser sync server on your source files 38 | * `gulp serve:dist` to launch a server on your optimized application 39 | * `gulp test` to launch your unit tests with Karma 40 | * `gulp test:auto` to launch your unit tests with Karma in watch mode 41 | * `gulp protractor` to launch your e2e tests with Protractor 42 | * `gulp protractor:dist` to launch your e2e tests with Protractor on the dist files 43 | 44 | ### Features included in the gulpfile 45 | * *useref* : allow configuration of your files in comments of your HTML file 46 | * *ngAnnotate* : convert simple injection to complete syntax to be minification proof 47 | * *uglify* : optimize all your JavaScript 48 | * *csso* : optimize all your CSS 49 | * *rev* : add a hash in the file names to prevent browser cache problems 50 | * *watch* : watch your source files and recompile them automatically 51 | * *jshint* : JavaScript code linter 52 | * *imagemin* : all your images will be optimized at build 53 | * *Unit test (karma)* : out of the box unit test configuration with karma 54 | * *e2e test (protractor)* : out of the box e2e test configuration with protractor 55 | * *browser sync* : full-featured development web server with livereload and devices sync 56 | * *angular-templatecache* : all HTML partials will be converted to JS to be bundled in the application 57 | 58 | ### Frameworks used on this project: 59 | 60 | AngularJS: HTML enhanced for web apps! 61 | https://angularjs.org/ 62 | 63 | Angular Material Design: The Angular reference implementation of the Google's Material Design specification. 64 | https://material.angularjs.org/#/ 65 | 66 | Sass (Node): Node.js binding to libsass, the C version of the popular stylesheet preprocessor, Sass. 67 | https://github.com/sass/node-sass 68 | 69 | Jasmine: Behavior-Driven JavaScript. 70 | http://jasmine.github.io/ 71 | 72 | BrowserSync: Time-saving synchronised browser testing. 73 | http://browsersync.io/ 74 | 75 | GulpJS: The streaming build system. 76 | http://gulpjs.com/ 77 | 78 | Protractor: End to end test framework for AngularJS applications built on top of WebDriverJS. 79 | https://github.com/angular/protractor 80 | 81 | Karma: Spectacular Test Runner for JavaScript. 82 | http://karma-runner.github.io/ 83 | 84 | 85 | ## File Structure 86 | 87 | [Best Practice Recommendations for Angular App Structure](https://docs.google.com/document/d/1XXMvReO8-Awi1EZXAXS4PzDzdNvV6pGcuaF4Q9821Es/pub) 88 | 89 | The root directory generated for a app with name `gulpAngular` : 90 |
91 | ├── src/ 92 | │ ├── app/ 93 | │ │ ├── components/ 94 | │ │ │ └── navbar/ 95 | │ │ │ │ ├── navbar.controller.js 96 | │ │ │ │ └── navbar.html 97 | │ │ ├── main/ 98 | │ │ │ ├── main.controller.js 99 | │ │ │ ├── main.controller.spec.js 100 | │ │ │ └── main.html 101 | │ │ └── index.js 102 | │ │ └── index.(css|less|scss) 103 | │ │ └── vendor.(css|less|scss) 104 | │ ├── assets/ 105 | │ │ └── images/ 106 | │ ├── 404.html 107 | │ ├── favico.ico 108 | │ └── index.html 109 | ├── gulp/ 110 | ├── e2e/ 111 | ├── bower_components/ 112 | ├── nodes_modules/ 113 | ├── .bowerrc 114 | ├── .editorconfig 115 | ├── .gitignore 116 | ├── .jshintrc 117 | ├── bower.json 118 | ├── gulpfile.js 119 | ├── karma.conf.js 120 | ├── package.json 121 | ├── protractor.conf.js 122 |123 | 124 | ## License 125 | 126 | Angular Seed is licensed under the [MIT License](http://gomakethings.com/mit/). -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angularSeedApp", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "angular-animate": "~1.4.0", 6 | "angular-cookies": "~1.4.0", 7 | "angular-touch": "~1.4.0", 8 | "angular-sanitize": "~1.4.0", 9 | "jquery": "~2.1.4", 10 | "angular-resource": "~1.4.0", 11 | "angular-ui-router": "~0.2.15", 12 | "bootstrap-sass-official": "~3.3.4", 13 | "angular-bootstrap": "~0.13.0", 14 | "malarkey": "yuanqing/malarkey#~1.3.0", 15 | "toastr": "~2.1.1", 16 | "moment": "~2.10.3", 17 | "animate.css": "~3.3.0", 18 | "angular": "~1.4.0", 19 | "fontawesome": "~4.3.0", 20 | "angular-markdown-filter": "~1.2.0" 21 | }, 22 | "devDependencies": { 23 | "angular-mocks": "~1.4.0" 24 | }, 25 | "resolutions": { 26 | "jquery": "~2.1.4", 27 | "angular": "~1.4.0" 28 | } 29 | } -------------------------------------------------------------------------------- /e2e/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.jshintrc", 3 | "globals": { 4 | "browser": false, 5 | "element": false, 6 | "by": false, 7 | "$": false, 8 | "$$": false 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /e2e/main.po.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file uses the Page Object pattern to define the main page for tests 3 | * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ 4 | */ 5 | 6 | 'use strict'; 7 | 8 | var MainPage = function() { 9 | this.jumbEl = element(by.css('.jumbotron')); 10 | this.h1El = this.jumbEl.element(by.css('h1')); 11 | this.imgEl = this.jumbEl.element(by.css('img')); 12 | this.thumbnailEls = element(by.css('body')).all(by.repeater('awesomeThing in main.awesomeThings')); 13 | }; 14 | 15 | module.exports = new MainPage(); -------------------------------------------------------------------------------- /e2e/main.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('The main view', function() { 4 | var page; 5 | 6 | beforeEach(function() { 7 | browser.get('/index.html'); 8 | page = require('./main.po'); 9 | }); 10 | 11 | it('should include jumbotron with correct data', function() { 12 | expect(page.h1El.getText()).toBe('\'Allo, \'Allo!'); 13 | expect(page.imgEl.getAttribute('src')).toMatch(/assets\/images\/yeoman.png$/); 14 | expect(page.imgEl.getAttribute('alt')).toBe('I\'m Yeoman'); 15 | }); 16 | 17 | it('should list more than 5 awesome things', function() { 18 | expect(page.thumbnailEls.count()).toBeGreaterThan(5); 19 | }); 20 | 21 | }); -------------------------------------------------------------------------------- /gulp/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.jshintrc", 3 | "node": true 4 | } 5 | -------------------------------------------------------------------------------- /gulp/build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var gulp = require('gulp'); 5 | var conf = require('./conf'); 6 | 7 | var $ = require('gulp-load-plugins')({ 8 | pattern: ['gulp-*', 'main-bower-files', 'uglify-save-license', 'del'] 9 | }); 10 | 11 | gulp.task('partials', function() { 12 | return gulp.src([ 13 | path.join(conf.paths.src, '/app/**/*.html'), 14 | path.join(conf.paths.tmp, '/serve/app/**/*.html') 15 | ]) 16 | .pipe($.minifyHtml({ 17 | empty: true, 18 | spare: true, 19 | quotes: true 20 | })) 21 | .pipe($.angularTemplatecache('templateCacheHtml.js', { 22 | module: 'angularSeedApp', 23 | root: 'app' 24 | })) 25 | .pipe(gulp.dest(conf.paths.tmp + '/partials/')); 26 | }); 27 | 28 | gulp.task('html', ['inject', 'partials'], function() { 29 | var partialsInjectFile = gulp.src(path.join(conf.paths.tmp, '/partials/templateCacheHtml.js'), { 30 | read: false 31 | }); 32 | var partialsInjectOptions = { 33 | starttag: '', 34 | ignorePath: path.join(conf.paths.tmp, '/partials'), 35 | addRootSlash: false 36 | }; 37 | 38 | var htmlFilter = $.filter('*.html'); 39 | var jsFilter = $.filter('**/*.js'); 40 | var cssFilter = $.filter('**/*.css'); 41 | var assets; 42 | 43 | return gulp.src(path.join(conf.paths.tmp, '/serve/*.html')) 44 | .pipe($.inject(partialsInjectFile, partialsInjectOptions)) 45 | .pipe(assets = $.useref.assets()) 46 | .pipe($.rev()) 47 | .pipe(jsFilter) 48 | .pipe($.ngAnnotate()) 49 | .pipe($.uglify({ 50 | preserveComments: $.uglifySaveLicense 51 | })).on('error', conf.errorHandler('Uglify')) 52 | .pipe(jsFilter.restore()) 53 | .pipe(cssFilter) 54 | .pipe($.replace('../../bower_components/bootstrap-sass-official/assets/fonts/bootstrap/', '../fonts/')) 55 | .pipe($.csso()) 56 | .pipe(cssFilter.restore()) 57 | .pipe(assets.restore()) 58 | .pipe($.useref()) 59 | .pipe($.revReplace()) 60 | .pipe(htmlFilter) 61 | .pipe($.minifyHtml({ 62 | empty: true, 63 | spare: true, 64 | quotes: true, 65 | conditionals: true 66 | })) 67 | .pipe(htmlFilter.restore()) 68 | .pipe(gulp.dest(path.join(conf.paths.dist, '/'))) 69 | .pipe($.size({ 70 | title: path.join(conf.paths.dist, '/'), 71 | showFiles: true 72 | })); 73 | }); 74 | 75 | // Only applies for fonts from bower dependencies 76 | // Custom fonts are handled by the "other" task 77 | gulp.task('fonts', function() { 78 | return gulp.src($.mainBowerFiles()) 79 | .pipe($.filter('**/*.{eot,svg,ttf,woff,woff2}')) 80 | .pipe($.flatten()) 81 | .pipe(gulp.dest(path.join(conf.paths.dist, '/fonts/'))); 82 | }); 83 | 84 | gulp.task('other', function() { 85 | var fileFilter = $.filter(function(file) { 86 | return file.stat.isFile(); 87 | }); 88 | 89 | return gulp.src([ 90 | path.join(conf.paths.src, '/**/*'), 91 | path.join('!' + conf.paths.src, '/**/*.{html,css,js,scss}') 92 | ]) 93 | .pipe(fileFilter) 94 | .pipe(gulp.dest(path.join(conf.paths.dist, '/'))); 95 | }); 96 | 97 | gulp.task('clean', function(done) { 98 | $.del([path.join(conf.paths.dist, '/'), path.join(conf.paths.tmp, '/')], done); 99 | }); 100 | 101 | gulp.task('build', ['html', 'fonts', 'other']); -------------------------------------------------------------------------------- /gulp/conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file contains the variables used in other gulp files 3 | * which defines tasks 4 | * By design, we only put there very generic config values 5 | * which are used in several places to keep good readability 6 | * of the tasks 7 | */ 8 | 9 | var gutil = require('gulp-util'); 10 | 11 | /** 12 | * The main paths of your project handle these with care 13 | */ 14 | exports.paths = { 15 | src: 'src', 16 | dist: 'dist', 17 | tmp: '.tmp', 18 | e2e: 'e2e' 19 | }; 20 | 21 | /** 22 | * Wiredep is the lib which inject bower dependencies in your project 23 | * Mainly used to inject script tags in the index.html but also used 24 | * to inject css preprocessor deps and js files in karma 25 | */ 26 | exports.wiredep = { 27 | exclude: [/bootstrap.js$/, /bootstrap-sass-official\/.*\.js/, /bootstrap\.css/], 28 | directory: 'bower_components' 29 | }; 30 | 31 | /** 32 | * Common implementation for an error handler of a Gulp plugin 33 | */ 34 | exports.errorHandler = function(title) { 35 | 'use strict'; 36 | 37 | return function(err) { 38 | gutil.log(gutil.colors.red('[' + title + ']'), err.toString()); 39 | this.emit('end'); 40 | }; 41 | }; -------------------------------------------------------------------------------- /gulp/deploy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | var $ = require('gulp-load-plugins')(); 5 | 6 | gulp.task('deploy', function() { 7 | return gulp.src('./dist/**/*') 8 | .pipe($.ghPages({ 9 | force: true 10 | })); 11 | }); 12 | -------------------------------------------------------------------------------- /gulp/e2e-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var gulp = require('gulp'); 5 | var conf = require('./conf'); 6 | 7 | var browserSync = require('browser-sync'); 8 | 9 | var $ = require('gulp-load-plugins')(); 10 | 11 | // Downloads the selenium webdriver 12 | gulp.task('webdriver-update', $.protractor.webdriver_update); 13 | 14 | gulp.task('webdriver-standalone', $.protractor.webdriver_standalone); 15 | 16 | function runProtractor(done) { 17 | var params = process.argv; 18 | var args = params.length > 3 ? [params[3], params[4]] : []; 19 | 20 | gulp.src(path.join(conf.paths.e2e, '/**/*.js')) 21 | .pipe($.protractor.protractor({ 22 | configFile: 'protractor.conf.js', 23 | args: args 24 | })) 25 | .on('error', function(err) { 26 | // Make sure failed tests cause gulp to exit non-zero 27 | throw err; 28 | }) 29 | .on('end', function() { 30 | // Close browser sync server 31 | browserSync.exit(); 32 | done(); 33 | }); 34 | } 35 | 36 | gulp.task('protractor', ['protractor:src']); 37 | gulp.task('protractor:src', ['serve:e2e', 'webdriver-update'], runProtractor); 38 | gulp.task('protractor:dist', ['serve:e2e-dist', 'webdriver-update'], runProtractor); -------------------------------------------------------------------------------- /gulp/inject.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var gulp = require('gulp'); 5 | var conf = require('./conf'); 6 | 7 | var $ = require('gulp-load-plugins')(); 8 | 9 | var wiredep = require('wiredep').stream; 10 | var _ = require('lodash'); 11 | 12 | gulp.task('inject', ['scripts', 'styles'], function() { 13 | var injectStyles = gulp.src([ 14 | path.join(conf.paths.tmp, '/serve/app/**/*.css'), 15 | path.join('!' + conf.paths.tmp, '/serve/app/vendor.css') 16 | ], { 17 | read: false 18 | }); 19 | 20 | var injectScripts = gulp.src([ 21 | path.join(conf.paths.src, '/app/**/*.module.js'), 22 | path.join(conf.paths.src, '/app/**/*.js'), 23 | path.join('!' + conf.paths.src, '/app/**/*.spec.js'), 24 | path.join('!' + conf.paths.src, '/app/**/*.mock.js') 25 | ]) 26 | .pipe($.angularFilesort()).on('error', conf.errorHandler('AngularFilesort')); 27 | 28 | var injectOptions = { 29 | ignorePath: [conf.paths.src, path.join(conf.paths.tmp, '/serve')], 30 | addRootSlash: false 31 | }; 32 | 33 | return gulp.src(path.join(conf.paths.src, '/*.html')) 34 | .pipe($.inject(injectStyles, injectOptions)) 35 | .pipe($.inject(injectScripts, injectOptions)) 36 | .pipe(wiredep(_.extend({}, conf.wiredep))) 37 | .pipe(gulp.dest(path.join(conf.paths.tmp, '/serve'))); 38 | }); -------------------------------------------------------------------------------- /gulp/scripts.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var gulp = require('gulp'); 5 | var conf = require('./conf'); 6 | 7 | var browserSync = require('browser-sync'); 8 | 9 | var $ = require('gulp-load-plugins')(); 10 | 11 | gulp.task('scripts', function() { 12 | return gulp.src(path.join(conf.paths.src, '/app/**/*.js')) 13 | .pipe($.jshint()) 14 | .pipe($.jshint.reporter('jshint-stylish')) 15 | .pipe(browserSync.reload({ 16 | stream: true 17 | })) 18 | .pipe($.size()) 19 | }); -------------------------------------------------------------------------------- /gulp/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var gulp = require('gulp'); 5 | var conf = require('./conf'); 6 | 7 | var browserSync = require('browser-sync'); 8 | var browserSyncSpa = require('browser-sync-spa'); 9 | 10 | var util = require('util'); 11 | 12 | var proxyMiddleware = require('http-proxy-middleware'); 13 | 14 | function browserSyncInit(baseDir, browser) { 15 | browser = browser === undefined ? 'default' : browser; 16 | 17 | var routes = null; 18 | if (baseDir === conf.paths.src || (util.isArray(baseDir) && baseDir.indexOf(conf.paths.src) !== -1)) { 19 | routes = { 20 | '/bower_components': 'bower_components' 21 | }; 22 | } 23 | 24 | var server = { 25 | baseDir: baseDir, 26 | routes: routes 27 | }; 28 | 29 | /* 30 | * You can add a proxy to your backend by uncommenting the line bellow. 31 | * You just have to configure a context which will we redirected and the target url. 32 | * Example: $http.get('/users') requests will be automatically proxified. 33 | * 34 | * For more details and option, https://github.com/chimurai/http-proxy-middleware/blob/v0.0.5/README.md 35 | */ 36 | // server.middleware = proxyMiddleware('/users', {target: 'http://jsonplaceholder.typicode.com', proxyHost: 'jsonplaceholder.typicode.com'}); 37 | 38 | browserSync.instance = browserSync.init({ 39 | startPath: '/', 40 | server: server, 41 | browser: browser 42 | }); 43 | } 44 | 45 | browserSync.use(browserSyncSpa({ 46 | selector: '[ng-app]' // Only needed for angular apps 47 | })); 48 | 49 | gulp.task('serve', ['watch'], function() { 50 | browserSyncInit([path.join(conf.paths.tmp, '/serve'), conf.paths.src]); 51 | }); 52 | 53 | gulp.task('serve:dist', ['build'], function() { 54 | browserSyncInit(conf.paths.dist); 55 | }); 56 | 57 | gulp.task('serve:e2e', ['inject'], function() { 58 | browserSyncInit([conf.paths.tmp + '/serve', conf.paths.src], []); 59 | }); 60 | 61 | gulp.task('serve:e2e-dist', ['build'], function() { 62 | browserSyncInit(conf.paths.dist, []); 63 | }); -------------------------------------------------------------------------------- /gulp/styles.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var gulp = require('gulp'); 5 | var conf = require('./conf'); 6 | 7 | var browserSync = require('browser-sync'); 8 | 9 | var $ = require('gulp-load-plugins')(); 10 | 11 | var wiredep = require('wiredep').stream; 12 | var _ = require('lodash'); 13 | 14 | gulp.task('styles', function() { 15 | var sassOptions = { 16 | style: 'expanded' 17 | }; 18 | 19 | var injectFiles = gulp.src([ 20 | path.join(conf.paths.src, '/app/**/*.scss'), 21 | path.join('!' + conf.paths.src, '/app/index.scss') 22 | ], { 23 | read: false 24 | }); 25 | 26 | var injectOptions = { 27 | transform: function(filePath) { 28 | filePath = filePath.replace(conf.paths.src + '/app/', ''); 29 | return '@import "' + filePath + '";'; 30 | }, 31 | starttag: '// injector', 32 | endtag: '// endinjector', 33 | addRootSlash: false 34 | }; 35 | 36 | 37 | return gulp.src([ 38 | path.join(conf.paths.src, '/app/index.scss') 39 | ]) 40 | .pipe($.inject(injectFiles, injectOptions)) 41 | .pipe(wiredep(_.extend({}, conf.wiredep))) 42 | .pipe($.sourcemaps.init()) 43 | .pipe($.sass(sassOptions)).on('error', conf.errorHandler('Sass')) 44 | .pipe($.autoprefixer()).on('error', conf.errorHandler('Autoprefixer')) 45 | .pipe($.sourcemaps.write()) 46 | .pipe(gulp.dest(path.join(conf.paths.tmp, '/serve/app/'))) 47 | .pipe(browserSync.reload({ 48 | stream: true 49 | })); 50 | }); -------------------------------------------------------------------------------- /gulp/unit-tests.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var gulp = require('gulp'); 5 | var conf = require('./conf'); 6 | 7 | var karma = require('karma'); 8 | 9 | function runTests(singleRun, done) { 10 | karma.server.start({ 11 | configFile: path.join(__dirname, '/../karma.conf.js'), 12 | singleRun: singleRun, 13 | autoWatch: !singleRun 14 | }, function() { 15 | done(); 16 | }); 17 | } 18 | 19 | gulp.task('test', ['scripts'], function(done) { 20 | runTests(true, done); 21 | }); 22 | 23 | gulp.task('test:auto', ['watch'], function(done) { 24 | runTests(false, done); 25 | }); -------------------------------------------------------------------------------- /gulp/watch.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var gulp = require('gulp'); 5 | var conf = require('./conf'); 6 | 7 | var browserSync = require('browser-sync'); 8 | 9 | function isOnlyChange(event) { 10 | return event.type === 'changed'; 11 | } 12 | 13 | gulp.task('watch', ['inject'], function() { 14 | 15 | gulp.watch([path.join(conf.paths.src, '/*.html'), 'bower.json'], ['inject']); 16 | 17 | gulp.watch([ 18 | path.join(conf.paths.src, '/app/**/*.css'), 19 | path.join(conf.paths.src, '/app/**/*.scss') 20 | ], function(event) { 21 | if (isOnlyChange(event)) { 22 | gulp.start('styles'); 23 | } else { 24 | gulp.start('inject'); 25 | } 26 | }); 27 | 28 | gulp.watch(path.join(conf.paths.src, '/app/**/*.js'), function(event) { 29 | if (isOnlyChange(event)) { 30 | gulp.start('scripts'); 31 | } else { 32 | gulp.start('inject'); 33 | } 34 | }); 35 | 36 | gulp.watch(path.join(conf.paths.src, '/app/**/*.html'), function(event) { 37 | browserSync.reload(event.path); 38 | }); 39 | }); -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Welcome to your gulpfile! 3 | * The gulp tasks are splitted in several files in the gulp directory 4 | * because putting all here was really too long 5 | */ 6 | 7 | 'use strict'; 8 | 9 | var gulp = require('gulp'); 10 | var wrench = require('wrench'); 11 | 12 | /** 13 | * This will load all js or coffee files in the gulp directory 14 | * in order to load all gulp tasks 15 | */ 16 | wrench.readdirSyncRecursive('./gulp').filter(function(file) { 17 | return (/\.(js|coffee)$/i).test(file); 18 | }).map(function(file) { 19 | require('./gulp/' + file); 20 | }); 21 | 22 | 23 | /** 24 | * Default task clean temporaries directories and launch the 25 | * main optimization build task 26 | */ 27 | gulp.task('default', ['clean'], function() { 28 | gulp.start('build'); 29 | }); -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var conf = require('./gulp/conf'); 5 | 6 | var _ = require('lodash'); 7 | var wiredep = require('wiredep'); 8 | 9 | function listFiles() { 10 | var wiredepOptions = _.extend({}, conf.wiredep, { 11 | dependencies: true, 12 | devDependencies: true 13 | }); 14 | 15 | return wiredep(wiredepOptions).js 16 | .concat([ 17 | path.join(conf.paths.src, '/app/**/*.module.js'), 18 | path.join(conf.paths.src, '/app/**/*.js'), 19 | path.join(conf.paths.src, '/**/*.spec.js'), 20 | path.join(conf.paths.src, '/**/*.mock.js'), 21 | path.join(conf.paths.src, '/**/*.html') 22 | ]); 23 | } 24 | 25 | module.exports = function(config) { 26 | 27 | var configuration = { 28 | files: listFiles(), 29 | 30 | singleRun: true, 31 | 32 | autoWatch: false, 33 | 34 | frameworks: ['jasmine', 'angular-filesort'], 35 | 36 | angularFilesort: { 37 | whitelist: [path.join(conf.paths.src, '/**/!(*.html|*.spec|*.mock).js')] 38 | }, 39 | 40 | ngHtml2JsPreprocessor: { 41 | stripPrefix: 'src/', 42 | moduleName: 'angularSeedApp' 43 | }, 44 | 45 | browsers: ['PhantomJS'], 46 | 47 | plugins: [ 48 | 'karma-phantomjs-launcher', 49 | 'karma-angular-filesort', 50 | 'karma-jasmine', 51 | 'karma-ng-html2js-preprocessor' 52 | ], 53 | 54 | preprocessors: { 55 | 'src/**/*.html': ['ng-html2js'] 56 | } 57 | }; 58 | 59 | // This block is needed to execute Chrome on Travis 60 | // If you ever plan to use Chrome and Travis, you can keep it 61 | // If not, you can safely remove it 62 | // https://github.com/karma-runner/karma/issues/1144#issuecomment-53633076 63 | if (configuration.browsers[0] === 'Chrome' && process.env.TRAVIS) { 64 | configuration.customLaunchers = { 65 | 'chrome-travis-ci': { 66 | base: 'Chrome', 67 | flags: ['--no-sandbox'] 68 | } 69 | }; 70 | configuration.browsers = ['chrome-travis-ci']; 71 | } 72 | 73 | config.set(configuration); 74 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angularSeedApp", 3 | "version": "0.0.0", 4 | "dependencies": {}, 5 | "scripts": { 6 | "test": "gulp test" 7 | }, 8 | "devDependencies": { 9 | "browser-sync": "~2.7.12", 10 | "browser-sync-spa": "~1.0.2", 11 | "chalk": "~1.0.0", 12 | "concat-stream": "~1.5.0", 13 | "coveralls": "^2.11.2", 14 | "del": "~1.2.0", 15 | "gulp": "~3.9.0", 16 | "gulp-angular-filesort": "~1.1.1", 17 | "gulp-angular-templatecache": "~1.6.0", 18 | "gulp-autoprefixer": "~2.3.1", 19 | "gulp-csso": "~1.0.0", 20 | "gulp-filter": "~2.0.2", 21 | "gulp-flatten": "~0.0.4", 22 | "gulp-gh-pages": "^0.5.2", 23 | "gulp-inject": "~1.3.1", 24 | "gulp-jshint": "~1.11.0", 25 | "gulp-load-plugins": "~0.10.0", 26 | "gulp-minify-html": "~1.0.3", 27 | "gulp-ng-annotate": "~1.0.0", 28 | "gulp-protractor": "~1.0.0", 29 | "gulp-rename": "~1.2.2", 30 | "gulp-replace": "~0.5.3", 31 | "gulp-rev": "~5.0.0", 32 | "gulp-rev-replace": "~0.4.2", 33 | "gulp-sass": "~2.0.1", 34 | "gulp-size": "~1.2.1", 35 | "gulp-sourcemaps": "~1.5.2", 36 | "gulp-uglify": "~1.2.0", 37 | "gulp-useref": "~1.2.0", 38 | "gulp-util": "~3.0.5", 39 | "http-proxy-middleware": "~0.0.5", 40 | "istanbul": "^0.3.15", 41 | "jshint-stylish": "~2.0.0", 42 | "karma": "~0.12.36", 43 | "karma-angular-filesort": "~0.1.0", 44 | "karma-jasmine": "~0.3.5", 45 | "karma-ng-html2js-preprocessor": "~0.1.2", 46 | "karma-phantomjs-launcher": "~0.2.0", 47 | "lodash": "~3.9.3", 48 | "main-bower-files": "~2.8.0", 49 | "merge-stream": "~0.1.7", 50 | "require-dir": "~0.3.0", 51 | "uglify-save-license": "~0.4.1", 52 | "wiredep": "~2.2.2", 53 | "wrench": "~1.5.8" 54 | }, 55 | "engines": { 56 | "node": ">=0.10.0" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /protractor.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var paths = require('./.yo-rc.json')['generator-gulp-angular'].props.paths; 4 | 5 | // An example configuration file. 6 | exports.config = { 7 | // The address of a running selenium server. 8 | //seleniumAddress: 'http://localhost:4444/wd/hub', 9 | //seleniumServerJar: deprecated, this should be set on node_modules/protractor/config.json 10 | 11 | // Capabilities to be passed to the webdriver instance. 12 | capabilities: { 13 | 'browserName': 'chrome' 14 | }, 15 | 16 | baseUrl: 'http://localhost:3000', 17 | 18 | // Spec patterns are relative to the current working directly when 19 | // protractor is called. 20 | specs: [paths.e2e + '/**/*.js'], 21 | 22 | // Options to be passed to Jasmine-node. 23 | jasmineNodeOpts: { 24 | showColors: true, 25 | defaultTimeoutInterval: 30000 26 | } 27 | }; -------------------------------------------------------------------------------- /script/fixmyjs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # you must install https://developers.google.com/closure/utilities/docs/linter_howto 3 | # sudo pip install https://closure-linter.googlecode.com/svn/trunk/ 4 | find -regex ".*\.\(js\)" \ 5 | -not -path "./bower_components/*" \ 6 | -not -path "./node_modules/*" \ 7 | -not -path "./dist/*" \ 8 | -not -path "./.publish/*" \ 9 | -not -path "./.tmp/*" \ 10 | | xargs fixjsstyle 11 | 12 | # you must install https://github.com/jshint/fixmyjs 13 | # npm install fixmyjs -g 14 | find -regex ".*\.\(js\)" \ 15 | -not -path "./bower_components/*" \ 16 | -not -path "./node_modules/*" \ 17 | -not -path "./dist/*" \ 18 | -not -path "./.publish/*" \ 19 | -not -path "./.tmp/*" \ 20 | | xargs fixmyjs 21 | 22 | ./script/formatCode.sh -------------------------------------------------------------------------------- /script/formatCode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # you must install https://www.npmjs.com/package/js-beautify 3 | # npm i -g js-beautify 4 | 5 | set -e 6 | 7 | find -regex ".*\.\(js\|json\|css\|html\)" \ 8 | -not -path "./bower_components/*" \ 9 | -not -path "./node_modules/*" \ 10 | -not -path "./dist/*" \ 11 | -not -path "./.publish/*" \ 12 | -not -path "./.tmp/*" \ 13 | | xargs js-beautify --quiet --replace -------------------------------------------------------------------------------- /src/app/components/navbar/navbar.directive.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('angularSeedApp') 6 | .directive('acmeNavbar', acmeNavbar); 7 | 8 | /** @ngInject */ 9 | function acmeNavbar() { 10 | var directive = { 11 | restrict: 'E', 12 | templateUrl: 'app/components/navbar/navbar.html', 13 | scope: { 14 | creationDate: '=' 15 | }, 16 | controller: NavbarController, 17 | controllerAs: 'vm', 18 | bindToController: true 19 | }; 20 | 21 | return directive; 22 | 23 | /** @ngInject */ 24 | function NavbarController() {} 25 | } 26 | 27 | })(); -------------------------------------------------------------------------------- /src/app/components/navbar/navbar.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/components/navbar/navbar.scss: -------------------------------------------------------------------------------- 1 | .acme-navbar-text{ 2 | color: white; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/index.config.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('angularSeedApp') 6 | .config(config); 7 | 8 | /** @ngInject */ 9 | function config($logProvider, toastr) { 10 | // Enable log 11 | $logProvider.debugEnabled(true); 12 | 13 | // Set options third-party lib 14 | toastr.options.timeOut = 3000; 15 | toastr.options.positionClass = 'toast-top-right'; 16 | toastr.options.preventDuplicates = true; 17 | toastr.options.progressBar = true; 18 | } 19 | 20 | })(); -------------------------------------------------------------------------------- /src/app/index.constants.js: -------------------------------------------------------------------------------- 1 | /* global malarkey:false, toastr:false, moment:false */ 2 | (function() { 3 | 'use strict'; 4 | 5 | angular 6 | .module('angularSeedApp') 7 | .constant('malarkey', malarkey) 8 | .constant('toastr', toastr) 9 | .constant('moment', moment); 10 | 11 | })(); -------------------------------------------------------------------------------- /src/app/index.module.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('angularSeedApp', ['ngAnimate', 'ngCookies', 'ngTouch', 'ngSanitize', 'ngResource', 'ui.router', 'ui.bootstrap']); 6 | 7 | })(); -------------------------------------------------------------------------------- /src/app/index.route.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('angularSeedApp') 6 | .config(routeConfig); 7 | 8 | /** @ngInject */ 9 | function routeConfig($stateProvider, $urlRouterProvider) { 10 | $stateProvider 11 | .state('home', { 12 | url: '/', 13 | templateUrl: 'app/main/main.html', 14 | controller: 'MainController', 15 | controllerAs: 'main' 16 | }); 17 | 18 | $urlRouterProvider.otherwise('/'); 19 | } 20 | 21 | })(); -------------------------------------------------------------------------------- /src/app/index.run.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('angularSeedApp') 6 | .run(runBlock); 7 | 8 | /** @ngInject */ 9 | function runBlock($log) { 10 | 11 | $log.debug('runBlock end'); 12 | } 13 | 14 | })(); -------------------------------------------------------------------------------- /src/app/index.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * If you want to override some bootstrap variables, you have to change values here. 3 | * The list of variables are listed here bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap/_variables.scss 4 | */ 5 | $navbar-inverse-link-color: #5AADBB; 6 | $icon-font-path: "../../bower_components/bootstrap-sass-official/assets/fonts/bootstrap/"; 7 | 8 | /** 9 | * Do not remove this comments bellow. It's the markers used by wiredep to inject 10 | * sass dependencies when defined in the bower.json of your dependencies 11 | */ 12 | // bower:scss 13 | // endbower 14 | 15 | .browsehappy { 16 | margin: 0.2em 0; 17 | background: #ccc; 18 | color: #000; 19 | padding: 0.2em 0; 20 | } 21 | 22 | .thumbnail { 23 | height: 200px; 24 | 25 | img.pull-right { 26 | width: 50px; 27 | } 28 | } 29 | 30 | /** 31 | * Do not remove this comments bellow. It's the markers used by gulp-inject to inject 32 | * all your sass files automatically 33 | */ 34 | // injector 35 | // endinjector 36 | -------------------------------------------------------------------------------- /src/app/main/main.controller.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular 5 | .module('angularSeedApp') 6 | .controller('MainController', MainController); 7 | 8 | /** @ngInject */ 9 | function MainController() {} 10 | })(); -------------------------------------------------------------------------------- /src/app/main/main.controller.spec.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | describe('controllers', function() { 5 | 6 | beforeEach(module('angularSeedApp')); 7 | 8 | it('should define more than 5 awesome things', inject(function($controller) { 9 | var vm = $controller('MainController'); 10 | 11 | expect(angular.isArray(vm.awesomeThings)).toBeTruthy(); 12 | expect(vm.awesomeThings.length > 5).toBeTruthy(); 13 | })); 14 | }); 15 | })(); -------------------------------------------------------------------------------- /src/app/main/main.html: -------------------------------------------------------------------------------- 1 |
8 |
Quickstart project for Angular + Gulp
9 |