├── .bowerrc ├── .codeclimate.yml ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .jscsrc ├── .jshintrc ├── .travis.yml ├── LICENSE ├── README.md ├── bower.json ├── gulp.conf.json ├── gulpfile.js ├── karma.conf.js ├── package.json ├── protractor.conf.js ├── src ├── app │ ├── app.config.js │ ├── app.module.js │ ├── components │ │ └── .gitkeep │ ├── core │ │ ├── core.config.js │ │ ├── core.module.js │ │ └── core.route.js │ ├── modules │ │ └── home │ │ │ ├── home.controller.js │ │ │ ├── home.html │ │ │ ├── home.module.js │ │ │ ├── home.route.js │ │ │ └── home.spec.js │ └── services │ │ └── .gitkeep ├── assets │ ├── images │ │ └── .gitkeep │ └── stylesheets │ │ └── app.css ├── index.tpl └── layout │ └── .gitkeep └── tests └── e2e └── home.scenario.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "src/assets/vendor" 3 | } 4 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | languages: 2 | JavaScript: true 3 | exclude_paths: 4 | - "gulpfile.js" 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | indent_size = 2 6 | indent_style = space 7 | insert_final_newline = true 8 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log 4 | src/assets/vendor/ 5 | src/build/ 6 | src/index.html 7 | src/index-dev.html 8 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "excludeFiles": ["node_modules/**", "src/assets/vendor/**"], 3 | "requireCurlyBraces": ["if", "else", "for", "while", "do", "try", "catch" ], 4 | "requireOperatorBeforeLineBreak": true, 5 | "maximumLineLength": { 6 | "value": 100, 7 | "allowComments": true, 8 | "allowRegex": true 9 | }, 10 | "validateIndentation": 2, 11 | "validateQuoteMarks": "'", 12 | "disallowMultipleLineStrings": true, 13 | "disallowMixedSpacesAndTabs": true, 14 | "disallowTrailingWhitespace": true, 15 | "disallowSpaceAfterPrefixUnaryOperators": true, 16 | "disallowMultipleVarDecl": null, 17 | "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"], 18 | "requireSpaceBeforeBinaryOperators": ["=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "&=", "|=", "^=", "+=", "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", "|", "^", "&&", "||", "===", "==", ">=", "<=", "<", ">", "!=", "!=="], 19 | "requireSpaceAfterBinaryOperators": true, 20 | "requireSpacesInConditionalExpression": true, 21 | "requireSpaceBeforeBlockStatements": true, 22 | "requireLineFeedAtFileEnd": true, 23 | "disallowSpacesInsideObjectBrackets": "all", 24 | "disallowSpacesInsideArrayBrackets": "all", 25 | "disallowSpacesInsideParentheses": true, 26 | "disallowMultipleLineBreaks": true, 27 | "disallowCommaBeforeLineBreak": null, 28 | "disallowDanglingUnderscores": null, 29 | "disallowEmptyBlocks": null, 30 | "disallowMultipleLineStrings": null, 31 | "disallowTrailingComma": null, 32 | "requireCommaBeforeLineBreak": null, 33 | "requireDotNotation": null, 34 | "requireMultipleVarDecl": null, 35 | "requireParenthesesAroundIIFE": true 36 | } 37 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "camelcase": true, 3 | "curly": true, 4 | "eqeqeq": true, 5 | "eqnull": true, 6 | "freeze": true, 7 | "funcscope": true, 8 | "globalstrict": true, 9 | "indent": 2, 10 | "maxdepth": 3, 11 | "maxlen": 80, 12 | "maxstatements": 15, 13 | "newcap": true, 14 | "node": true, 15 | "quotmark": "single", 16 | "strict": true, 17 | "globals": { 18 | "afterEach": false, 19 | "angular": false, 20 | "beforeEach": false, 21 | "describe": false, 22 | "expect": false, 23 | "inject": false, 24 | "it": false, 25 | "module": false 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6.6.0" 4 | 5 | before_script: 6 | - npm install 7 | 8 | script: 9 | - npm run-script test-unit 10 | - npm run-script test-e2e 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Mert Yazıcıoğlu 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 | # angular-boilerplate 2 | 3 | [![Build Status](https://travis-ci.org/merty/angular-boilerplate.svg?branch=master)](https://travis-ci.org/merty/angular-boilerplate) 4 | [![Code Climate](https://codeclimate.com/github/merty/angular-boilerplate/badges/gpa.svg)](https://codeclimate.com/github/merty/angular-boilerplate) 5 | [![devDependency Status](https://david-dm.org/merty/angular-boilerplate/dev-status.svg)](https://david-dm.org/merty/angular-boilerplate#info=devDependencies) 6 | 7 | An opinionated boilerplate project for AngularJS applications, crafted with best practices in mind. 8 | 9 | ## Getting Started 10 | 11 | It is highly recommended that you read the project yourself to have a better understanding of how the project is composed. 12 | 13 | In addition to that, sample codes provided within the project are also thoughtfully written so following those practices (such as using IIFEs and choosing named functions over anonymous functions) should also be helpful. 14 | 15 | Below you can find instructions on using the project, an outline of how the project is composed and brief explanations of parts of the project. 16 | 17 | ## Usage 18 | 19 | Install the packages from NPM and Bower repositories: 20 | 21 | ``` 22 | $ npm install 23 | ``` 24 | 25 | Perform the build operations using Gulp: 26 | 27 | ``` 28 | $ npm run build # Builds for production 29 | $ npm run build-dev # Builds for development 30 | ``` 31 | 32 | Serve the application using Browsersync: *(for development purposes)* 33 | 34 | ``` 35 | $ npm run-start # Serves the production build 36 | $ npm run-start-dev # Serves the development build 37 | ``` 38 | 39 | Run unit tests using Karma: 40 | 41 | ``` 42 | $ npm run test-unit 43 | ``` 44 | 45 | Run end-to-end tests using Protractor: 46 | 47 | ``` 48 | $ npm run start-test # Start a web server for testing 49 | $ npm run test-e2e # Run the tests 50 | ``` 51 | 52 | ## Directory Structure 53 | 54 | The root directory is composed of directories `src` and `tests`, as well as configuration and deployment-related files. 55 | 56 | `src` directory contains every single file the application needs to run. Think of this directory as the public facing directory. 57 | 58 | `tests` directory contains integration-testing-related files. It comes with an `e2e` directory by default where you can place your end-to-end testing files. 59 | 60 | The rest of the files are either to configure the development environment or to handle various deployment processes. 61 | 62 | ``` 63 | . 64 | ├── src # Complete client application 65 | │   ├── app # AngularJS application files 66 | │   │   ├── components # Directive definitions 67 | │   │   ├── core # Core module 68 | │   │   ├── modules # Application-specific modules 69 | │   │   │   └── home # An example module 70 | │   │   └── services # Service definitions 71 | │   ├── assets # Third-party or non-JS assets 72 | │   │   ├── images # Image files 73 | │   │   ├── stylesheets # CSS files 74 | │   │   └── vendor # Third-party assets 75 | │   │   ├── angular # AngularJS core 76 | │   │   ├── angular-loader # AngularJS module loader 77 | │   │   ├── angular-mocks # AngularJS mocking library for unit tests 78 | │   │   ├── angular-route # AngularJS routing library 79 | │   │   ├── angular-sanitize # AngularJS HTML-sanitizing library 80 | │   │   └── html5-boilerplate # HTML5 Boilerplate library 81 | │   ├── build # Combined and minified CSS & JS files 82 | │   └── layout # Partial HTML files for the application layout 83 | ├── tests # Integration tests 84 | │ └── e2e # End-to-end test specifications 85 | ├── .bowerrc # Bower package definition file 86 | ├── .editorconfig # Editor configuration file 87 | ├── .gitattributes # Git configuration file 88 | ├── .gitignore # Git configuration file 89 | ├── .jscsrc # JSCS configuration file 90 | ├── .jshintrc # JSHint configuration file 91 | ├── .travis.yml # Travis-CI configuration file 92 | ├── LICENSE # Project license text 93 | ├── README.md # The file you are reading right now 94 | ├── bower.json # Bower dependencies file 95 | ├── gulp.conf.json # Gulp configuration file 96 | ├── gulpfile.js # Gulp tasks for build automation 97 | ├── karma.conf.js # Unit-testing configuration 98 | ├── package.json # Node.js package definition file 99 | └── protractor.conf.js # End-to-end testing configuration 100 | ``` 101 | 102 | ## App Structure 103 | 104 | The AngularJS application has its own directory inside the `src` directory. Everything AngularJS (except the third-party libraries) should be within this directory. 105 | 106 | App module is the main module, therefore it resides at the root of the `src/app` directory, together with its own configuration file. 107 | 108 | ``` 109 | src/app # Everything that composes the AngularJS app is here 110 | ├── components # Directive definitions 111 | ├── core # Core module 112 | ├── modules # Application-specific modules 113 | ├── services # Service definitions 114 | ├── app.config.js # App configuration 115 | └── app.module.js # App module definition 116 | ``` 117 | 118 | ## Core Module Structure 119 | 120 | Core module is considered special, therefore it has its own directory within the `src/app` directory. 121 | 122 | Core module is responsible for loading global dependencies such as `ngRoute` and `ngSanitize`. 123 | 124 | ``` 125 | src/app/core/ # Has its own folder within the app directory 126 | ├── core.config.js # Configuration 127 | ├── core.module.js # Module definition 128 | └── core.route.js # Route definition 129 | ``` 130 | 131 | ## Sample Module Structure 132 | 133 | Each module has its own directory to increase its reusability. 134 | 135 | If the module has partial HTML files, they can be stored in a `partials` directory within the module. 136 | 137 | Unit test specifications are also stored together with the module to make it easier to write unit tests as you go. 138 | 139 | ``` 140 | src/app/modules/home/ # Should have its own directory under app/modules 141 | ├── home.controller.js # Controller definition 142 | ├── home.html # HTML file associated with the module, if any 143 | ├── home.module.js # Module definition 144 | ├── home.route.js # Route definition 145 | └── home.spec.js # Unit tests for the module 146 | ``` 147 | 148 | ## Sample End-to-End Test for a Module 149 | 150 | Each module has its separate end-to-end test scenario file within the `tests/e2e` directory, named as `.scenario.js` where `` represents the name of the module. 151 | 152 | ``` 153 | tests/e2e/ # Under the directory for end-to-end tests 154 | └── home.scenario.js # Test file, named as ".scenario.js" 155 | ``` 156 | 157 | ## Extras 158 | 159 | This project comes with JSCS and JSHint pre-configured for you. You may make changes on them to your taste by editing `.jscsrc` and `.jshintrc` in the root directory. These tasks run each time you run `npm run build`, as a part of the `build` task defined in `Gulpfile.js`. 160 | 161 | You can also run the build sequence manually: 162 | 163 | ``` 164 | $ npm run gulp build 165 | ``` 166 | 167 | ...or maybe build for development purposes: 168 | 169 | ``` 170 | $ npm run gulp build:dev 171 | ``` 172 | 173 | ...or just run the JS task which also runs js:style, js:lint and js:deps tasks: 174 | 175 | ``` 176 | $ npm run gulp js 177 | ``` 178 | 179 | ...or simply just run JSCS and JSHint: 180 | 181 | ``` 182 | $ npm run gulp js:style 183 | $ npm run gulp js:lint 184 | ``` 185 | 186 | If you have `gulp` installed globally, you may omit `npm run` in the commands listed above. For other Gulp tasks, please take a look at `gulpfile.js`. 187 | 188 | ## Changelog 189 | 190 | **0.3.0** 191 | 192 | * Updated dependencies 193 | 194 | **0.2.0** 195 | 196 | * ngRoute is replaced with UI Router 197 | * CSS and JS bundles are now automatically injected into the HTML file 198 | * Bower assets are now automatically added to the bundles 199 | * HTML files are now compiled and cached using $templateCache 200 | * Integrated Browsersync to make development easier 201 | * Added JS files in the root directory to the linting 202 | * All gulp plugins are now used via gulp-load-plugins 203 | 204 | **0.1.0** 205 | 206 | * Initial release. 207 | 208 | ## Author 209 | 210 | Mert Yazicioglu - [Website](https://www.mertyazicioglu.com) · [GitHub](https://github.com/merty) · [Twitter](https://twitter.com/_mert) 211 | 212 | ## License 213 | 214 | This project is released under the MIT License. See the `LICENSE` file for details. 215 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-boilerplate", 3 | "description": "An opinionated boilerplate project for AngularJS applications, crafted with best practices in mind.", 4 | "authors": [ 5 | "Mert Yazicioglu " 6 | ], 7 | "homepage": "https://github.com/merty/angular-boilerplate", 8 | "license": "MIT", 9 | "private": true, 10 | "version": "0.2.0", 11 | "dependencies": { 12 | "angular": "1.4.1", 13 | "angular-loader": "1.4.1", 14 | "angular-mocks": "1.4.1", 15 | "angular-sanitize": "1.4.1", 16 | "angular-ui-router": "~0.2.15" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /gulp.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "path": { 3 | "app": "./src/app/", 4 | "build": "./src/build/", 5 | "src": "./src/", 6 | "tests": "./tests/" 7 | }, 8 | "css": { 9 | "bundle": "app.min.css", 10 | "files": [ 11 | "assets/stylesheets/app.css" 12 | ] 13 | }, 14 | "html": { 15 | "bundle": "templates.min.js", 16 | "files": "modules/**/*.html", 17 | "index": { 18 | "dev": "index-dev.html", 19 | "prod": "index.html", 20 | "tpl": "index.tpl" 21 | } 22 | }, 23 | "js": { 24 | "bundle": "app.min.js", 25 | "files": [ 26 | "**/*.js", 27 | "!**/*.spec.js" 28 | ], 29 | "vendor": { 30 | "bundle": "dependencies.min.js" 31 | } 32 | }, 33 | "jscs": { 34 | "files": [ 35 | "src/app/**/*.js", 36 | "tests/**/*.js" 37 | ] 38 | }, 39 | "jshint": { 40 | "config": ".jshintrc", 41 | "files": [ 42 | "src/app/**/*.js", 43 | "tests/**/*.js", 44 | "*.js" 45 | ] 46 | }, 47 | "test": { 48 | "specs": "**/*.spec.js", 49 | "vendor": { 50 | "bundle": "dependencies-test.min.js", 51 | "packages": [ 52 | "angular-loader", 53 | "angular-mocks" 54 | ] 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var config = require('./gulp.conf.json'); 4 | var gulp = require('gulp'); 5 | var plugins = require('gulp-load-plugins')(); 6 | 7 | var autoprefixer = require('autoprefixer'); 8 | var browserSync = require('browser-sync'); 9 | var del = require('del'); 10 | var mainBowerFiles = require('main-bower-files'); 11 | var series = require('stream-series'); 12 | 13 | gulp.task('clean', function (next) { 14 | del(getPath('build', '*.{css,js}'), next); 15 | }); 16 | 17 | gulp.task('css', function () { 18 | return gulp.src(getPath('src', config.css.files)) 19 | .pipe(plugins.concat(config.css.bundle)) 20 | .pipe(plugins.sourcemaps.init()) 21 | .pipe(plugins.postcss([autoprefixer({browsers: ['last 2 version']})])) 22 | .pipe(plugins.cssnano({keepSpecialComments: 0})) 23 | .pipe(plugins.sourcemaps.write()) 24 | .pipe(gulp.dest(config.path.build)); 25 | }); 26 | 27 | gulp.task('html:lint', function () { 28 | return gulp.src(getPath('app', config.html.files)) 29 | .pipe(plugins.htmlhint({'doctype-first': false})) 30 | .pipe(plugins.htmlhint.reporter()); 31 | }); 32 | 33 | gulp.task('html', ['html:lint'], function () { 34 | return gulp.src(getPath('app', config.html.files)) 35 | .pipe(plugins.htmlmin({empty: true, spare: true, quotes: true})) 36 | .pipe(plugins.angularTemplatecache({ 37 | root: 'app/modules' 38 | })) 39 | .pipe(plugins.concat(config.html.bundle)) 40 | .pipe(plugins.uglify({mangle: true})) 41 | .pipe(gulp.dest(config.path.build)); 42 | }); 43 | 44 | gulp.task('js:deps', function () { 45 | return gulp.src(mainBowerFiles({ 46 | filter: function (file) { 47 | var testingPackages = config.test.vendor.packages.join('|'); 48 | var isJSFile = (file.substr(-3) === '.js'); 49 | var isTestingPackage = new RegExp(testingPackages).test(file); 50 | return (isJSFile && ! isTestingPackage); 51 | } 52 | })) 53 | .pipe(plugins.concat(config.js.vendor.bundle)) 54 | .pipe(plugins.uglify({mangle: true})) 55 | .pipe(gulp.dest(config.path.build)); 56 | }); 57 | 58 | gulp.task('js:deps:test', function () { 59 | return gulp.src(mainBowerFiles({ 60 | filter: function (file) { 61 | var testingPackages = config.test.vendor.packages.join('|'); 62 | var isJSFile = (file.substr(-3) === '.js'); 63 | var isTestingPackage = new RegExp(testingPackages).test(file); 64 | return (isJSFile && isTestingPackage); 65 | } 66 | })) 67 | .pipe(plugins.concat(config.test.vendor.bundle)) 68 | .pipe(plugins.uglify({mangle: true})) 69 | .pipe(gulp.dest(config.path.build)); 70 | }); 71 | 72 | gulp.task('js:lint', function () { 73 | return gulp.src(config.jshint.files) 74 | .pipe(plugins.jshint(config.jshint.config)) 75 | .pipe(plugins.jshint.reporter('jshint-stylish')) 76 | .pipe(plugins.jshint.reporter('fail')); 77 | }); 78 | 79 | gulp.task('js:style', function () { 80 | return gulp.src(config.jscs.files) 81 | .pipe(plugins.jscs()); 82 | }); 83 | 84 | gulp.task('js', ['js:style', 'js:lint', 'js:deps'], function () { 85 | return gulp.src(getPath('app', config.js.files)) 86 | .pipe(plugins.angularFilesort()) 87 | .pipe(plugins.concat(config.js.bundle)) 88 | .pipe(plugins.ngAnnotate({'add': true, 'single_quotes': true})) 89 | .pipe(plugins.uglify({mangle: true})) 90 | .pipe(gulp.dest(config.path.build)); 91 | }); 92 | 93 | gulp.task('inject', ['clean', 'css', 'js', 'html'], function () { 94 | 95 | var files = { 96 | css: gulp.src(getPath('build', config.css.bundle), {read: false}), 97 | js: gulp.src(getPath('build', [ 98 | config.js.vendor.bundle, 99 | config.js.bundle, 100 | config.html.bundle 101 | ]), {read: false}) 102 | }; 103 | 104 | return gulp.src(getPath('src', config.html.index.tpl)) 105 | .pipe(plugins.inject(files.css, 106 | {addRootSlash: false, ignorePath: 'src/', starttag: ''} 107 | )) 108 | .pipe(plugins.inject(files.js, 109 | {addRootSlash: false, ignorePath: 'src/', starttag: ''} 110 | )) 111 | .pipe(plugins.rename(config.html.index.prod)) 112 | .pipe(gulp.dest(config.path.src)); 113 | }); 114 | 115 | gulp.task('inject:dev', ['clean', 'css', 'js'], function () { 116 | 117 | var appFiles = getPath('app', config.js.files); 118 | var vendorFiles = mainBowerFiles({ 119 | filter: function (file) { 120 | var testingPackages = config.test.vendor.packages.join('|'); 121 | var isJSFile = (file.substr(-3) === '.js'); 122 | var isTestingPackage = new RegExp(testingPackages).test(file); 123 | return (isJSFile && ! isTestingPackage); 124 | } 125 | }); 126 | var files = { 127 | css: gulp.src(getPath('build', config.css.bundle), {read: false}), 128 | js: { 129 | app: gulp.src(appFiles).pipe(plugins.angularFilesort()), 130 | vendor: gulp.src(vendorFiles, {read: false}) 131 | } 132 | }; 133 | 134 | return gulp.src(getPath('src', config.html.index.tpl)) 135 | .pipe(plugins.rename(config.html.index.dev)) 136 | .pipe(plugins.inject(files.css, 137 | {relative: true, starttag: ''} 138 | )) 139 | .pipe(plugins.inject( 140 | series(files.js.vendor, files.js.app), 141 | {addRootSlash: false, ignorePath: '/src/', starttag: ''} 142 | )) 143 | .pipe(gulp.dest(config.path.src)); 144 | }); 145 | 146 | gulp.task('build', ['clean', 'css', 'js', 'html', 'inject']); 147 | gulp.task('build:dev', ['clean', 'css', 'js:deps', 'inject:dev']); 148 | gulp.task('build:test', ['build', 'js:deps:test']); 149 | 150 | gulp.task('serve', ['build'], function () { 151 | 152 | browserSync.init({ 153 | server: config.path.src, 154 | startPath: '/index.html' 155 | }); 156 | 157 | gulp.watch(getPath('app', '**/*.html'), ['html']); 158 | gulp.watch(getPath('app', '**/*.js'), ['js']); 159 | gulp.watch(getPath('src', config.css.files), ['css']); 160 | gulp.watch(getPath('src', config.html.index.tpl), ['inject:dev']); 161 | 162 | gulp.watch(getPath('build', '*')).on('change', browserSync.reload); 163 | }); 164 | 165 | gulp.task('serve:dev', ['build:dev'], function () { 166 | 167 | browserSync.init({ 168 | server: config.path.src, 169 | startPath: '/index-dev.html' 170 | }); 171 | 172 | gulp.watch(getPath('src', config.css.files), ['css']); 173 | gulp.watch(getPath('src', config.html.index.tpl), ['inject:dev']); 174 | 175 | gulp.watch(getPath('app', '**/*')).on('change', browserSync.reload); 176 | gulp.watch(getPath('build', '*')).on('change', browserSync.reload); 177 | gulp.watch(getPath('src', '**/*.js')).on('change', browserSync.reload); 178 | }); 179 | 180 | var getPath = function (type, file) { 181 | if ( file === null || typeof file === 'string' ) { 182 | return config.path[type] + file; 183 | } 184 | var files = [], i; 185 | for ( i = 0; i < file.length; i++ ) { 186 | var fileName = file[i]; 187 | if ( fileName[0] === '!' ) { 188 | fileName = '!' + config.path[type] + file[i].substring(1); 189 | } else { 190 | fileName = config.path[type] + file[i]; 191 | } 192 | files.push(fileName); 193 | } 194 | return files; 195 | }; 196 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (config) { 4 | 5 | var gulpConfig = require('./gulp.conf.json'); 6 | 7 | config.set({ 8 | autoWatch: true, 9 | browsers: ['PhantomJS'], 10 | colors: true, 11 | exclude: [], 12 | files: [ 13 | gulpConfig.path.build + gulpConfig.js.vendor.bundle, 14 | gulpConfig.path.build + gulpConfig.test.vendor.bundle, 15 | gulpConfig.path.build + gulpConfig.js.bundle, 16 | gulpConfig.path.build + gulpConfig.html.bundle, 17 | gulpConfig.path.app + gulpConfig.test.specs 18 | ], 19 | frameworks: ['mocha', 'chai'], 20 | logLevel: config.LOG_INFO, 21 | port: 9876, 22 | preprocessors: {}, 23 | reporters: ['progress'], 24 | singleRun: false 25 | }); 26 | }; 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-boilerplate", 3 | "description": "An opinionated boilerplate project for AngularJS applications, crafted with best practices in mind.", 4 | "author": { 5 | "name": "Mert Yazicioglu", 6 | "email": "mert@mertyazicioglu.com", 7 | "url": "https://www.mertyazicioglu.com" 8 | }, 9 | "bugs": { 10 | "email": "mert@mertyazicioglu.com", 11 | "url": "https://github.com/merty/angular-boilerplate/issues" 12 | }, 13 | "homepage": "https://github.com/merty/angular-boilerplate", 14 | "keywords": [ 15 | "angular", 16 | "angularjs", 17 | "boilerplate" 18 | ], 19 | "license": "MIT", 20 | "private": true, 21 | "repository": "https://github.com/merty/angular-boilerplate", 22 | "version": "0.3.0", 23 | "devDependencies": { 24 | "autoprefixer": "^6.0.1", 25 | "bower": "^1.3.1", 26 | "browser-sync": "^2.8.2", 27 | "chai": "^3.0.0", 28 | "chai-as-promised": "^5.1.0", 29 | "del": "^2.2.0", 30 | "gulp": "^3.8.11", 31 | "gulp-angular-filesort": "^1.1.1", 32 | "gulp-angular-templatecache": "^2.0.0", 33 | "gulp-autoprefixer": "^3.1.0", 34 | "gulp-concat": "^2.5.2", 35 | "gulp-cssnano": "^2.1.1", 36 | "gulp-htmlhint": "^0.3.0", 37 | "gulp-htmlmin": "^2.0.0", 38 | "gulp-inject": "^4.1.0", 39 | "gulp-jscs": "^4.0.0", 40 | "gulp-jshint": "^2.0.0", 41 | "gulp-load-plugins": "^1.2.0", 42 | "gulp-ng-annotate": "^2.0.0", 43 | "gulp-postcss": "^6.1.0", 44 | "gulp-rename": "^1.2.2", 45 | "gulp-sourcemaps": "^1.5.2", 46 | "gulp-uglify": "^2.0.0", 47 | "gulp-util": "^3.0.4", 48 | "http-server": "^0.9.0", 49 | "jscs": "^3.0.0", 50 | "jshint-stylish": "^2.0.1", 51 | "karma": "^1.3.0", 52 | "karma-chai": "^0.1.0", 53 | "karma-mocha": "^1.1.0", 54 | "karma-phantomjs-launcher": "^1.0.0", 55 | "main-bower-files": "^2.9.0", 56 | "mocha": "^3.0.0", 57 | "phantomjs-prebuilt": "^2.1.3", 58 | "protractor": "^4.0.0", 59 | "shelljs": "^0.7.0", 60 | "stream-series": "^0.1.1" 61 | }, 62 | "scripts": { 63 | "build": "gulp build", 64 | "build-dev": "gulp build:dev", 65 | "build-test": "gulp build:test", 66 | "gulp": "gulp", 67 | "karma": "karma", 68 | "postinstall": "bower install", 69 | "protractor": "protractor", 70 | "start": "gulp serve", 71 | "start-dev": "gulp serve:dev", 72 | "start-test": "http-server ./src -a localhost -p 8000 &", 73 | "update-webdriver": "webdriver-manager update --standalone", 74 | "pretest-e2e": "npm run build-test;npm run start-test;npm run update-webdriver", 75 | "pretest-unit": "npm run build-test", 76 | "test-e2e": "protractor protractor.conf.js", 77 | "test-unit": "karma start karma.conf.js --single-run" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /protractor.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var phantomjs = require('phantomjs-prebuilt'); 4 | 5 | exports.config = { 6 | baseUrl: 'http://localhost:8000/', 7 | capabilities: { 8 | 'browserName': 'phantomjs' 9 | }, 10 | framework: 'mocha', 11 | mochaOpts: { 12 | reporter: 'spec', 13 | slow: 3000 14 | }, 15 | phantomjs: { 16 | binary: { 17 | path: phantomjs.path 18 | } 19 | }, 20 | specs: [ 21 | './tests/e2e/*.js' 22 | ] 23 | }; 24 | -------------------------------------------------------------------------------- /src/app/app.config.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app').config(AppConfig); 5 | 6 | function AppConfig() { 7 | 8 | } 9 | })(); 10 | -------------------------------------------------------------------------------- /src/app/app.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app', ['app.core', 'app.home']).run(App); 5 | 6 | function App() { 7 | 8 | } 9 | })(); 10 | -------------------------------------------------------------------------------- /src/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merty/angular-boilerplate/3c240dde60d5038b871dba273071e9727014c108/src/app/components/.gitkeep -------------------------------------------------------------------------------- /src/app/core/core.config.js: -------------------------------------------------------------------------------- 1 | /*jshint -W030 */ 2 | (function () { 3 | 'use strict'; 4 | 5 | angular.module('app.core').constant('config', { 6 | appName: 'angular-boilerplate', 7 | appVersion: '0.2.0' 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /src/app/core/core.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('templates', []); 5 | angular.module('app.core', ['ngSanitize', 'templates', 'ui.router']); 6 | })(); 7 | -------------------------------------------------------------------------------- /src/app/core/core.route.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.core').config(CoreRoute); 5 | 6 | CoreRoute.$inject = ['$stateProvider', '$urlRouterProvider']; 7 | 8 | function CoreRoute($stateProvider, $urlRouterProvider) { 9 | $urlRouterProvider.otherwise('/'); 10 | } 11 | })(); 12 | -------------------------------------------------------------------------------- /src/app/modules/home/home.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.home').controller('Home', HomeController); 5 | 6 | function HomeController() { 7 | var vm = this; 8 | vm.heading = 'Hello World!'; 9 | vm.getHeading = function () { 10 | return vm.heading; 11 | }; 12 | } 13 | })(); 14 | -------------------------------------------------------------------------------- /src/app/modules/home/home.html: -------------------------------------------------------------------------------- 1 |

{{ vm.heading }}

2 | -------------------------------------------------------------------------------- /src/app/modules/home/home.module.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.home', []); 5 | })(); 6 | -------------------------------------------------------------------------------- /src/app/modules/home/home.route.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('app.home').config(HomeRoute); 5 | 6 | HomeRoute.$inject = ['$stateProvider', '$urlRouterProvider']; 7 | 8 | function HomeRoute($stateProvider, $urlRouterProvider) { 9 | $stateProvider.state('home', { 10 | controller: 'Home', 11 | controllerAs: 'vm', 12 | templateUrl: 'app/modules/home/home.html', 13 | url: '/' 14 | }); 15 | } 16 | })(); 17 | -------------------------------------------------------------------------------- /src/app/modules/home/home.spec.js: -------------------------------------------------------------------------------- 1 | describe('HomeController', function () { 2 | 'use strict'; 3 | 4 | beforeEach(module('app')); 5 | 6 | describe('getHeading()', function () { 7 | it('should get heading message correctly', inject(function ($controller) { 8 | var homeController = $controller('Home'); 9 | homeController.heading = 'Hello World!'; 10 | homeController.getHeading().should.equal('Hello World!'); 11 | })); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /src/app/services/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merty/angular-boilerplate/3c240dde60d5038b871dba273071e9727014c108/src/app/services/.gitkeep -------------------------------------------------------------------------------- /src/assets/images/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merty/angular-boilerplate/3c240dde60d5038b871dba273071e9727014c108/src/assets/images/.gitkeep -------------------------------------------------------------------------------- /src/assets/stylesheets/app.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | font-family: Arial, sans-serif; 4 | } 5 | -------------------------------------------------------------------------------- /src/index.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | angular-boilerplate 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/layout/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merty/angular-boilerplate/3c240dde60d5038b871dba273071e9727014c108/src/layout/.gitkeep -------------------------------------------------------------------------------- /tests/e2e/home.scenario.js: -------------------------------------------------------------------------------- 1 | /* global browser, by, element */ 2 | 'use strict'; 3 | 4 | var chai = require('chai'); 5 | var chaiAsPromised = require('chai-as-promised'); 6 | 7 | chai.use(chaiAsPromised); 8 | 9 | describe('Application', function () { 10 | beforeEach(function () { 11 | browser.get('/'); 12 | }); 13 | 14 | it('home page should greet', function (done) { 15 | var heading = element(by.css('h1')).getText(); 16 | chai.expect(heading).to.eventually.equal('Hello World!'); 17 | done(); 18 | }); 19 | }); 20 | --------------------------------------------------------------------------------