├── .gitignore ├── README.md ├── gulpfile.js ├── package.json ├── source ├── app.js ├── components │ ├── example_controller │ │ ├── example.scss │ │ ├── index.js │ │ └── tempate.html │ ├── example_directive │ │ ├── index.js │ │ └── template.html │ └── index.js ├── conf │ └── routes.js ├── index.html ├── sass │ └── app.scss └── services │ ├── example-factory.js │ └── index.js └── test ├── e2e └── home.js ├── karma.conf.js ├── protractor.conf.js └── spec ├── controllers └── example.js └── directives └── example.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | build 4 | source/partials 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Angular Starter Kit 2 | 3 | Angular Starter Kit is a boilerplate project to build web applications with 4 | Angular. This include: 5 | 6 | - Browserify support 7 | - Gulp for automatization 8 | - Babel for all the application 9 | - Angular 10 | - UI-router 11 | 12 | #### Instalation 13 | 14 | ``` 15 | git clone https://github.com/highercomve/angular-starter-kit newApp 16 | cd newApp 17 | npm install 18 | ``` 19 | 20 | #### Main Gulp task 21 | 22 | ```bash 23 | gulp build 24 | ``` 25 | 26 | This task is for build the assets for development, that means no minify (JS,CSS or HTML) 27 | but transforms all html/partials to and angular $templateCache with ngHTML2JS. 28 | 29 | ```bash 30 | gulp serve 31 | ``` 32 | 33 | Open a server in port 8000 for development with livereload 34 | 35 | ```bash 36 | gulp dist 37 | ``` 38 | 39 | Run test suit 40 | 41 | ```bash 42 | gulp test 43 | ``` 44 | 45 | Run test suit with watch and reload test every change on source files 46 | 47 | ```bash 48 | gulp tdd 49 | ``` 50 | 51 | Create a folder dist, with de minify version of the files. This files are ready 52 | for production environments. 53 | 54 | ```bash 55 | gulp serve:dist 56 | ``` 57 | 58 | A local server to serve the files on dist folder. 59 | 60 | ### End to End test with protractor 61 | 62 | You have to install protractor 63 | 64 | ``` 65 | npm install -g protractor 66 | webdriver-manager update 67 | ``` 68 | 69 | And then to run the test start a selenium webdriver server 70 | 71 | ``` 72 | webdriver-manager start 73 | ``` 74 | 75 | Later start the server 76 | 77 | ``` 78 | gulp serve 79 | ``` 80 | 81 | Last run the test 82 | 83 | ``` 84 | gulp 2e2 85 | ``` 86 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | browserify = require("browserify"), 3 | source = require('vinyl-source-stream'), 4 | babelify = require("babelify"), 5 | webserver = require('gulp-webserver'), 6 | htmlmin = require('gulp-htmlmin'), 7 | uglify = require('gulp-uglify'), 8 | minifyCss = require('gulp-minify-css'), 9 | uuid = require('node-uuid'), 10 | rename = require("gulp-rename"), 11 | concat = require("gulp-concat"), 12 | ngHtml2Js = require("gulp-ng-html2js"), 13 | inject = require('gulp-inject'), 14 | es = require('event-stream'), 15 | del = require('del'), 16 | Q = require('q'), 17 | streamify = require('gulp-streamify'), 18 | globbing = require('gulp-css-globbing'), 19 | ngAnnotate = require('gulp-ng-annotate'), 20 | Server = require('karma').Server, 21 | protractor = require('gulp-protractor').protractor, 22 | envify = require('envify'), 23 | sass = require('gulp-sass'); 24 | 25 | var source_paths = { 26 | sass: './source/sass/app.scss', 27 | js: './source/app.js', 28 | all_sass: './source/**/*.scss', 29 | all_js: './source/**/*.js', 30 | all_html: './source/**/*.html', 31 | images: './sources/images/**/*', 32 | html_index: './source/index.html', 33 | partials: './source/components/**/*.html', 34 | partials_dest: './source/partials', 35 | dev_css: './build/css', 36 | dev_js: './build/js', 37 | dev_html: './build/', 38 | prod_css: './dist/css', 39 | prod_js: './dist/js', 40 | prod_html: './dist', 41 | unit_test: __dirname + '/test/karma.conf.js', 42 | protractor_test: './test/protractor.conf.js', 43 | e2e: ['./test/e2e/**/*.js'], 44 | } 45 | 46 | tasks = { 47 | baseBrowserify: function() { 48 | return browserify([source_paths.js], { 49 | transform: [ 50 | 'babelify' 51 | ]}) 52 | .transform('envify') 53 | .bundle() 54 | .on('error', function(e) { console.log(e.message) }) 55 | .pipe(source('app.js')) 56 | }, 57 | prodBrowserify: function() { 58 | return tasks.baseBrowserify() 59 | .pipe(ngAnnotate()) 60 | .pipe(streamify(uglify())) 61 | .pipe(rename(tasks.assetProdName('js'))) 62 | .pipe(gulp.dest(source_paths.prod_js)) 63 | }, 64 | devBrowserify: function() { 65 | return tasks.baseBrowserify() 66 | .pipe(gulp.dest(source_paths.dev_js)); 67 | }, 68 | prodCss: function() { 69 | return gulp.src(source_paths.sass) 70 | .pipe(globbing({extensions: ['.scss']})) 71 | .pipe(sass.sync().on('error', sass.logError)) 72 | .pipe(minifyCss({compatibility: 'ie8'})) 73 | .pipe(rename(tasks.assetProdName('css'))) 74 | .pipe(gulp.dest(source_paths.prod_css)); 75 | }, 76 | copyImages: function(type) { 77 | return gulp.src(source_paths.images) 78 | .pipe(gulp.dest('./'+type+'/images')); 79 | }, 80 | devCss: function() { 81 | return gulp.src(source_paths.sass) 82 | .pipe(globbing({extensions: ['.scss']})) 83 | .pipe(sass.sync().on('error', sass.logError)) 84 | .pipe(gulp.dest(source_paths.dev_css)); 85 | }, 86 | injectHtml: function(dest, injected_files) { 87 | return gulp.src(source_paths.html_index) 88 | .pipe(inject(injected_files, 89 | { 90 | ignorePath: ['dist', 'build', 'source'], 91 | removeTags: true, 92 | })) 93 | .pipe(gulp.dest(dest)) 94 | }, 95 | BaseNgHtml: function(dest) { 96 | return gulp.src(source_paths.partials) 97 | .pipe(htmlmin({ 98 | empty: true, 99 | spare: true, 100 | quotes: true 101 | })) 102 | .pipe(ngHtml2Js({ 103 | moduleName: "App.partialsPrecompile" 104 | })) 105 | .pipe(concat("index.js")) 106 | .pipe(gulp.dest(dest)) 107 | }, 108 | assetProdName: function(type) { 109 | var name = "app-" + uuid.v1() + "." + type; 110 | return name 111 | }, 112 | } 113 | 114 | gulp.task('clean', function() { 115 | var deferred = Q.defer(); 116 | del(source_paths.prod_html, function() { 117 | deferred.resolve(); 118 | }); 119 | return deferred.promise; 120 | }); 121 | 122 | gulp.task('sass', tasks.devCss) 123 | 124 | gulp.task('browserify', tasks.devBrowserify) 125 | 126 | gulp.task('ngHtml', function() { 127 | return tasks.BaseNgHtml(source_paths.partials_dest) 128 | }) 129 | 130 | gulp.task('inject', ['ngHtml'], function() { 131 | return tasks.injectHtml( 132 | source_paths.dev_html, 133 | es.merge(tasks.devCss(), tasks.copyImages('build'),tasks.devBrowserify()) 134 | ) 135 | }) 136 | 137 | gulp.task('inject:prod',['ngHtml'], function() { 138 | return tasks.injectHtml( 139 | source_paths.prod_html, 140 | es.merge(tasks.prodCss(), tasks.copyImages('dist'), tasks.prodBrowserify()) 141 | ) 142 | }); 143 | 144 | gulp.task('build', ['inject']) 145 | 146 | gulp.task('build:watch', ['build'], function() { 147 | gulp.watch(source_paths.all_sass, ['sass']) 148 | gulp.watch(source_paths.all_js, ['browserify']) 149 | gulp.watch(source_paths.all_html, ['inject']) 150 | }); 151 | 152 | gulp.task('dist', ['clean','inject:prod']); 153 | 154 | gulp.task('serve', ['build:watch'],function() { 155 | return gulp.src('build') 156 | .pipe(webserver({open: true, livereload: true})); 157 | }); 158 | 159 | gulp.task('serve:dist',function() { 160 | return gulp.src('dist') 161 | .pipe(webserver({open: true})); 162 | }); 163 | 164 | /** 165 | * * Run test once and exit 166 | * */ 167 | gulp.task('test', ['build'] ,function (done) { 168 | return new Server({ 169 | configFile: source_paths.unit_test, 170 | singleRun: true 171 | }, done).start(); 172 | }); 173 | 174 | /** 175 | * * Watch for file changes and re-run tests on each change 176 | * */ 177 | gulp.task('tdd', ['build:watch'], function (done) { 178 | return new Server({ 179 | configFile: source_paths.unit_test 180 | }, done).start(); 181 | }); 182 | 183 | gulp.task('e2e', function() { 184 | return gulp.src(source_paths.e2e) 185 | .pipe(protractor({ 186 | configFile: source_paths.protractor_test, 187 | })) 188 | .on('error', function(e) { throw e }) 189 | }) 190 | 191 | gulp.task('default', ['build']); 192 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-starter-kit", 3 | "version": "1.0.0", 4 | "description": "My angular default project schema", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://www.github.com/escuelaweb/intro-angular" 12 | }, 13 | "author": "Sergio Marin", 14 | "license": "ISC", 15 | "devDependencies": { 16 | "angular": "^1.5.3", 17 | "angular-mocks": "^1.4.7", 18 | "angular-ui-router": "^0.2.18", 19 | "babelify": "^6.3.0", 20 | "browserify": "^11.2.0", 21 | "del": "^2.0.2", 22 | "envify": "^3.4.0", 23 | "event-stream": "^3.3.1", 24 | "gulp": "^3.9.0", 25 | "gulp-concat": "^2.6.0", 26 | "gulp-css-globbing": "^0.1.8", 27 | "gulp-htmlmin": "^1.1.4", 28 | "gulp-inject": "^3.0.0", 29 | "gulp-karma": "*", 30 | "gulp-minify-css": "^1.2.1", 31 | "gulp-ng-annotate": "^1.1.0", 32 | "gulp-ng-html2js": "^0.2.0", 33 | "gulp-rename": "^1.2.2", 34 | "gulp-sass": "^2.0.4", 35 | "gulp-streamify": "^1.0.2", 36 | "gulp-uglify": "^1.4.1", 37 | "gulp-webserver": "^0.9.1", 38 | "jasmine-core": "^2.3.4", 39 | "karma": "^0.13.22", 40 | "karma-browserify": "^5.0.3", 41 | "karma-chrome-launcher": "^0.2.3", 42 | "karma-jasmine": "^0.3.8", 43 | "karma-phantomjs-launcher": "^1.0.0", 44 | "node-uuid": "^1.4.3", 45 | "phantomjs-prebuilt": "^2.1.7", 46 | "q": "^1.4.1", 47 | "uglifyify": "^3.0.1", 48 | "vinyl-source-stream": "^1.1.0", 49 | "watchify": "^3.7.0" 50 | }, 51 | "dependencies": { 52 | "gulp-protractor": "^1.0.0", 53 | "protractor": "^2.5.1" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /source/app.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular' 2 | import components from './components' 3 | import services from './services' 4 | import partials from './partials' 5 | import routes from './conf/routes' 6 | 7 | angular.module('App', [ 8 | 'App.components', 9 | 'App.partialsPrecompile', 10 | 'App.services', 11 | 'App.routes' 12 | ]) 13 | 14 | /* 15 | * You can add environment variables using envify and this sintax 16 | * 17 | const node_env = process.env.NODE_ENV || 'development' 18 | 19 | angular.module('App').constant('ENV', { 20 | app_name: 'New app', 21 | type: node_env, 22 | }) 23 | */ 24 | 25 | angular.bootstrap(document.body, ['App']) 26 | 27 | // angular.module('App').run(function() { 28 | // console.log('Running Angular with browserify') 29 | // }) 30 | -------------------------------------------------------------------------------- /source/components/example_controller/example.scss: -------------------------------------------------------------------------------- 1 | 2 | .home-page { 3 | color: blue; 4 | .example-directive { 5 | border: blue 1px solid; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /source/components/example_controller/index.js: -------------------------------------------------------------------------------- 1 | export default function(ngComponent) { 2 | ngComponent.controller('exampleController', ExampleController) 3 | 4 | function ExampleController($scope, exampleFactory) { 5 | $scope.helloMessage = "Hello from a controller router" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /source/components/example_controller/tempate.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

{{ helloMessage }}

4 | 5 |
6 | -------------------------------------------------------------------------------- /source/components/example_directive/index.js: -------------------------------------------------------------------------------- 1 | export default function(ngComponent) { 2 | ngComponent.directive('exampleDirective', ExampleDirective) 3 | 4 | function ExampleDirective() { 5 | return { 6 | restrict: 'E', 7 | templateUrl: 'example_directive/template.html', 8 | link(scope, element, attrs) { 9 | scope.message = attrs.message 10 | }, 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /source/components/example_directive/template.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

{{ message }}

4 |
5 | -------------------------------------------------------------------------------- /source/components/index.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular' 2 | 3 | angular.module('App.components', []) 4 | 5 | const components = angular.module('App.components') 6 | 7 | // components.run(() => { 8 | // console.log('Running directives module') 9 | // }) 10 | 11 | // How to add controller to the components module 12 | require('./example_controller')(components); 13 | 14 | // How to add a directive to the components module 15 | require('./example_directive')(components); 16 | 17 | export default components 18 | -------------------------------------------------------------------------------- /source/conf/routes.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular' 2 | import uiRouter from 'angular-ui-router' 3 | 4 | let routes = angular.module('App.routes', ['ui.router']) 5 | 6 | routes.config(function($stateProvider, $urlRouterProvider) { 7 | $stateProvider 8 | .state('home', { 9 | url: "/", 10 | controller: 'exampleController', 11 | templateUrl: "example_controller/template.html" 12 | }) 13 | $urlRouterProvider.otherwise("/") 14 | }) 15 | 16 | export default routes 17 | -------------------------------------------------------------------------------- /source/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | App with Angular 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

Basic Angular app Template

14 |
15 | 16 |
17 |
18 |
19 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /source/sass/app.scss: -------------------------------------------------------------------------------- 1 | @import "../components/**/*.scss"; 2 | 3 | [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { 4 | display: none !important; 5 | } 6 | -------------------------------------------------------------------------------- /source/services/example-factory.js: -------------------------------------------------------------------------------- 1 | export default function(ngComponent) { 2 | ngComponent.factory('exampleFactory', ExampleFactory) 3 | 4 | function ExampleFactory($resource) { 5 | /* 6 | * Example content for a Service that will use ngResource 7 | * 8 | let privateVariable 9 | let Model = $resource('/url/:id', { id: '@id'}) 10 | 11 | Model.prototype.publicVariable = 'Data' 12 | 13 | Model.classMethod = () => { 14 | 15 | } 16 | 17 | Model.prototype.instanceMethod = () => { 18 | 19 | } 20 | 21 | function PrivateFunction() { 22 | 23 | } 24 | return Model 25 | */ 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /source/services/index.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular' 2 | // You can add here dependencies that 3 | // import ngResource from 'ng-resource' 4 | 5 | // the ng-resource library, don't autoexecute therefore: 6 | // ngResource(window, angular) 7 | 8 | angular.module('App.services', []) 9 | 10 | let services = angular.module('App.services') 11 | 12 | // services.run(() => { 13 | // console.log('Running services module') 14 | // }) 15 | 16 | // How to add factories or services to the services module 17 | require('./example-factory.js')(services) 18 | 19 | export default services 20 | -------------------------------------------------------------------------------- /test/e2e/home.js: -------------------------------------------------------------------------------- 1 | describe('angularjs homepage todo list', function() { 2 | it('should add a todo', function() { 3 | browser.get('/'); 4 | expect(true).toEqual(true); 5 | }); 6 | }); 7 | -------------------------------------------------------------------------------- /test/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // http://karma-runner.github.io/0.12/config/configuration-file.html 3 | // Generated on 2015-10-15 using 4 | // generator-karma 1.0.0 5 | 6 | module.exports = function(config) { 7 | 'use strict'; 8 | 9 | config.set({ 10 | // enable / disable watching file and executing tests whenever any file changes 11 | autoWatch: true, 12 | 13 | // base path, that will be used to resolve files and exclude 14 | basePath: '../', 15 | 16 | // testing framework to use (jasmine/mocha/qunit/...) 17 | // as well as any additional frameworks (requirejs/chai/sinon/...) 18 | frameworks: [ 19 | "jasmine" 20 | ], 21 | 22 | // list of files / patterns to load in the browser 23 | files: [ 24 | "node_modules/angular/angular.min.js", 25 | "node_modules/angular-mocks/angular-mocks.js", 26 | "test/spec/**/*.js", 27 | "build/**/*.js", 28 | ], 29 | 30 | // list of files / patterns to exclude 31 | exclude: [ 32 | ], 33 | 34 | // web server port 35 | port: 8080, 36 | 37 | // Start these browsers, currently available: 38 | // - Chrome 39 | // - ChromeCanary 40 | // - Firefox 41 | // - Opera 42 | // - Safari (only Mac) 43 | // - PhantomJS 44 | // - IE (only Windows) 45 | browsers: [ 46 | "PhantomJS" 47 | ], 48 | 49 | // Which plugins to enable 50 | plugins: [ 51 | "karma-phantomjs-launcher", 52 | "karma-jasmine" 53 | ], 54 | 55 | // Continuous Integration mode 56 | // if true, it capture browsers, run tests and exit 57 | singleRun: false, 58 | 59 | colors: true, 60 | 61 | // level of logging 62 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 63 | logLevel: config.LOG_INFO, 64 | 65 | // Uncomment the following lines if you are using grunt's server to run the tests 66 | // proxies: { 67 | // '/': 'http://localhost:9000/' 68 | // }, 69 | // URL root prevent conflicts with the site root 70 | // urlRoot: '_karma_' 71 | }); 72 | }; 73 | -------------------------------------------------------------------------------- /test/protractor.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // An example configuration file. 4 | exports.config = { 5 | // The address of a running selenium server. 6 | 7 | seleniumAddress: 'http://localhost:4444/wd/hub', 8 | 9 | //seleniumServerJar: deprecated, this should be set on node_modules/protractor/config.json 10 | 11 | chromeOnly: true, 12 | 13 | baseUrl: 'http://127.0.0.1:8000', 14 | 15 | // Capabilities to be passed to the webdriver instance. 16 | capabilities: { 17 | 'browserName': 'chrome' 18 | }, 19 | 20 | // Spec patterns are relative to the current working directly when 21 | // protractor is called. 22 | specs: ['./e2e/**/*.js'], 23 | 24 | framework: 'jasmine2', 25 | 26 | // Options to be passed to Jasmine-node. 27 | jasmineNodeOpts: { 28 | showColors: true, 29 | defaultTimeoutInterval: 30000 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /test/spec/controllers/example.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: exampleController', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('App')); 7 | 8 | var exampleController, 9 | scope; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function ($controller, $rootScope) { 13 | scope = $rootScope.$new(); 14 | exampleController = $controller('exampleController', { 15 | $scope: scope 16 | // place here mocked dependencies 17 | }); 18 | })); 19 | 20 | it('should attach a helloMessage to the scope', function () { 21 | expect(scope.helloMessage).toBe("Hello from a controller router"); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/spec/directives/example.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Test ExampleDirective', function() { 4 | 5 | beforeEach(module('App')); 6 | 7 | var compile, rootScope; 8 | 9 | beforeEach(inject(function($compile, $rootScope) { 10 | compile = $compile; 11 | rootScope = $rootScope 12 | })); 13 | 14 | it('render directive with message',function() { 15 | var message = "Testing message" 16 | var element = compile("")(rootScope); 17 | rootScope.$digest(); 18 | expect(element.html()).toContain(message); 19 | }) 20 | }) 21 | --------------------------------------------------------------------------------