├── .babelrc ├── .gitignore ├── .jscsrc ├── README.md ├── app ├── app.config.js ├── common │ ├── button │ │ ├── button.component.js │ │ ├── button.controller.js │ │ ├── button.html │ │ ├── button.js │ │ ├── button.scss │ │ └── button.test.js │ ├── components.js │ └── navbar │ │ ├── navbar.component.js │ │ ├── navbar.controller.js │ │ ├── navbar.html │ │ ├── navbar.js │ │ ├── navbar.scss │ │ └── navbar.test.js ├── components │ ├── components.js │ ├── hero │ │ ├── hero.component.js │ │ ├── hero.controller.js │ │ ├── hero.html │ │ ├── hero.js │ │ ├── hero.scss │ │ └── hero.test.js │ └── home │ │ ├── home.component.js │ │ ├── home.controller.js │ │ ├── home.html │ │ ├── home.js │ │ ├── home.scss │ │ └── home.test.js ├── index.html ├── index.js ├── index.scss └── services │ ├── services.js │ └── users │ ├── users.js │ ├── users.service.js │ └── users.test.js ├── generator ├── component │ ├── temp.component.js │ ├── temp.controller.js │ ├── temp.html │ ├── temp.js │ ├── temp.scss │ └── temp.test.js └── service │ ├── temp.js │ ├── temp.service.js │ └── temp.test.js ├── gulpfile.babel.js ├── karma.conf.js ├── package.json └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | presets: ["es2015", "stage-0", "stage-1"] 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bundle.js 3 | npm-debug.log 4 | /build/* 5 | /dist/* -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "excludeFiles": ["node_modules/**", "bower_components/**"], 3 | 4 | "requireCurlyBraces": [ 5 | "if", 6 | "else", 7 | "for", 8 | "while", 9 | "do", 10 | "try", 11 | "catch" 12 | ], 13 | "requireOperatorBeforeLineBreak": true, 14 | "requireCamelCaseOrUpperCaseIdentifiers": true, 15 | "maximumLineLength": { 16 | "value": 100, 17 | "allowComments": true, 18 | "allowRegex": true 19 | }, 20 | "validateIndentation": 2, 21 | "validateQuoteMarks": "'", 22 | 23 | "disallowMultipleLineStrings": true, 24 | "disallowMixedSpacesAndTabs": true, 25 | "disallowTrailingWhitespace": true, 26 | "disallowSpaceAfterPrefixUnaryOperators": true, 27 | "disallowMultipleVarDecl": null, 28 | 29 | "requireSpaceAfterKeywords": [ 30 | "if", 31 | "else", 32 | "for", 33 | "while", 34 | "do", 35 | "switch", 36 | "return", 37 | "try", 38 | "catch" 39 | ], 40 | "requireSpaceBeforeBinaryOperators": [ 41 | "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", 42 | "&=", "|=", "^=", "+=", 43 | 44 | "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", 45 | "|", "^", "&&", "||", "===", "==", ">=", 46 | "<=", "<", ">", "!=", "!==" 47 | ], 48 | "requireSpaceAfterBinaryOperators": true, 49 | "requireSpacesInConditionalExpression": true, 50 | "requireSpaceBeforeBlockStatements": true, 51 | "requireLineFeedAtFileEnd": true, 52 | "disallowSpacesInsideObjectBrackets": "all", 53 | "disallowSpacesInsideArrayBrackets": "all", 54 | "disallowSpacesInsideParentheses": true, 55 | 56 | "jsDoc": { 57 | "checkAnnotations": true, 58 | "checkParamNames": true, 59 | "requireParamTypes": true, 60 | "checkReturnTypes": true, 61 | "checkTypes": true 62 | }, 63 | 64 | "disallowMultipleLineBreaks": true, 65 | 66 | "disallowCommaBeforeLineBreak": null, 67 | "disallowDanglingUnderscores": null, 68 | "disallowEmptyBlocks": null, 69 | "disallowTrailingComma": null, 70 | "requireCommaBeforeLineBreak": null, 71 | "requireDotNotation": null, 72 | "requireMultipleVarDecl": null, 73 | "requireParenthesesAroundIIFE": true 74 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # angular-starter-es6-webpack 2 | [![Join the chat at https://gitter.im/angularclass/NG6-starter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/thelarkinn/angular-starter-es6-webpack?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 3 | 4 | 5 | This is an Angular Starter App with component and service generators using gulp for easy component development. Uses Karma-Mocha-Chai as test suit and Babel Loader and Webpack for ES6 6 | 7 | ## Instructions for Installation 8 | 1. Fork and Clone Repository. 9 | 2. Open terminal and `cd` to path of the repo. 10 | 3. Install any node modules: `npm install` 11 | 4. Install gulp: `npm install gulp -g` 12 | 13 | ## Running the Enviornments 14 | ### Development 15 | ` npm run dev ` 16 | 17 | ### Test (Karma-Mocha-Chai) 18 | ` npm run test ` 19 | 20 | ### Production 21 | ` npm run build ` 22 | 23 | 24 | ## Generators 25 | This app comes with some helpful and generators for creating a new component/service. You simply have to hook them up to your components.js and services.js file. 26 | 27 | ### To create a new component: 28 | ` gulp component --name ` 29 | 30 | ### To create a new common component: 31 | 32 | ` gulp common_component --name ` 33 | 34 | ### To create a new service 35 | 36 | ` gulp service --name ` 37 | 38 | 39 | ##Project Structure 40 | 41 | ``` 42 | /app 43 | /assets 44 | /images 45 | /foo.png 46 | /common 47 | /button 48 | /button.js 49 | /button.component.js 50 | /button.controller.js 51 | /button.html 52 | /button.scss 53 | /button.test.js 54 | /navbar/ 55 | /components.js 56 | /components 57 | /about 58 | /about.js 59 | /about.component.js 60 | /about.controller.js 61 | /about.html 62 | /about.scss 63 | /about.test.js 64 | /home/ 65 | /components.js 66 | /services 67 | /users 68 | /users.js 69 | /users.service.js 70 | /users.test.js 71 | /documents/ 72 | /services.js 73 | /app.config.js 74 | /index.js 75 | /index.html 76 | /index.scss 77 | ``` 78 | 79 | ### Main/Entry File To the Project 80 | `index.js` is the main entry file which serves as the total include point for all of your components, services, assets, and styles. 81 | 82 | Notice that throughout the project, that the angular setter/getter is called once, and is assigned to a constant which is passed through each of the dependancy trees so that it is exposed to the rest of the imported/exported components. 83 | 84 | _Why?_ Because this makes your components more *modular*, allowing whatever `angular.module` object to be assigned to the exported component/component set. 85 | 86 | ```javascript 87 | // index.js 88 | // Angular & Router ES6 Imports 89 | import angular from 'angular'; 90 | import angularUIRouter from 'angular-ui-router'; 91 | import appComponents from './components/components.js'; 92 | import commonComponents from './common/components.js'; 93 | import appServices from './services/services.js'; 94 | import appConfiguration from './app.config'; 95 | 96 | // Single Style Entry Point 97 | import './index.scss'; 98 | 99 | if (ENVIRONMENT === 'test') { 100 | console.log('ENV:', ENVIRONMENT); 101 | require('angular-mocks/angular-mocks'); 102 | } 103 | 104 | const app = angular.module('app', ['ui.router']); 105 | 106 | // Components Entrypoint 107 | appComponents(app); 108 | 109 | // Common Components Entrypoint 110 | commonComponents(app); 111 | 112 | // App Services Entrypoint 113 | appServices(app); 114 | 115 | // Router Configuration 116 | // Components must be declared first since 117 | // Routes reference controllers that will be bound to route templates. 118 | // appConfiguration(app); 119 | 120 | ``` 121 | 122 | ### -------------------------------------------------------------------------------- /app/app.config.js: -------------------------------------------------------------------------------- 1 | export default app => { 2 | app.config([configFn]); 3 | 4 | function configFn() { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /app/common/button/button.component.js: -------------------------------------------------------------------------------- 1 | import template from './button.html'; 2 | import controller from './button.controller'; 3 | 4 | let buttonComponent = function () { 5 | return { 6 | restrict: 'E', 7 | scope: {}, 8 | template, 9 | controller, 10 | controllerAs: 'buttonCtrl', 11 | bindToController: true 12 | }; 13 | }; 14 | 15 | export default buttonComponent; 16 | -------------------------------------------------------------------------------- /app/common/button/button.controller.js: -------------------------------------------------------------------------------- 1 | export default class ButtonController { 2 | constructor() { 3 | this.name = 'button'; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /app/common/button/button.html: -------------------------------------------------------------------------------- 1 |
2 |

{{ buttonCtrl.name }}

3 |
4 | -------------------------------------------------------------------------------- /app/common/button/button.js: -------------------------------------------------------------------------------- 1 | import buttonComponent from './button.component'; 2 | 3 | export default app => { 4 | app.directive('button', buttonComponent); 5 | 6 | if (ENVIRONMENT === 'test') { 7 | require('./button.test.js'); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /app/common/button/button.scss: -------------------------------------------------------------------------------- 1 | .button { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /app/common/button/button.test.js: -------------------------------------------------------------------------------- 1 | import ButtonModule from './button' 2 | import ButtonController from './button.controller'; 3 | import ButtonComponent from './button.component'; 4 | import ButtonTemplate from './button.html'; 5 | 6 | describe('Button', () => { 7 | let $rootScope, makeController; 8 | 9 | beforeEach(window.module('app')); 10 | beforeEach(inject((_$rootScope_) => { 11 | $rootScope = _$rootScope_; 12 | makeController = () => { 13 | return new ButtonController(); 14 | }; 15 | })); 16 | 17 | describe('Module', () => { 18 | // top-level specs: i.e., routes, injection, naming 19 | }); 20 | 21 | describe('Controller', () => { 22 | // controller specs 23 | it('has a name property [REMOVE]', () => { // erase if removing this.name from the controller 24 | let controller = makeController(); 25 | expect(controller).to.have.property('name'); 26 | }); 27 | }); 28 | 29 | describe('Template', () => { 30 | // template specs 31 | // tip: use regex to ensure correct bindings are used e.g., {{ }} 32 | it('has name in template [REMOVE]', () => { 33 | expect(ButtonTemplate).to.match(/{{\s?vm\.name\s?}}/g); 34 | }); 35 | }); 36 | 37 | describe('Component', () => { 38 | // component/directive specs 39 | let component = ButtonComponent(); 40 | 41 | it('includes the intended template',() => { 42 | expect(component.template).to.equal(ButtonTemplate); 43 | }); 44 | 45 | it('uses `controllerAs` syntax', () => { 46 | expect(component).to.have.property('controllerAs'); 47 | }); 48 | 49 | it('invokes the right controller', () => { 50 | expect(component.controller).to.equal(ButtonController); 51 | }); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /app/common/components.js: -------------------------------------------------------------------------------- 1 | import navbarComponent from './navbar/navbar'; 2 | import buttonComponent from './button/button'; 3 | 4 | export default app => { 5 | INCLUDE_ALL_MODULES([navbarComponent, buttonComponent], app); 6 | } 7 | -------------------------------------------------------------------------------- /app/common/navbar/navbar.component.js: -------------------------------------------------------------------------------- 1 | import template from './navbar.html'; 2 | import controller from './navbar.controller'; 3 | 4 | let navbarComponent = function () { 5 | return { 6 | restrict: 'E', 7 | scope: {}, 8 | template, 9 | controller, 10 | controllerAs: 'navBarCtrl', 11 | bindToController: true 12 | }; 13 | }; 14 | 15 | export default navbarComponent; 16 | -------------------------------------------------------------------------------- /app/common/navbar/navbar.controller.js: -------------------------------------------------------------------------------- 1 | export default class NavbarController { 2 | constructor() { 3 | this.name = 'navbar'; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /app/common/navbar/navbar.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /app/common/navbar/navbar.js: -------------------------------------------------------------------------------- 1 | import navbarComponent from './navbar.component'; 2 | 3 | export default app => { 4 | app.directive('navbar', navbarComponent); 5 | 6 | if (ENVIRONMENT === 'test') { 7 | require('./navbar.test.js'); 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /app/common/navbar/navbar.scss: -------------------------------------------------------------------------------- 1 | .navbar { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /app/common/navbar/navbar.test.js: -------------------------------------------------------------------------------- 1 | import NavbarModule from './navbar' 2 | import NavbarController from './navbar.controller'; 3 | import NavbarComponent from './navbar.component'; 4 | import NavbarTemplate from './navbar.html'; 5 | 6 | describe('Navbar', () => { 7 | let $rootScope, makeController; 8 | 9 | beforeEach(window.module('app')); 10 | beforeEach(inject((_$rootScope_) => { 11 | $rootScope = _$rootScope_; 12 | makeController = () => { 13 | return new NavbarController(); 14 | }; 15 | })); 16 | 17 | describe('Module', () => { 18 | // top-level specs: i.e., routes, injection, naming 19 | }); 20 | 21 | describe('Controller', () => { 22 | // controller specs 23 | it('has a name property [REMOVE]', () => { // erase if removing this.name from the controller 24 | let controller = makeController(); 25 | expect(controller).to.have.property('name'); 26 | }); 27 | }); 28 | 29 | describe('Template', () => { 30 | // template specs 31 | // tip: use regex to ensure correct bindings are used e.g., {{ }} 32 | it('has name in template [REMOVE]', () => { 33 | expect(NavbarTemplate).to.match(/{{\s?vm\.name\s?}}/g); 34 | }); 35 | }); 36 | 37 | describe('Component', () => { 38 | // component/directive specs 39 | let component = NavbarComponent(); 40 | 41 | it('includes the intended template',() => { 42 | expect(component.template).to.equal(NavbarTemplate); 43 | }); 44 | 45 | it('uses `controllerAs` syntax', () => { 46 | expect(component).to.have.property('controllerAs'); 47 | }); 48 | 49 | it('invokes the right controller', () => { 50 | expect(component.controller).to.equal(NavbarController); 51 | }); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /app/components/components.js: -------------------------------------------------------------------------------- 1 | import homeComponent from './home/home'; 2 | import heroComponent from './hero/hero'; 3 | 4 | export default app => { 5 | INCLUDE_ALL_MODULES([homeComponent, heroComponent], app); 6 | } 7 | -------------------------------------------------------------------------------- /app/components/hero/hero.component.js: -------------------------------------------------------------------------------- 1 | import template from './hero.html'; 2 | import controller from './hero.controller'; 3 | 4 | let heroComponent = function () { 5 | return { 6 | restrict: 'E', 7 | scope: {}, 8 | template, 9 | controller, 10 | controllerAs: 'heroCtrl', 11 | bindToController: true 12 | }; 13 | }; 14 | 15 | export default heroComponent; 16 | -------------------------------------------------------------------------------- /app/components/hero/hero.controller.js: -------------------------------------------------------------------------------- 1 | export default class HeroController { 2 | constructor() { 3 | this.name = 'hero'; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /app/components/hero/hero.html: -------------------------------------------------------------------------------- 1 |
2 |

{{ heroCtrl.name }} Component

3 |
4 | -------------------------------------------------------------------------------- /app/components/hero/hero.js: -------------------------------------------------------------------------------- 1 | import heroComponent from './hero.component'; 2 | 3 | export default app => { 4 | app.directive('hero', heroComponent); 5 | 6 | if (ENVIRONMENT === 'test') { 7 | require('./hero.test.js'); 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /app/components/hero/hero.scss: -------------------------------------------------------------------------------- 1 | .hero { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /app/components/hero/hero.test.js: -------------------------------------------------------------------------------- 1 | import HeroModule from './hero' 2 | import HeroController from './hero.controller'; 3 | import HeroComponent from './hero.component'; 4 | import HeroTemplate from './hero.html'; 5 | 6 | describe('Hero', () => { 7 | let $rootScope, makeController; 8 | 9 | beforeEach(window.module('app')); 10 | beforeEach(inject((_$rootScope_) => { 11 | $rootScope = _$rootScope_; 12 | makeController = () => { 13 | return new HeroController(); 14 | }; 15 | })); 16 | 17 | describe('Module', () => { 18 | // top-level specs: i.e., routes, injection, naming 19 | }); 20 | 21 | describe('Controller', () => { 22 | // controller specs 23 | it('has a name property [REMOVE]', () => { // erase if removing this.name from the controller 24 | let controller = makeController(); 25 | expect(controller).to.have.property('name'); 26 | }); 27 | }); 28 | 29 | describe('Template', () => { 30 | // template specs 31 | // tip: use regex to ensure correct bindings are used e.g., {{ }} 32 | it('has name in template [REMOVE]', () => { 33 | expect(HeroTemplate).to.match(/{{\s?vm\.name\s?}}/g); 34 | }); 35 | }); 36 | 37 | describe('Component', () => { 38 | // component/directive specs 39 | let component = HeroComponent(); 40 | 41 | it('includes the intended template',() => { 42 | expect(component.template).to.equal(HeroTemplate); 43 | }); 44 | 45 | it('uses `controllerAs` syntax', () => { 46 | expect(component).to.have.property('controllerAs'); 47 | }); 48 | 49 | it('invokes the right controller', () => { 50 | expect(component.controller).to.equal(HeroController); 51 | }); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /app/components/home/home.component.js: -------------------------------------------------------------------------------- 1 | import template from './home.html'; 2 | import controller from './home.controller'; 3 | 4 | // This is the Directive Definition Object function seen in a traditional Angular setup. 5 | // In this example it is abstracted as a shell and used in the home.js. 6 | let homeComponent = function () { 7 | return { 8 | restrict: 'EA', 9 | scope: {}, 10 | template: template, 11 | controller: controller, 12 | controllerAs: 'homeCtrl', 13 | bindToController: true 14 | }; 15 | }; 16 | 17 | export default homeComponent; 18 | -------------------------------------------------------------------------------- /app/components/home/home.controller.js: -------------------------------------------------------------------------------- 1 | export default class HomeController { 2 | constructor() { 3 | this.name = 'home'; 4 | } 5 | } 6 | 7 | -------------------------------------------------------------------------------- /app/components/home/home.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 |
6 |
7 |

Found in {{ homeCtrl.name }}.html

8 |
9 |
10 | -------------------------------------------------------------------------------- /app/components/home/home.js: -------------------------------------------------------------------------------- 1 | import homeComponent from './home.component'; 2 | 3 | export default app => { 4 | app.config(($stateProvider, $urlRouterProvider) => { 5 | $urlRouterProvider.otherwise('/'); 6 | 7 | $stateProvider 8 | .state('home', { 9 | url: '/', 10 | template: '' //Essentially Treats the Home Directive as the Route View. 11 | }); 12 | }).directive('home', homeComponent); 13 | 14 | if (ENVIRONMENT === 'test') { 15 | require('./home.test.js'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/components/home/home.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheLarkInn/angular-starter-es6-webpack/5c38d557393021e0268190168214dd55fdfe4dfa/app/components/home/home.scss -------------------------------------------------------------------------------- /app/components/home/home.test.js: -------------------------------------------------------------------------------- 1 | import HomeModule from './home' 2 | import HomeController from './home.controller'; 3 | import HomeComponent from './home.component'; 4 | import HomeTemplate from './home.html'; 5 | 6 | describe('Home', () => { 7 | let $rootScope, makeController; 8 | 9 | beforeEach(window.module('app')); 10 | beforeEach(inject((_$rootScope_) => { 11 | $rootScope = _$rootScope_; 12 | makeController = () => { 13 | return new HomeController(); 14 | }; 15 | })); 16 | 17 | describe('Module', () => { 18 | // top-level specs: i.e., routes, injection, naming 19 | }); 20 | 21 | describe('Controller', () => { 22 | // controller specs 23 | it('has a name property [REMOVE]', () => { // erase if removing this.name from the controller 24 | let controller = makeController(); 25 | expect(controller).to.have.property('name'); 26 | }); 27 | }); 28 | 29 | describe('Template', () => { 30 | // template specs 31 | // tip: use regex to ensure correct bindings are used e.g., {{ }} 32 | it('has name in template [REMOVE]', () => { 33 | expect(HomeTemplate).to.match(/{{\s?vm\.name\s?}}/g); 34 | }); 35 | }); 36 | 37 | describe('Component', () => { 38 | // component/directive specs 39 | let component = HomeComponent(); 40 | 41 | it('includes the intended template',() => { 42 | expect(component.template).to.equal(HomeTemplate); 43 | }); 44 | 45 | it('uses `controllerAs` syntax', () => { 46 | expect(component).to.have.property('controllerAs'); 47 | }); 48 | 49 | it('invokes the right controller', () => { 50 | expect(component.controller).to.equal(HomeController); 51 | }); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | My Sweet App 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | // Angular & Router ES6 Imports 2 | import angular from 'angular'; 3 | import angularUIRouter from 'angular-ui-router'; 4 | import appComponents from './components/components.js'; 5 | import commonComponents from './common/components.js'; 6 | import appServices from './services/services.js'; 7 | import appConfiguration from './app.config'; 8 | 9 | // Single Style Entry Point 10 | import './index.scss'; 11 | 12 | if (ENVIRONMENT === 'test') { 13 | console.log('ENV:', ENVIRONMENT); 14 | require('angular-mocks/angular-mocks'); 15 | } 16 | 17 | const app = angular.module('app', ['ui.router']); 18 | 19 | // Components Entrypoint 20 | appComponents(app); 21 | 22 | // Common Components Entrypoint 23 | commonComponents(app); 24 | 25 | // App Services Entrypoint 26 | appServices(app); 27 | 28 | // Router Configuration 29 | // Components must be declared first since 30 | // Routes reference controllers that will be bound to route templates. 31 | // appConfiguration(app); 32 | -------------------------------------------------------------------------------- /app/index.scss: -------------------------------------------------------------------------------- 1 | // Imports will go here to import other component's style. 2 | // This allows for sharing of global variables. 3 | @import 'bourbon'; 4 | @import "~components/hero/hero.scss"; 5 | @import "~components/home/home.scss"; 6 | @import "~common/button/button.scss"; 7 | @import "~common/navbar/navbar.scss"; -------------------------------------------------------------------------------- /app/services/services.js: -------------------------------------------------------------------------------- 1 | import usersService from './users/users'; 2 | 3 | export default app => { 4 | INCLUDE_ALL_MODULES([usersService], app); 5 | } 6 | -------------------------------------------------------------------------------- /app/services/users/users.js: -------------------------------------------------------------------------------- 1 | import usersService from './users.service'; 2 | 3 | export default app => { 4 | app.factory('users', usersService); 5 | 6 | if (ENVIRONMENT === 'test') { 7 | require('./users.test.js'); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /app/services/users/users.service.js: -------------------------------------------------------------------------------- 1 | let usersService = function usersServiceFn () { 2 | return { 3 | name: 'users' 4 | }; 5 | }; 6 | 7 | export default usersService; 8 | -------------------------------------------------------------------------------- /app/services/users/users.test.js: -------------------------------------------------------------------------------- 1 | import UsersModule from './users' 2 | import UsersService from './users.service'; 3 | 4 | describe('Users', () => { 5 | let $rootScope, makeController; 6 | 7 | beforeEach(window.module('app')); 8 | 9 | describe('Service', () => { 10 | // component/directive specs 11 | let service = UsersService(); 12 | 13 | it('has property: name' ,() => { 14 | expect(service).to.have.property('name'); 15 | }); 16 | 17 | it('the name property has the correct value', () => { 18 | expect(service.name).to.equal('users'); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /generator/component/temp.component.js: -------------------------------------------------------------------------------- 1 | import template from './<%= name %>.html'; 2 | import controller from './<%= name %>.controller'; 3 | 4 | let <%= name %>Component = function () { 5 | return { 6 | restrict: 'E', 7 | scope: {}, 8 | template, 9 | controller, 10 | controllerAs: '<%= name %>Ctrl', 11 | bindToController: true 12 | }; 13 | }; 14 | 15 | export default <%= name %>Component; 16 | -------------------------------------------------------------------------------- /generator/component/temp.controller.js: -------------------------------------------------------------------------------- 1 | export default class <%= upCaseName %>Controller { 2 | constructor() { 3 | this.name = '<%= name %>'; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /generator/component/temp.html: -------------------------------------------------------------------------------- 1 |
2 |

{{ <%= name %>Ctrl.name }}

3 |
4 | -------------------------------------------------------------------------------- /generator/component/temp.js: -------------------------------------------------------------------------------- 1 | import <%= name %>Component from './<%= name %>.component'; 2 | 3 | export default app => { 4 | app.directive('<%= name %>', <%= name %>Component); 5 | 6 | if (ENVIRONMENT === 'test') { 7 | require('./<%= name %>.test.js'); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /generator/component/temp.scss: -------------------------------------------------------------------------------- 1 | .<%= name %> { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /generator/component/temp.test.js: -------------------------------------------------------------------------------- 1 | import <%= upCaseName %>Module from './<%= name %>' 2 | import <%= upCaseName %>Controller from './<%= name %>.controller'; 3 | import <%= upCaseName %>Component from './<%= name %>.component'; 4 | import <%= upCaseName %>Template from './<%= name %>.html'; 5 | 6 | describe('<%= upCaseName %>', () => { 7 | let $rootScope, makeController; 8 | 9 | beforeEach(window.module('app')); 10 | beforeEach(inject((_$rootScope_) => { 11 | $rootScope = _$rootScope_; 12 | makeController = () => { 13 | return new <%= upCaseName %>Controller(); 14 | }; 15 | })); 16 | 17 | describe('Module', () => { 18 | // top-level specs: i.e., routes, injection, naming 19 | }); 20 | 21 | describe('Controller', () => { 22 | // controller specs 23 | it('has a name property [REMOVE]', () => { // erase if removing this.name from the controller 24 | let controller = makeController(); 25 | expect(controller).to.have.property('name'); 26 | }); 27 | }); 28 | 29 | describe('Template', () => { 30 | // template specs 31 | // tip: use regex to ensure correct bindings are used e.g., {{ }} 32 | it('has name in template [REMOVE]', () => { 33 | expect(<%= upCaseName %>Template).to.match(/{{\s?vm\.name\s?}}/g); 34 | }); 35 | }); 36 | 37 | describe('Component', () => { 38 | // component/directive specs 39 | let component = <%= upCaseName %>Component(); 40 | 41 | it('includes the intended template',() => { 42 | expect(component.template).to.equal(<%= upCaseName %>Template); 43 | }); 44 | 45 | it('uses `controllerAs` syntax', () => { 46 | expect(component).to.have.property('controllerAs'); 47 | }); 48 | 49 | it('invokes the right controller', () => { 50 | expect(component.controller).to.equal(<%= upCaseName %>Controller); 51 | }); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /generator/service/temp.js: -------------------------------------------------------------------------------- 1 | import <%= name %>Service from './<%= name %>.service'; 2 | 3 | export default app => { 4 | app.factory('<%= name %>', <%= name %>Service); 5 | 6 | if (ENVIRONMENT === 'test') { 7 | require('./<%= name %>.test.js'); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /generator/service/temp.service.js: -------------------------------------------------------------------------------- 1 | let <%= name %>Service = function <%= name %>ServiceFn () { 2 | return { 3 | name: '<%= name %>' 4 | }; 5 | }; 6 | 7 | export default <%= name %>Service; 8 | -------------------------------------------------------------------------------- /generator/service/temp.test.js: -------------------------------------------------------------------------------- 1 | import <%= upCaseName %>Module from './<%= name %>' 2 | import <%= upCaseName %>Service from './<%= name %>.service'; 3 | 4 | describe('<%= upCaseName %>', () => { 5 | let $rootScope, makeController; 6 | 7 | beforeEach(window.module('app')); 8 | 9 | describe('Service', () => { 10 | // component/directive specs 11 | let service = <%= upCaseName %>Service(); 12 | 13 | it('has property: name' ,() => { 14 | expect(service).to.have.property('name'); 15 | }); 16 | 17 | it('the name property has the correct value', () => { 18 | expect(service.name).to.equal('<%= name %>'); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /gulpfile.babel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import gulp from 'gulp'; 4 | import path from 'path'; 5 | import concat from 'gulp-concat'; 6 | import replace from 'gulp-replace'; 7 | import clean from 'gulp-clean'; 8 | import sync from 'run-sequence'; 9 | import rename from 'gulp-rename'; 10 | import template from 'gulp-template'; 11 | import inject from 'gulp-inject-string'; 12 | import merge from 'merge-stream'; 13 | import fs from 'fs'; 14 | import yargs from 'yargs'; 15 | import lodash from 'lodash'; 16 | 17 | let root = './'; 18 | 19 | // helper method for resolving paths 20 | let resolveToApp = (glob) => { 21 | glob = glob || ''; 22 | return path.join(root, 'app', glob); // app/{glob} 23 | }; 24 | 25 | let resolveToComponents = (glob) => { 26 | glob = glob || ''; 27 | return path.join(root, 'app/components', glob); // app/components/{glob} 28 | }; 29 | 30 | let resolveToCommon = (glob) => { 31 | glob = glob || ''; 32 | return path.join(root, 'app/common', glob); 33 | } 34 | 35 | let resolveToServices = (glob) => { 36 | glob = glob || ''; 37 | return path.join(root, 'app/services', glob); 38 | } 39 | 40 | let resolveToGlobalSCSS = (glob) => { 41 | glob = glob || ''; 42 | return path.join(root, 'app', glob); 43 | } 44 | 45 | // map of all paths 46 | let paths = { 47 | js: resolveToComponents('**/*!(.test.js).js'), // exclude spec files 48 | html: [ 49 | resolveToApp('**/*.html'), 50 | path.join(root, 'index.html') 51 | ], 52 | entry: path.join(root, 'app/app.js'), 53 | output: root, 54 | blankTemplates: path.join(__dirname, 'generator', 'component/**/*.**'), 55 | blankServiceTemplates: path.join(__dirname, 'generator', 'service/**/*.**') 56 | }; 57 | 58 | // gulp component --name 'home' 59 | gulp.task('component', () => { 60 | let cap = (val) => { 61 | return val.charAt(0).toUpperCase() + val.slice(1); 62 | }; 63 | let name = yargs.argv.name; 64 | let parentPath = yargs.argv.parent || ''; 65 | let destPath = path.join(resolveToComponents(), parentPath, name); 66 | let indexPath = path.join(resolveToComponents(), parentPath, 'components.js'); 67 | let scssPath = path.join(resolveToGlobalSCSS(), parentPath, 'index.scss'); 68 | 69 | let includeAdditionallSCSSFileInGlobalIndex = gulp.src(scssPath) 70 | .pipe(inject.append(`\n@import "~components/${name}/${name}.scss";`)) 71 | .pipe(clean({force: true})) 72 | .pipe(gulp.dest(resolveToGlobalSCSS())); 73 | 74 | let addNewImportAndDependanciesToComponentsJS = gulp.src(indexPath) 75 | .pipe(inject.prepend(`import ${name}Component from './${name}/${name}';\n`)) 76 | .pipe(replace(/INCLUDE_ALL_MODULES\(\[(.*)\]/, function(match) { 77 | if (match.match(/\[\]/)) { 78 | return match.replace(']', `${name}Component]`); 79 | } else { 80 | return match.replace(']', `, ${name}Component]`); 81 | } 82 | })) 83 | .pipe(clean({force: true})) 84 | .pipe(gulp.dest(resolveToComponents())); 85 | 86 | let createNewComponentBasedOnComponentTemplate = gulp.src(paths.blankTemplates) 87 | .pipe(template({ 88 | name: name, 89 | upCaseName: cap(name) 90 | })) 91 | .pipe(rename((path) => { 92 | path.basename = path.basename.replace('temp', name); 93 | })) 94 | .pipe(gulp.dest(destPath)); 95 | 96 | return merge( 97 | merge(addNewImportAndDependanciesToComponentsJS, createNewComponentBasedOnComponentTemplate), 98 | includeAdditionallSCSSFileInGlobalIndex 99 | ); 100 | }); 101 | 102 | // gulp common_component --name 'users' 103 | gulp.task('common_component', () => { 104 | let cap = (val) => { 105 | return val.charAt(0).toUpperCase() + val.slice(1); 106 | }; 107 | let name = yargs.argv.name; 108 | let parentPath = yargs.argv.parent || ''; 109 | let destPath = path.join(resolveToCommon(), parentPath, name); 110 | let indexPath = path.join(resolveToCommon(), parentPath, 'components.js'); 111 | let scssPath = path.join(resolveToGlobalSCSS(), parentPath, 'index.scss'); 112 | 113 | let includeAdditionallSCSSFileInGlobalIndex = gulp.src(scssPath) 114 | .pipe(inject.append(`\n@import "~common/${name}/${name}.scss";`)) 115 | .pipe(clean({force: true})) 116 | .pipe(gulp.dest(resolveToGlobalSCSS())); 117 | 118 | let addNewImportAndDependanciesToComponentsJS = gulp.src(indexPath) 119 | .pipe(inject.prepend(`import ${name}Component from './${name}/${name}';\n`)) 120 | .pipe(replace(/INCLUDE_ALL_MODULES\(\[(.*)\]/, function(match) { 121 | if (match.match(/\[\]/)) { 122 | return match.replace(']', `${name}Component]`); 123 | } else { 124 | return match.replace(']', `, ${name}Component]`); 125 | } 126 | })) 127 | .pipe(clean({force: true})) 128 | .pipe(gulp.dest(resolveToCommon())); 129 | 130 | let createNewCommonComponentBasedOnComponentTemplate = gulp.src(paths.blankTemplates) 131 | .pipe(template({ 132 | name: name, 133 | upCaseName: cap(name) 134 | })) 135 | .pipe(rename((path) => { 136 | path.basename = path.basename.replace('temp', name); 137 | })) 138 | .pipe(gulp.dest(destPath)); 139 | 140 | return merge( 141 | merge(addNewImportAndDependanciesToComponentsJS, createNewCommonComponentBasedOnComponentTemplate), 142 | includeAdditionallSCSSFileInGlobalIndex 143 | ); 144 | }); 145 | 146 | // gulp service --name 147 | gulp.task('service', () => { 148 | let cap = (val) => { 149 | return val.charAt(0).toUpperCase() + val.slice(1); 150 | }; 151 | let name = yargs.argv.name; 152 | let parentPath = yargs.argv.parent || ''; 153 | let destPath = path.join(resolveToServices(), parentPath, name); 154 | let indexPath = path.join(resolveToServices(), parentPath, 'services.js'); 155 | 156 | let addNewImportAndDependanciesToServiceJS = gulp.src(indexPath) 157 | .pipe(inject.prepend(`import ${name}Service from './${name}/${name}';\n`)) 158 | .pipe(replace(/INCLUDE_ALL_MODULES\(\[(.*)\]/, function(match) { 159 | if (match.match(/\[\]/)) { 160 | return match.replace(']', `${name}Service]`); 161 | } else { 162 | return match.replace(']', `, ${name}Service]`); 163 | } 164 | })) 165 | .pipe(clean({force: true})) 166 | .pipe(gulp.dest(resolveToServices())); 167 | 168 | let createNewServiceFilesWithProperDirectory = gulp.src(paths.blankServiceTemplates) 169 | .pipe(template({ 170 | name: name, 171 | upCaseName: cap(name) 172 | })) 173 | .pipe(rename((path) => { 174 | path.basename = path.basename.replace('temp', name); 175 | })) 176 | .pipe(gulp.dest(destPath)); 177 | 178 | return merge(addNewImportAndDependanciesToServiceJS, createNewServiceFilesWithProperDirectory); 179 | }); 180 | 181 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpackConfig = require('./webpack.config'); 3 | var entry = path.resolve(webpackConfig.context, webpackConfig.entry); //accessing [0] because there are mutli entry points for webpack hot loader 4 | var preprocessors = {}; 5 | preprocessors[entry] = ['webpack']; 6 | preprocessors['**/*.html'] = ['ng-html2js']; 7 | 8 | module.exports = function(config) { 9 | config.set({ 10 | 11 | // base path that will be used to resolve all patterns (eg. files, exclude) 12 | basePath: '', 13 | 14 | // frameworks to use 15 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 16 | frameworks: ['mocha', 'chai'], 17 | 18 | // list of files / patterns to load in the browser 19 | files: [entry], 20 | webpack: webpackConfig, 21 | 22 | // list of files to exclude 23 | exclude: [], 24 | 25 | // test results reporter to use 26 | // possible values: 'dots', 'progress' 27 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 28 | reporters: ['progress'], 29 | 30 | preprocessors: preprocessors, 31 | 32 | // web server port 33 | port: 9876, 34 | 35 | // enable / disable colors in the output (reporters and logs) 36 | colors: true, 37 | 38 | // level of logging 39 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 40 | logLevel: config.LOG_INFO, 41 | 42 | 43 | // enable / disable watching file and executing tests whenever any file changes 44 | autoWatch: true, 45 | 46 | ngHtml2JsPreprocessor: { 47 | stripPrefix: 'app/components/', 48 | moduleName: 'my.templates' 49 | }, 50 | 51 | reporters: ['mocha'], 52 | 53 | // start these browsers 54 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 55 | browsers: ['Chrome'], 56 | 57 | // Continuous Integration mode 58 | // if true, Karma captures browsers, runs the tests and exits 59 | singleRun: false, 60 | 61 | plugins: [ 62 | require('karma-webpack'), 63 | 'karma-chai', 64 | 'karma-mocha', 65 | 'karma-chrome-launcher', 66 | 'karma-ng-html2js-preprocessor', 67 | 'karma-mocha-reporter' 68 | ] 69 | }); 70 | } 71 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-starter-es6-webpack", 3 | "version": "1.0.3", 4 | "description": "An Application Shell for generating Angular Applications with Webpack and ES6", 5 | "main": "app.js", 6 | "scripts": { 7 | "dev": "export NODE_ENV=development && webpack-dev-server --devtool=source-map --progress --colors --port=9000 --content-base app", 8 | "test": "export NODE_ENV=test && karma start", 9 | "build": "export NODE_ENV=production && node node_modules/.bin/webpack && cp app/index.html dist/index.html" 10 | }, 11 | "keywords": [ 12 | "angular", 13 | "webpack", 14 | "es6" 15 | ], 16 | "author": "Sean Larkin", 17 | "license": "ISC", 18 | "devDependencies": { 19 | "angular-mocks": "^1.4.7", 20 | "chai": "^3.3.0", 21 | "file-loader": "^0.8.4", 22 | "gulp": "^3.9.0", 23 | "gulp-clean": "^0.3.1", 24 | "gulp-concat": "^2.6.0", 25 | "gulp-inject-string": "^1.0.0", 26 | "gulp-rename": "^1.2.2", 27 | "gulp-replace": "^0.5.4", 28 | "gulp-template": "^3.0.0", 29 | "karma": "^0.13.10", 30 | "karma-chai": "^0.1.0", 31 | "karma-chrome-launcher": "^0.2.1", 32 | "karma-mocha": "^0.2.0", 33 | "karma-mocha-reporter": "^1.1.1", 34 | "karma-ng-html2js-preprocessor": "^0.2.0", 35 | "karma-verbose-reporter": "0.0.3", 36 | "karma-webpack": "^1.7.0", 37 | "lodash": "^3.10.1", 38 | "merge-stream": "^1.0.0", 39 | "mocha": "^2.3.3", 40 | "ng-loader": "^1.0.0", 41 | "ngrequire-webpack-plugin": "^2.0.23", 42 | "raw-loader": "^0.5.1", 43 | "run-sequence": "^1.1.0", 44 | "url-loader": "^0.5.6", 45 | "webpack": "^1.12.13", 46 | "webpack-dev-server": "^1.12.0", 47 | "yargs": "^3.9.0" 48 | }, 49 | "dependencies": { 50 | "angular": "^1.4.7", 51 | "angular-ui-router": "^0.2.15", 52 | "babel-core": "^6.13.2", 53 | "babel-loader": "^6.2.4", 54 | "babel-preset-es2015": "^6.13.2", 55 | "babel-preset-stage-0": "^6.5.0", 56 | "babel-preset-stage-1": "^6.5.0", 57 | "babel-preset-stage-2": "^6.5.0", 58 | "css-loader": "^0.19.0", 59 | "html-loader": "^0.3.0", 60 | "jscs": "^3.0.5", 61 | "jscs-loader": "^0.3.0", 62 | "karma-chai": "^0.1.0", 63 | "ng-annotate-loader": "0.0.10", 64 | "ngrequire-webpack-plugin": "^2.0.23", 65 | "node-bourbon": "^4.2.3", 66 | "node-sass": "^3.3.3", 67 | "sass-loader": "^3.0.0", 68 | "scss-loader": "0.0.1", 69 | "style-loader": "^0.12.4" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var nodeEnvironment = process.env.NODE_ENV 3 | var bourbon = require('node-bourbon').includePaths; 4 | var _ = require('lodash'); 5 | 6 | var config = { 7 | context: __dirname + '/app', 8 | entry: './index.js', 9 | plugins: [ 10 | new webpack.HotModuleReplacementPlugin(), 11 | new webpack.DefinePlugin({ 12 | 'INCLUDE_ALL_MODULES': function includeAllModulesGlobalFn(modulesArray, application) { 13 | modulesArray.forEach(function executeModuleIncludesFn(moduleFn) { 14 | moduleFn(application); 15 | }); 16 | }, 17 | ENVIRONMENT: JSON.stringify(nodeEnvironment) 18 | }) 19 | ], 20 | output: { 21 | path: __dirname + '/app', 22 | filename: 'bundle.js' 23 | }, 24 | resolve: { 25 | root: __dirname + '/app' 26 | }, 27 | jscs: { 28 | // JSCS errors are displayed by default as warnings. 29 | // Set `emitErrors` to `true` to display them as errors. 30 | emitErrors: false, 31 | 32 | // JSCS errors do not interrupt the compilation. 33 | // Set `failOnHint` to `true` if you want any file with 34 | // JSCS errors to fail. 35 | failOnHint: false 36 | }, 37 | module: { 38 | preLoaders: [{ 39 | test: /\.js$/, 40 | exclude: /node_modules/, 41 | loader: 'jscs-loader' 42 | }], 43 | loaders: [ 44 | {test: /\.js$/, exclude: /(node_modules)/, loader: 'babel'}, 45 | {test: /\.html/, exclude: /(node_modules)/, loader: 'html-loader'}, 46 | {test: /\.s?css$/, loader: 'style!css!sass?includePaths[]=' + bourbon }, 47 | {test: /\.(png|jpg)$/, loader: 'url-loader?mimetype=image/png'} 48 | ] 49 | } 50 | } 51 | 52 | switch (nodeEnvironment) { 53 | case 'production': 54 | config.output.path = __dirname + '/dist'; 55 | config.plugins.push(new webpack.optimize.UglifyJsPlugin()); 56 | config.plugins.push(new webpack.optimize.DedupePlugin()); 57 | config.plugins.push(new webpack.optimize.OccurenceOrderPlugin()); 58 | config.plugins.push(new webpack.optimize.CommonsChunkPlugin({name: 'vendor', minChunks: Infinity})); 59 | 60 | config.output.filename = '[name].js'; 61 | 62 | config.entry = { 63 | bundle: './index.js', 64 | vendor: ['angular', 'angular-ui-router', 'lodash'] 65 | } 66 | config.devtool = 'source-map'; 67 | break; 68 | 69 | case 'test': 70 | config.entry = './index.js'; 71 | break; 72 | 73 | case 'development': 74 | config.entry = ['./index.js', 'webpack/hot/dev-server']; 75 | config.devtool = 'source-map'; 76 | break; 77 | 78 | default: 79 | console.warn('Unknown or Undefigned Node Environment. Please refer to package.json for available build commands.'); 80 | } 81 | 82 | module.exports = config; 83 | --------------------------------------------------------------------------------