├── src ├── common │ ├── user │ │ ├── user.spec.js │ │ ├── user.js │ │ └── user.factory.js │ ├── hero │ │ ├── hero.jade │ │ ├── hero.controller.js │ │ ├── hero.styl │ │ ├── hero.js │ │ ├── hero.component.js │ │ └── hero.spec.js │ ├── navbar │ │ ├── navbar.controller.js │ │ ├── navbar.js │ │ ├── navbar.styl │ │ ├── navbar.component.js │ │ ├── navbar.html │ │ └── navbar.spec.js │ └── common.js ├── styles.scss ├── components │ ├── contact │ │ ├── contact.address.jade │ │ ├── contact.controller.js │ │ ├── contact.address.controller.js │ │ ├── contact.form.jade │ │ ├── contact.form.component.js │ │ ├── contact.address.component.js │ │ ├── contact.component.js │ │ ├── contact.js │ │ ├── contact.html │ │ ├── contact.address.js │ │ ├── contact.form.js │ │ └── contact.form.controller.js │ ├── home │ │ ├── home.controller.js │ │ ├── home.component.js │ │ ├── home.html │ │ └── home.js │ ├── new │ │ ├── new.html │ │ ├── new.controller.js │ │ ├── new.component.js │ │ └── new.js │ ├── forms │ │ ├── forms.component.js │ │ ├── forms.html │ │ ├── forms.js │ │ ├── forms.controller.js │ │ ├── base.js │ │ └── forms.spec.js │ ├── github │ │ ├── github.component.js │ │ ├── github.controller.js │ │ ├── github.html │ │ ├── github.js │ │ └── github.service.js │ └── components.js ├── app.html ├── app.component.js ├── utils.js ├── app.js ├── index.html └── utils.spec.js ├── requirements.txt ├── .gitignore ├── config ├── webpack.dev.js ├── webpack.common.js └── webpack.prod.js ├── webpack.config.js ├── .travis.yml ├── .eslintrc ├── spec.bundle.js ├── test.robot ├── karma.conf.js ├── package.json └── README.rst /src/common/user/user.spec.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 80px; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/contact/contact.address.jade: -------------------------------------------------------------------------------- 1 | h2 Contact Address 2 | div {{ vm.address }} 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | robotframework 2 | robotframework-selenium2library 3 | robotframework-debuglibrary 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | .env/ 4 | log.html 5 | output.xml 6 | report.html 7 | *.png 8 | npm-debug.log 9 | 10 | -------------------------------------------------------------------------------- /src/common/hero/hero.jade: -------------------------------------------------------------------------------- 1 | div.jumbotron 2 | h1 Angular, ES6, Webpack Starter! 3 | h3 You can find my template inside {{ vm.name }}.html 4 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 | -------------------------------------------------------------------------------- /src/common/hero/hero.controller.js: -------------------------------------------------------------------------------- 1 | class HeroController { 2 | constructor() { 3 | this.name = 'hero'; 4 | } 5 | } 6 | 7 | export default HeroController; 8 | -------------------------------------------------------------------------------- /src/components/home/home.controller.js: -------------------------------------------------------------------------------- 1 | class HomeController { 2 | constructor() { 3 | this.name = 'home'; 4 | } 5 | } 6 | 7 | export default HomeController; 8 | -------------------------------------------------------------------------------- /src/components/new/new.html: -------------------------------------------------------------------------------- 1 | 2 |

You can find my template inside {{ vm.name }}.html

3 |
4 | New Component Example. 5 |
6 | -------------------------------------------------------------------------------- /src/common/hero/hero.styl: -------------------------------------------------------------------------------- 1 | @import '../common' 2 | 3 | .hero 4 | background-color $darkBgColor 5 | height 50rem 6 | padding 3rem 7 | * 8 | color $lightTextColor 9 | -------------------------------------------------------------------------------- /src/common/navbar/navbar.controller.js: -------------------------------------------------------------------------------- 1 | class NavbarController { 2 | constructor() { 3 | this.name = 'navbar'; 4 | } 5 | } 6 | 7 | export default NavbarController; 8 | -------------------------------------------------------------------------------- /src/components/new/new.controller.js: -------------------------------------------------------------------------------- 1 | class NewController { 2 | constructor() { 3 | this.name = 'New Component Syntax'; 4 | } 5 | } 6 | 7 | export default NewController; 8 | -------------------------------------------------------------------------------- /config/webpack.dev.js: -------------------------------------------------------------------------------- 1 | const webpackMerge = require('webpack-merge'); 2 | const commonConfig = require('./webpack.common.js'); 3 | 4 | module.exports = webpackMerge(commonConfig, {}); 5 | -------------------------------------------------------------------------------- /src/components/contact/contact.controller.js: -------------------------------------------------------------------------------- 1 | class ContactController { 2 | constructor() { 3 | this.name = 'Contact Us'; 4 | } 5 | } 6 | 7 | export default ContactController; 8 | -------------------------------------------------------------------------------- /src/app.component.js: -------------------------------------------------------------------------------- 1 | import template from './app.html'; 2 | 3 | let appComponent = () => { 4 | return { 5 | template, 6 | restrict: 'E' 7 | }; 8 | }; 9 | 10 | export default appComponent; 11 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | 3 | // add field to existing json schema 4 | let addField = function(schema, field) { 5 | return _.merge(schema, {'properties': field}); 6 | }; 7 | 8 | export default addField; 9 | -------------------------------------------------------------------------------- /src/common/user/user.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | import UserFactory from './user.factory'; 3 | 4 | let userModule = angular.module('user', []) 5 | 6 | .factory('User', UserFactory); 7 | 8 | export default userModule; 9 | -------------------------------------------------------------------------------- /src/components/contact/contact.address.controller.js: -------------------------------------------------------------------------------- 1 | class ContactAddressController { 2 | constructor() { 3 | this.name = 'Our Address'; 4 | this.address = 'Bornheimer Straße 37; 531111 Bonn'; 5 | } 6 | } 7 | 8 | export default ContactAddressController; 9 | -------------------------------------------------------------------------------- /src/common/common.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | import Navbar from './navbar/navbar'; 3 | import Hero from './hero/hero'; 4 | import User from './user/user'; 5 | 6 | export default angular.module('app.common', [ 7 | Navbar.name, 8 | Hero.name, 9 | User.name 10 | ]); 11 | -------------------------------------------------------------------------------- /src/common/hero/hero.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | import uiRouter from 'angular-ui-router'; 3 | import heroComponent from './hero.component'; 4 | 5 | let heroModule = angular.module('hero', [ 6 | uiRouter 7 | ]) 8 | 9 | .directive('hero', heroComponent); 10 | 11 | export default heroModule; 12 | -------------------------------------------------------------------------------- /src/components/contact/contact.form.jade: -------------------------------------------------------------------------------- 1 | h2 Contact Form 2 | form(name='form' sf-schema='vm.schema', sf-form='vm.form', sf-model='vm.model', ng-submit="vm.onSubmit(form)") 3 | 4 | h3 Schema 5 | pre {{vm.schema | json}} 6 | 7 | h3 Form 8 | pre {{vm.form | json}} 9 | 10 | h3 Model 11 | pre {{vm.model | json}} 12 | -------------------------------------------------------------------------------- /src/components/new/new.component.js: -------------------------------------------------------------------------------- 1 | import template from './new.html'; 2 | import controller from './new.controller'; 3 | 4 | let newComponent = { 5 | restrict: 'E', 6 | scope: {}, 7 | template: template, 8 | controller: controller, 9 | controllerAs: 'vm' 10 | }; 11 | 12 | export default newComponent; 13 | -------------------------------------------------------------------------------- /src/common/navbar/navbar.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | import uiRouter from 'angular-ui-router'; 3 | import navbarComponent from './navbar.component'; 4 | 5 | let navbarModule = angular.module('navbar', [ 6 | uiRouter 7 | ]) 8 | 9 | .directive('navbar', navbarComponent); 10 | 11 | export default navbarModule; 12 | -------------------------------------------------------------------------------- /src/common/user/user.factory.js: -------------------------------------------------------------------------------- 1 | let UserFactory = function () { 2 | const user = {}; 3 | 4 | let getUser = () => { 5 | return user; 6 | }; 7 | 8 | let isSignedIn = () => { 9 | return user.isSignedIn; 10 | }; 11 | 12 | return { getUser, isSignedIn }; 13 | }; 14 | 15 | export default UserFactory; 16 | -------------------------------------------------------------------------------- /src/components/forms/forms.component.js: -------------------------------------------------------------------------------- 1 | import template from './forms.html'; 2 | import controller from './forms.controller'; 3 | 4 | let formsComponent = { 5 | restrict: 'E', 6 | scope: {}, 7 | template: template, 8 | controller: controller, 9 | controllerAs: 'vm' 10 | }; 11 | 12 | export default formsComponent; 13 | -------------------------------------------------------------------------------- /src/components/home/home.component.js: -------------------------------------------------------------------------------- 1 | import template from './home.html'; 2 | import controller from './home.controller'; 3 | 4 | let homeComponent = { 5 | restrict: 'E', 6 | scope: {}, 7 | template, 8 | controller, 9 | controllerAs: 'vm', 10 | bindToController: true 11 | }; 12 | 13 | export default homeComponent; 14 | -------------------------------------------------------------------------------- /src/components/github/github.component.js: -------------------------------------------------------------------------------- 1 | import template from './github.html'; 2 | import controller from './github.controller'; 3 | 4 | let githubComponent = { 5 | restrict: 'E', 6 | scope: {}, 7 | template, 8 | controller, 9 | controllerAs: 'vm', 10 | bindToController: true 11 | }; 12 | 13 | export default githubComponent; 14 | -------------------------------------------------------------------------------- /src/common/navbar/navbar.styl: -------------------------------------------------------------------------------- 1 | @import '../common' 2 | 3 | .navbar 4 | height 6.5rem 5 | background-color $primaryColor 6 | padding 1rem 7 | .nav-links 8 | .logo 9 | display inline-block 10 | .logo 11 | color $lightTextColor 12 | margin-right 50% 13 | .nav-links 14 | span 15 | color $lightTextColor 16 | font-size 1.6rem 17 | -------------------------------------------------------------------------------- /src/components/home/home.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 |
6 |
7 |

You can find my template inside {{ vm.name }}.html

8 | 14 |
15 |
16 | -------------------------------------------------------------------------------- /src/components/contact/contact.form.component.js: -------------------------------------------------------------------------------- 1 | import template from './contact.form.jade'; 2 | import controller from './contact.form.controller'; 3 | 4 | let contactFormComponent = { 5 | restrict: 'E', 6 | scope: {}, 7 | template, 8 | controller, 9 | controllerAs: 'vm', 10 | bindToController: true 11 | }; 12 | 13 | export default contactFormComponent; 14 | -------------------------------------------------------------------------------- /src/common/hero/hero.component.js: -------------------------------------------------------------------------------- 1 | import template from './hero.jade'; 2 | import controller from './hero.controller'; 3 | 4 | let heroComponent = function () { 5 | return { 6 | restrict: 'E', 7 | scope: {}, 8 | template, 9 | controller, 10 | controllerAs: 'vm', 11 | bindToController: true 12 | }; 13 | }; 14 | 15 | export default heroComponent; 16 | -------------------------------------------------------------------------------- /src/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: 'vm', 11 | bindToController: true 12 | }; 13 | }; 14 | 15 | export default navbarComponent; 16 | -------------------------------------------------------------------------------- /src/components/contact/contact.address.component.js: -------------------------------------------------------------------------------- 1 | import template from './contact.address.jade'; 2 | import controller from './contact.address.controller'; 3 | 4 | let contactAddressComponent = { 5 | restrict: 'E', 6 | scope: {}, 7 | template, 8 | controller, 9 | controllerAs: 'vm', 10 | bindToController: true 11 | }; 12 | 13 | export default contactAddressComponent; 14 | -------------------------------------------------------------------------------- /src/components/github/github.controller.js: -------------------------------------------------------------------------------- 1 | class GithubController { 2 | 3 | constructor(githubService) { 4 | this.result = {}; 5 | this.service = githubService; 6 | } 7 | 8 | getDetails() { 9 | this.service.getItems(this.githubUsername).then((res) => { 10 | this.result = res.data; 11 | }); 12 | } 13 | 14 | } 15 | 16 | export default GithubController; 17 | -------------------------------------------------------------------------------- /src/components/contact/contact.component.js: -------------------------------------------------------------------------------- 1 | import template from './contact.html'; 2 | import controller from './contact.controller'; 3 | 4 | let contactComponent = function () { 5 | return { 6 | restrict: 'E', 7 | scope: {}, 8 | template, 9 | controller, 10 | controllerAs: 'vm', 11 | bindToController: true 12 | }; 13 | }; 14 | 15 | export default contactComponent; 16 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | switch (process.env.NODE_ENV) { 2 | case 'prod': 3 | case 'production': 4 | module.exports = require('./config/webpack.prod'); 5 | break; 6 | case 'test': 7 | case 'testing': 8 | module.exports = require('./config/webpack.test'); 9 | break; 10 | case 'dev': 11 | case 'development': 12 | default: 13 | module.exports = require('./config/webpack.dev'); 14 | } 15 | -------------------------------------------------------------------------------- /src/components/new/new.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | import uiRouter from 'angular-ui-router'; 3 | import newComponent from './new.component'; 4 | 5 | let newModule = angular.module('new', [ 6 | uiRouter 7 | ]) 8 | 9 | .config(($stateProvider) => { 10 | $stateProvider 11 | .state('new', { 12 | url: '/new', 13 | template: '' 14 | }); 15 | }) 16 | 17 | .component('new', newComponent); 18 | 19 | export default newModule; 20 | -------------------------------------------------------------------------------- /src/components/forms/forms.html: -------------------------------------------------------------------------------- 1 | 2 |

Forms

3 |
4 |
9 | 10 |

Schema

11 |
{{vm.schema | json}}
12 | 13 |

Form

14 |
{{vm.form | json}}
15 | 16 |

Model

17 |
{{vm.model | json}}
18 | 19 |
20 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | import 'jquery'; 2 | import 'bootstrap/dist/css/bootstrap.min.css'; 3 | import angular from 'angular'; 4 | import uiRouter from 'angular-ui-router'; 5 | import AppComponent from './app.component.js'; 6 | import Common from './common/common'; 7 | import Components from './components/components'; 8 | import './styles.scss'; 9 | 10 | angular.module('myApp', [ 11 | uiRouter, 12 | Common.name, 13 | Components.name 14 | ]) 15 | .directive('app', AppComponent); 16 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Angular Webpack Example 7 | 8 | 9 | 10 |
11 |
12 | 13 | Loading... 14 | 15 |
16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /src/components/contact/contact.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | import uiRouter from 'angular-ui-router'; 3 | import contactComponent from './contact.component'; 4 | 5 | let contactModule = angular.module('contact', [ 6 | uiRouter 7 | ]) 8 | 9 | .config(($stateProvider) => { 10 | $stateProvider 11 | .state('contact', { 12 | url: '/contact', 13 | template: '' 14 | }); 15 | }) 16 | 17 | .component('contact', contactComponent); 18 | 19 | export default contactModule; 20 | -------------------------------------------------------------------------------- /src/components/github/github.html: -------------------------------------------------------------------------------- 1 | 2 |

Github User Details

3 |
4 | 5 | 6 | 7 | 13 |
{{vm.result | json}}
14 |
15 | -------------------------------------------------------------------------------- /src/components/home/home.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | import uiRouter from 'angular-ui-router'; 3 | import homeComponent from './home.component'; 4 | 5 | let homeModule = angular.module('home', [ 6 | uiRouter 7 | ]) 8 | 9 | .config(($stateProvider, $urlRouterProvider) => { 10 | $urlRouterProvider.otherwise('/'); 11 | 12 | $stateProvider 13 | .state('home', { 14 | url: '/', 15 | template: '' 16 | }); 17 | }) 18 | 19 | .component('home', homeComponent); 20 | 21 | export default homeModule; 22 | -------------------------------------------------------------------------------- /src/components/contact/contact.html: -------------------------------------------------------------------------------- 1 | 2 |

You can find my template inside {{ vm.name }}.html

3 |
4 |

Contact

5 |
6 |
7 | 15 |
16 |
17 | 18 |
19 |
20 |
21 | -------------------------------------------------------------------------------- /src/components/contact/contact.address.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | import uiRouter from 'angular-ui-router'; 3 | import contactAddressComponent from './contact.address.component'; 4 | 5 | let contactAddressModule = angular.module('contactAddress', [ 6 | uiRouter 7 | ]) 8 | 9 | .config(($stateProvider) => { 10 | $stateProvider 11 | .state('contact.address', { 12 | url: '/address', 13 | template: '
' 14 | }); 15 | }) 16 | 17 | .component('address', contactAddressComponent); 18 | 19 | export default contactAddressModule; 20 | -------------------------------------------------------------------------------- /src/components/components.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | import Home from './home/home'; 3 | import Contact from './contact/contact'; 4 | import ContactAddress from './contact/contact.address'; 5 | import ContactForm from './contact/contact.form'; 6 | import Github from './github/github'; 7 | import Forms from './forms/forms'; 8 | import New from './new/new'; 9 | 10 | export default angular.module('app.components', [ 11 | Contact.name, 12 | ContactAddress.name, 13 | ContactForm.name, 14 | Home.name, 15 | Github.name, 16 | Forms.name, 17 | New.name 18 | ]); 19 | -------------------------------------------------------------------------------- /src/components/github/github.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | import uiRouter from 'angular-ui-router'; 3 | import githubComponent from './github.component'; 4 | import githubService from './github.service'; 5 | 6 | let githubModule = angular.module('github', [ 7 | uiRouter 8 | ]) 9 | 10 | .config(($stateProvider) => { 11 | $stateProvider 12 | .state('github', { 13 | url: '/github', 14 | template: '' 15 | }); 16 | }) 17 | 18 | .component('github', githubComponent) 19 | .service('githubService', githubService); 20 | 21 | export default githubModule; 22 | -------------------------------------------------------------------------------- /src/components/forms/forms.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | import uiRouter from 'angular-ui-router'; 3 | import formsComponent from './forms.component'; 4 | // order important: tv4 -> objectpath -> shemaForm -> shemaForm-bootstrap 5 | import 'tv4'; 6 | import 'objectpath'; 7 | import schemaForm from 'angular-schema-form'; 8 | import 'script!angular-schema-form-bootstrap'; 9 | 10 | let formsModule = angular.module('forms', [ 11 | schemaForm.name, 12 | uiRouter 13 | ]) 14 | 15 | .config(($stateProvider) => { 16 | $stateProvider 17 | .state('forms', { 18 | url: '/forms', 19 | template: '' 20 | }); 21 | }) 22 | 23 | .component('forms', formsComponent); 24 | 25 | export default formsModule; 26 | -------------------------------------------------------------------------------- /src/components/github/github.service.js: -------------------------------------------------------------------------------- 1 | class GithubService { 2 | 3 | constructor($http) { 4 | this.$http = $http; 5 | } 6 | 7 | getItems(githubUsername) { 8 | var githubUrl = 'https://api.github.com'; 9 | return this.$http({ 10 | method: 'JSONP', 11 | url: githubUrl + '/users/' + 12 | githubUsername + '?callback=JSON_CALLBACK' 13 | }).success(function(data) { 14 | // this callback will be called asynchronously 15 | // when the response is available 16 | return data.data.toJSON(); 17 | }). 18 | error(function(data, status) { 19 | // called asynchronously if an error occurs 20 | // or server returns response with an error status. 21 | alert(status); 22 | }); 23 | } 24 | 25 | } 26 | 27 | export default GithubService; 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 4.4.5 4 | env: 5 | - NODE_ENV=prod 6 | - NODE_ENV=dev 7 | cache: 8 | directories: 9 | - node_modules 10 | dist: trusty 11 | addons: 12 | apt: 13 | sources: 14 | - google-chrome 15 | packages: 16 | - google-chrome-stable 17 | install: 18 | - npm install -g babel 19 | - npm install -g webpack 20 | - npm install -g webpack-dev-server 21 | - npm install -g eslint 22 | - npm install 23 | - sudo pip install -r requirements.txt 24 | before_script: 25 | - wget "http://chromedriver.storage.googleapis.com/2.30/chromedriver_linux64.zip" 26 | - unzip chromedriver_linux64.zip 27 | - sudo mv chromedriver /usr/local/bin 28 | - "export DISPLAY=:99.0" 29 | - "sh -e /etc/init.d/xvfb start" 30 | - sleep 2 31 | script: 32 | - npm run lint:travis 33 | - npm run test 34 | - pybot test.robot 35 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [angular], 3 | "extends": ["eslint:recommended", "angular"], 4 | "rules": { 5 | "indent": [ 6 | 2, 7 | 2, 8 | { "SwitchCase": 1 } 9 | ], 10 | "quotes": [ 11 | 2, 12 | "single" 13 | ], 14 | "linebreak-style": [ 15 | 2, 16 | "unix" 17 | ], 18 | "semi": [ 19 | 2, 20 | "always" 21 | ], 22 | "angular/di": [ 23 | 2, 24 | "$inject" 25 | ] 26 | }, 27 | "env": { 28 | "es6": true, 29 | "browser": true, 30 | "jasmine": true, 31 | "mocha": true, 32 | "amd": true 33 | }, 34 | "ecmaFeatures": { 35 | "modules": true 36 | }, 37 | "globals" : { 38 | "chai" : false, 39 | "describe" : false, 40 | "it" : false, 41 | "before" : false, 42 | "beforeEach" : false, 43 | "after" : false, 44 | "afterEach" : false, 45 | "inject" : false 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/components/contact/contact.form.js: -------------------------------------------------------------------------------- 1 | import angular from 'angular'; 2 | import uiRouter from 'angular-ui-router'; 3 | import contactFormComponent from './contact.form.component'; 4 | import 'angular-sanitize'; 5 | require('script!tv4/tv4.js'); 6 | require('script!objectpath/lib/ObjectPath'); 7 | require('script!angular-schema-form/dist/schema-form'); 8 | require('script!angular-schema-form/dist/bootstrap-decorator'); 9 | // XXX: regular import do not work! 10 | // import schemaForm from 'angular-schema-form/dist/schema-form'; 11 | // import 'angular-schema-form/dist/bootstrap-decorator'; 12 | 13 | let contactFormModule = angular.module('contactForm', [ 14 | uiRouter, 15 | 'schemaForm' 16 | ]) 17 | 18 | .config(($stateProvider) => { 19 | $stateProvider 20 | .state('contact.form', { 21 | url: '/form', 22 | template: '' 23 | }); 24 | }) 25 | 26 | .component('contactform', contactFormComponent); 27 | 28 | export default contactFormModule; 29 | -------------------------------------------------------------------------------- /src/components/forms/forms.controller.js: -------------------------------------------------------------------------------- 1 | import BaseFormController from './base.js'; 2 | 3 | class FormController extends BaseFormController { 4 | constructor($scope) { 5 | super($scope); 6 | this.schema = { 7 | type: 'object', 8 | properties: { 9 | title: { 10 | type: 'string', 11 | enum: ['dr','jr','sir','mrs','mr','NaN','dj'] 12 | }, 13 | name: { type: 'string', minLength: 2, title: 'Name', description: 'Name or alias' }, 14 | email: { 15 | 'title': 'Email', 16 | 'type': 'string', 17 | 'pattern': '^\\S+@\\S+$', 18 | 'description': 'Invalid email address' 19 | }, 20 | comment: { 21 | 'title': 'Comment', 22 | 'type': 'string', 23 | 'maxLength': 20, 24 | 'validationMessage': 'Maximum length is 20 characters' 25 | } 26 | }, 27 | 'required': [ 28 | 'name' 29 | ] 30 | }; 31 | } 32 | } 33 | 34 | export default FormController; 35 | -------------------------------------------------------------------------------- /src/components/contact/contact.form.controller.js: -------------------------------------------------------------------------------- 1 | class ContactFormController { 2 | constructor($scope, $log) { 3 | this.name = 'Contact Us'; 4 | this.model = {}; 5 | this.schema = { 6 | type: 'object', 7 | properties: { 8 | name: { type: 'string', minLength: 2, title: 'Name', description: 'Name or alias' }, 9 | title: { 10 | type: 'string', 11 | enum: ['dr','jr','sir','mrs','mr','NaN','dj'] 12 | } 13 | }, 14 | 'required': [ 15 | 'name' 16 | ] 17 | }; 18 | this.form = [ 19 | '*', 20 | { 21 | type: 'submit', 22 | title: 'Save' 23 | } 24 | ]; 25 | 26 | this.onSubmit = function(form) { 27 | // First we broadcast an event so all fields validate themselves 28 | $scope.$broadcast('schemaFormValidate'); 29 | 30 | // Then we check if the form is valid 31 | if (form.$valid) { 32 | $log('form is valid'); 33 | } else { 34 | $log('form is invalid'); 35 | } 36 | }; 37 | } 38 | } 39 | 40 | export default ContactFormController; 41 | -------------------------------------------------------------------------------- /src/utils.spec.js: -------------------------------------------------------------------------------- 1 | import addField from './utils'; 2 | 3 | // do not truncate diffs 4 | chai.config.truncateThreshold = 0; 5 | 6 | 7 | describe('Test addField', () => { 8 | 9 | it('add empty field', () => { 10 | let schema = { 11 | 'properties': { 12 | 'one': {'one': 'foo'} 13 | } 14 | }; 15 | addField(schema, {}); 16 | let expected = { 17 | 'properties': { 18 | 'one': {'one': 'foo'} 19 | } 20 | }; 21 | // console.log(JSON.stringify(expected, null, 2)); 22 | // console.log(JSON.stringify(schema, null, 2)); 23 | expect(expected).to.deep.equal(schema); 24 | }); 25 | 26 | it('add simple field', () => { 27 | let schema = { 28 | 'properties': { 29 | 'one': {'one': 'foo'} 30 | } 31 | }; 32 | addField(schema, {'two': {'two': 'bar'}}); 33 | let expected = { 34 | 'properties': { 35 | 'one': {'one': 'foo'}, 36 | 'two': {'two': 'bar'} 37 | } 38 | }; 39 | // console.log(JSON.stringify(expected, null, 2)); 40 | // console.log(JSON.stringify(schema, null, 2)); 41 | expect(expected).to.deep.equal(schema); 42 | }); 43 | 44 | }); 45 | -------------------------------------------------------------------------------- /src/common/navbar/navbar.html: -------------------------------------------------------------------------------- 1 | 25 | -------------------------------------------------------------------------------- /src/components/forms/base.js: -------------------------------------------------------------------------------- 1 | class BaseFormController { 2 | constructor($scope) { 3 | this.name = 'Form Component Syntax'; 4 | this.model = {}; 5 | this.schema = { 6 | type: 'object', 7 | properties: {}, 8 | 'required': [] 9 | }; 10 | this.form = [ 11 | '*', 12 | { 13 | type: 'submit', 14 | title: 'Save' 15 | } 16 | ]; 17 | 18 | this.onSubmit = function(form) { 19 | // First we broadcast an event so all fields validate themselves 20 | $scope.$broadcast('schemaFormValidate'); 21 | 22 | // Then we check if the form is valid 23 | var msg = []; 24 | if (form.$valid) { 25 | msg = [{ 26 | 'type': 'help', 27 | 'helpvalue': '
Form submitted successfully
' 28 | }]; 29 | this.form = msg.concat(this.form); 30 | } else { 31 | msg = [{ 32 | 'type': 'help', 33 | 'helpvalue': '
Form is invalid
' 34 | }]; 35 | this.form = msg.concat(this.form); 36 | 37 | } 38 | }; 39 | } 40 | } 41 | 42 | export default BaseFormController; 43 | -------------------------------------------------------------------------------- /spec.bundle.js: -------------------------------------------------------------------------------- 1 | /* 2 | * When testing with Webpack and ES6, we have to do some 3 | * preliminary setup. Because we are writing our tests also in ES6, 4 | * we must transpile those as well, which is handled inside 5 | * `karma.conf.js` via the `karma-webpack` plugin. This is the entry 6 | * file for the Webpack tests. Similarly to how Webpack creates a 7 | * `bundle.js` file for the compressed app source files, when we 8 | * run our tests, Webpack, likewise, compiles and bundles those tests here. 9 | */ 10 | 11 | import angular from 'angular'; 12 | // Built by the core Angular team for mocking dependencies 13 | import mocks from 'angular-mocks'; 14 | 15 | // We use the context method on `require` which Webpack created 16 | // in order to signify which files we actually want to require or import. 17 | // Below, `context` will be a/an function/object with file names as keys. 18 | // Using that regex, we scan within `client/app` and target 19 | // all files ending with `.spec.js` and trace its path. 20 | // By passing in true, we permit this process to occur recursively. 21 | let context = require.context('./src', true, /\.spec\.js/); 22 | 23 | // Get all files, for each file, call the context function 24 | // that will require the file and load it here. Context will 25 | // loop and require those spec files here. 26 | context.keys().forEach(context); 27 | -------------------------------------------------------------------------------- /src/common/navbar/navbar.spec.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 makeController; 8 | 9 | beforeEach(window.module(NavbarModule.name)); // eslint-disable-line 10 | beforeEach(inject(() => { 11 | makeController = () => { 12 | return new NavbarController(); 13 | }; 14 | })); 15 | 16 | describe('Module', () => { 17 | // top-level specs: i.e., routes, injection, naming 18 | }); 19 | 20 | describe('Controller', () => { 21 | // controller specs 22 | it('has a name property', () => { // erase if removing this.name from the controller 23 | let controller = makeController(); 24 | expect(controller).to.have.property('name'); 25 | }); 26 | }); 27 | 28 | describe('Component', () => { 29 | // component/directive specs 30 | let component = NavbarComponent(); 31 | 32 | it('includes the intended template',() => { 33 | expect(component.template).to.equal(NavbarTemplate); 34 | }); 35 | 36 | it('uses `controllerAs` syntax', () => { 37 | expect(component).to.have.property('controllerAs'); 38 | }); 39 | 40 | it('invokes the right controller', () => { 41 | expect(component.controller).to.equal(NavbarController); 42 | }); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /src/common/hero/hero.spec.js: -------------------------------------------------------------------------------- 1 | import HeroModule from './hero'; 2 | import HeroController from './hero.controller'; 3 | import HeroComponent from './hero.component'; 4 | import HeroTemplate from './hero.jade'; 5 | 6 | describe('Hero', () => { 7 | let makeController; 8 | 9 | beforeEach(window.module(HeroModule.name)); // eslint-disable-line 10 | beforeEach(inject(() => { 11 | makeController = () => { 12 | return new HeroController(); 13 | }; 14 | })); 15 | 16 | describe('Module', () => { 17 | // top-level specs: i.e., routes, injection, naming 18 | }); 19 | 20 | describe('Controller', () => { 21 | // controller specs 22 | it('has a name property', () => { // erase if removing this.name from the controller 23 | let controller = makeController(); 24 | expect(controller).to.have.property('name'); 25 | }); 26 | }); 27 | 28 | describe('Template', () => { 29 | // template specs 30 | // tip: use regex to ensure correct bindings are used e.g., {{ }} 31 | it('has name in template', () => { 32 | expect(HeroTemplate).to.match(/{{\s?vm\.name\s?}}/g); 33 | }); 34 | }); 35 | 36 | describe('Component', () => { 37 | // component/directive specs 38 | let component = HeroComponent(); 39 | 40 | it('includes the intended template',() => { 41 | expect(component.template).to.equal(HeroTemplate); 42 | }); 43 | 44 | it('uses `controllerAs` syntax', () => { 45 | expect(component).to.have.property('controllerAs'); 46 | }); 47 | 48 | it('invokes the right controller', () => { 49 | expect(component.controller).to.equal(HeroController); 50 | }); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /test.robot: -------------------------------------------------------------------------------- 1 | *** Variables *** 2 | 3 | ${HOSTNAME} 127.0.0.1 4 | ${PORT} 3000 5 | ${SERVER} http://${HOSTNAME}:${PORT}/ 6 | ${BROWSER} chrome 7 | 8 | 9 | *** Settings *** 10 | 11 | Documentation Webpack Starter Angular Acceptance Tests 12 | Library Process 13 | Library DebugLibrary 14 | Library Selenium2Library timeout=10 implicit_wait=0 15 | Suite Setup Test Setup 16 | Suite Teardown Test Teardown 17 | 18 | 19 | *** Keywords *** 20 | 21 | Test Setup 22 | ${webpack-dev-server}= Start Process node_modules/webpack-dev-server/bin/webpack-dev-server.js --bail --inline --port 3000 --content-base ${CURDIR}/dist cwd=${CURDIR} shell=true 23 | Set Suite Variable ${WEBPACK-DEV-SERVER} ${webpack-dev-server} 24 | Sleep 5s 25 | Open Browser ${SERVER} ${BROWSER} 26 | Set Window Size 1280 1024 27 | 28 | Test Teardown 29 | Close Browser 30 | Log ${WEBPACK-DEV-SERVER} 31 | Terminate Process ${WEBPACK-DEV-SERVER} 32 | 33 | 34 | *** Test Cases *** 35 | 36 | Front Page 37 | Go To ${SERVER} 38 | Wait until page contains Webpack Starter 39 | Page Should Contain Webpack Starter 40 | 41 | Forms 42 | Go To ${SERVER} 43 | Wait until page contains Webpack Starter 44 | Click Link Forms 45 | Wait until page contains Forms 46 | Page should contain element name 47 | Page should contain element title 48 | Input Text name John Doe 49 | Select From List By Label title mr 50 | Input Text email john@doe.com 51 | Input Text comment Hi World 52 | Click Element css=.btn-primary 53 | Wait until page contains element css=.alert 54 | Page should contain Form submitted successfully 55 | 56 | 57 | -------------------------------------------------------------------------------- /config/webpack.common.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | 5 | 6 | module.exports = { 7 | entry: './src/app.js', 8 | output: { 9 | path: path.join(__dirname, '..', 'dist'), 10 | filename: 'bundle.js' 11 | }, 12 | plugins: [ 13 | new HtmlWebpackPlugin({ 14 | title: 'Webpack Starter Angular - kitconcept', 15 | template: 'src/index.html', 16 | minify: { 17 | collapseWhitespace: true, 18 | removeComments: true, 19 | removeRedundantAttributes: true, 20 | removeScriptTypeAttributes: true, 21 | removeStyleLinkTypeAttributes: true 22 | } 23 | }) 24 | ], 25 | module: { 26 | loaders: [ 27 | { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ }, 28 | { test: /\.css$/, loader: 'style-loader!css-loader' }, 29 | { test: /\.scss$/, loaders: ['style', 'css?sourceMap', 'sass?sourceMap']}, 30 | { test: /\.html$/, loader: 'raw' }, 31 | { test: /\.jade$/, loader: 'jade-loader' }, 32 | // inline base64 URLs for <=8k images, direct URLs for the rest 33 | { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'}, 34 | // helps to load bootstrap's css. 35 | { test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, 36 | loader: 'url?limit=10000&minetype=application/font-woff' }, 37 | { test: /\.woff2$/, 38 | loader: 'url?limit=10000&minetype=application/font-woff' }, 39 | { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, 40 | loader: 'url?limit=10000&minetype=application/octet-stream' }, 41 | { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, 42 | loader: 'file' }, 43 | { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, 44 | loader: 'url?limit=10000&minetype=image/svg+xml' } 45 | ] 46 | }, 47 | devtool: 'eval-source-map' 48 | }; 49 | -------------------------------------------------------------------------------- /src/components/forms/forms.spec.js: -------------------------------------------------------------------------------- 1 | import FormsModule from './forms'; 2 | import FormsController from './forms.controller'; 3 | import FormsComponent from './forms.component'; 4 | import FormsTemplate from './forms.html'; 5 | 6 | describe('Forms', () => { 7 | let makeController; 8 | 9 | beforeEach(window.module(FormsModule.name)); // eslint-disable-line 10 | beforeEach(inject(() => { 11 | makeController = () => { 12 | return new FormsController(); 13 | }; 14 | })); 15 | 16 | describe('Module', () => { 17 | // top-level specs: i.e., routes, injection, naming 18 | }); 19 | 20 | describe('Controller', () => { 21 | // controller specs 22 | it('has a name/from/schema/model property', () => { // erase if removing this.name from the controller 23 | let controller = makeController(); 24 | expect(controller).to.have.property('name'); 25 | expect(controller).to.have.property('form'); 26 | expect(controller).to.have.property('schema'); 27 | expect(controller).to.have.property('model'); 28 | }); 29 | }); 30 | 31 | describe('Template', () => { 32 | // template specs 33 | // tip: use regex to ensure correct bindings are used e.g., {{ }} 34 | it('has schema/form/model in template', () => { 35 | expect(FormsTemplate).to.match(/\s?vm\.schema\s?/g); 36 | expect(FormsTemplate).to.match(/\s?vm\.form\s?/g); 37 | expect(FormsTemplate).to.match(/\s?vm\.model\s?/g); 38 | }); 39 | }); 40 | 41 | 42 | describe('Component', () => { 43 | // component/directive specs 44 | let component = FormsComponent; 45 | 46 | it('includes the intended template',() => { 47 | expect(component.template).to.equal(FormsTemplate); 48 | }); 49 | 50 | it('uses `controllerAs` syntax', () => { 51 | expect(component).to.have.property('controllerAs'); 52 | }); 53 | 54 | it('invokes the right controller', () => { 55 | expect(component.controller).to.equal(FormsController); 56 | }); 57 | }); 58 | 59 | }); 60 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function (config) { 2 | 3 | var configuration = { 4 | // base path used to resolve all patterns 5 | basePath: '', 6 | 7 | // frameworks to use 8 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 9 | frameworks: ['mocha', 'chai'], 10 | 11 | // list of files/patterns to load in the browser 12 | files: [{ pattern: 'spec.bundle.js', watched: false }], 13 | 14 | // files to exclude 15 | exclude: [], 16 | 17 | // preprocess matching files before serving them to the browser 18 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 19 | preprocessors: { 'spec.bundle.js': ['webpack', 'sourcemap'] }, 20 | 21 | webpack: { 22 | devtool: 'inline-source-map', 23 | module: { 24 | loaders: [ 25 | { test: /\.js/, exclude: [/app\/lib/, /node_modules/], loader: 'babel' }, 26 | { test: /\.html/, loader: 'raw' }, 27 | { test: /\.jade$/, loader: 'jade-loader' }, 28 | { test: /\.styl$/, loader: 'style!css!stylus' }, 29 | { test: /\.css$/, loader: 'style!css' } 30 | ] 31 | } 32 | }, 33 | 34 | webpackServer: { 35 | noInfo: true // prevent console spamming when running in Karma! 36 | }, 37 | 38 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 39 | reporters: ['mocha'], 40 | 41 | // web server port 42 | port: 9876, 43 | 44 | // enable colors in the output 45 | colors: true, 46 | 47 | // level of logging 48 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 49 | logLevel: config.LOG_INFO, 50 | 51 | // toggle whether to watch files and rerun tests upon incurring changes 52 | autoWatch: true, 53 | 54 | // start these browsers 55 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 56 | browsers: ['Chrome'], 57 | 58 | customLaunchers: { 59 | Chrome_travis_ci: { 60 | base: 'Chrome', 61 | flags: ['--no-sandbox'] 62 | } 63 | }, 64 | 65 | // if true, Karma runs tests once and exits 66 | singleRun: true 67 | }; 68 | 69 | if(process.env.TRAVIS){ 70 | configuration.browsers = ['Chrome_travis_ci']; 71 | // configuration.reporters = configuration.reporters.concat(['coverage', 'coveralls']); 72 | // configuration.coverageReporter = { 73 | // type : 'lcovonly', 74 | // dir : 'coverage/' 75 | // }; 76 | } 77 | 78 | config.set(configuration); 79 | 80 | }; 81 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-starter-angular", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "keywords": [ 7 | "bundle", 8 | "bundling", 9 | "webpack", 10 | "npm", 11 | "angular" 12 | ], 13 | "author": "Timo Stollenwerk", 14 | "license": "MIT", 15 | "babel": { 16 | "presets": [ 17 | "es2015" 18 | ] 19 | }, 20 | "devDependencies": { 21 | "angular-mocks": "1.5.8", 22 | "babel-core": "6.3.26", 23 | "babel-loader": "6.1.0", 24 | "babel-polyfill": "6.3.14", 25 | "babel-preset-es2015": "6.1.18", 26 | "chai": "^4.1.2", 27 | "css-loader": "~0.21.0", 28 | "eslint": "1.7.3", 29 | "eslint-config-angular": "0.4.0", 30 | "eslint-plugin-angular": "0.12.0", 31 | "file-loader": "~0.8.4", 32 | "html-webpack-plugin": "~1.7.0", 33 | "jade": "~1.11.0", 34 | "jade-loader": "~0.7.1", 35 | "karma": "1.2.0", 36 | "karma-chai": "0.1.0", 37 | "karma-chrome-launcher": "2.0.0", 38 | "karma-mocha": "1.1.1", 39 | "karma-mocha-reporter": "2.1.0", 40 | "karma-sourcemap-loader": "0.3.7", 41 | "karma-webpack": "1.8.0", 42 | "mocha": "~2.3.0", 43 | "node-sass": "~3.3.3", 44 | "raw-loader": "~0.5.1", 45 | "rimraf": "~2.5.2", 46 | "sass-loader": "~3.0.0", 47 | "script-loader": "~0.6.1", 48 | "style-loader": "~0.13.0", 49 | "url-loader": "~0.5.6", 50 | "webpack": "~1.12.14", 51 | "webpack-dev-server": "1.12.1", 52 | "webpack-md5-hash": "0.0.5", 53 | "webpack-merge": "~0.14.0" 54 | }, 55 | "dependencies": { 56 | "angular": "1.5.8", 57 | "angular-sanitize": "1.5.8", 58 | "angular-schema-form": "0.8.13", 59 | "angular-schema-form-bootstrap": "0.2.0", 60 | "angular-ui-bootstrap": "1.1.2", 61 | "angular-ui-router": "0.2.18", 62 | "bootstrap": "3.3.7", 63 | "jquery": "2.2.4", 64 | "lodash": "4.6.1", 65 | "objectpath": "1.2.1", 66 | "tv4": "1.2.7" 67 | }, 68 | "scripts": { 69 | "analyze": "webpack --json | webpack-bundle-size-analyzer", 70 | "clean": "npm cache clean && rimraf -- node_modules doc typings coverage dist", 71 | "build": "export NODE_ENV=dev && webpack", 72 | "build:production": "export NODE_ENV=prod && webpack -p", 73 | "lint": "eslint src/ || true", 74 | "lint:travis": "eslint src/", 75 | "start": "node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --port 3000 --content-base dist/", 76 | "test": "karma start", 77 | "test:debug": "karma start karma.conf.js --log-level=debug --single-run=False", 78 | "test:watch": "karma start karma.conf.js --auto-watch=true --single-run=False" 79 | }, 80 | "repository": "git@github.com:kitconcept/webpack-starter-angular.git" 81 | } 82 | -------------------------------------------------------------------------------- /config/webpack.prod.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpackMerge = require('webpack-merge'); 3 | const commonConfig = require('./webpack.common.js'); 4 | 5 | // webpack plugins 6 | const WebpackMd5Hash = require('webpack-md5-hash'); 7 | const DedupePlugin = require('webpack/lib/optimize/DedupePlugin'); 8 | const UglifyJsPlugin = require('webpack/lib/optimize/UglifyJsPlugin'); 9 | 10 | 11 | module.exports = webpackMerge(commonConfig, { 12 | 13 | /** 14 | * Disable debug mode for production. 15 | * 16 | * See: http://webpack.github.io/docs/configuration.html#debug 17 | */ 18 | debug: false, 19 | 20 | /** 21 | * Developer tool to enhance debugging. 22 | * 23 | * The 'source-map' settings is meant to be used in production only. It 24 | * splits the source map in a separate file and it is slow to compute. 25 | * 26 | * See: http://webpack.github.io/docs/configuration.html#devtool 27 | * See: https://github.com/webpack/docs/wiki/build-performance#sourcemaps 28 | */ 29 | devtool: 'source-map', 30 | 31 | 32 | /** 33 | * Options affecting the output of the compilation. 34 | * 35 | * See: http://webpack.github.io/docs/configuration.html#output 36 | */ 37 | output: { 38 | 39 | /** 40 | * The output directory as absolute path (required). 41 | * 42 | * See: http://webpack.github.io/docs/configuration.html#output-path 43 | */ 44 | path: path.join(__dirname, '..', 'dist'), 45 | 46 | /** 47 | * Specifies the name of each output file on disk. 48 | * IMPORTANT: You must not specify an absolute path here! 49 | * 50 | * See: http://webpack.github.io/docs/configuration.html#output-filename 51 | */ 52 | filename: '[name].[chunkhash].bundle.js', 53 | 54 | /** 55 | * The filename of the SourceMaps for the JavaScript files. 56 | * They are inside the output.path directory. 57 | * 58 | * See: http://webpack.github.io/docs/configuration.html#output-sourcemapfilename 59 | */ 60 | sourceMapFilename: '[name].[chunkhash].bundle.map', 61 | 62 | /** 63 | * The filename of non-entry chunks as relative path 64 | * inside the output.path directory. 65 | * 66 | * See: http://webpack.github.io/docs/configuration.html#output-chunkfilename 67 | */ 68 | chunkFilename: '[id].[chunkhash].chunk.js' 69 | 70 | }, 71 | 72 | /** 73 | * Add additional plugins to the compiler. 74 | * 75 | * See: http://webpack.github.io/docs/configuration.html#plugins 76 | */ 77 | plugins: [ 78 | 79 | /** 80 | * Plugin: WebpackMd5Hash 81 | * Description: Plugin to replace a standard webpack chunkhash with md5. 82 | * 83 | * See: https://www.npmjs.com/package/webpack-md5-hash 84 | */ 85 | new WebpackMd5Hash(), 86 | 87 | /** 88 | * Plugin: DedupePlugin 89 | * Description: Prevents the inclusion of duplicate code into your bundle 90 | * and instead applies a copy of the function at runtime. 91 | * 92 | * See: https://webpack.github.io/docs/list-of-plugins.html#defineplugin 93 | * See: https://github.com/webpack/docs/wiki/optimization#deduplication 94 | */ 95 | new DedupePlugin(), 96 | 97 | /** 98 | * Plugin: UglifyJsPlugin 99 | * Description: Minimize all JavaScript output of chunks. 100 | * Loaders are switched into minimizing mode. 101 | * 102 | * See: https://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin 103 | */ 104 | // NOTE: To debug prod builds uncomment //debug lines and comment //prod lines 105 | // new UglifyJsPlugin({ 106 | // // beautify: true, //debug 107 | // // mangle: false, //debug 108 | // // dead_code: false, //debug 109 | // // unused: false, //debug 110 | // // deadCode: false, //debug 111 | // // compress: { 112 | // // screw_ie8: true, 113 | // // keep_fnames: true, 114 | // // drop_debugger: false, 115 | // // dead_code: false, 116 | // // unused: false 117 | // // }, // debug 118 | // // comments: true, //debug 119 | 120 | 121 | // // beautify: false, //prod 122 | // // mangle: { screw_ie8 : true }, //prod 123 | // // compress: { screw_ie8: true }, //prod 124 | // // comments: false //prod 125 | // }) 126 | 127 | ] 128 | 129 | }); 130 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://travis-ci.org/kitconcept/webpack-starter-angular.svg?branch=master 2 | :target: https://travis-ci.org/kitconcept/webpack-starter-angular 3 | 4 | 5 | Webpack Starter Angular 6 | ======================= 7 | 8 | Stack 9 | ----- 10 | 11 | - NPM 12 | - Webpack 13 | - Angular 1.5 14 | - Angular UI Router (with self-registering components) 15 | - ES 6 / Babel (Module Loading) 16 | - SASS 17 | - Jade 18 | - Tests 19 | - Karma (Test Runner, http://karma-runner.github.io/) 20 | - Mocha (Test Framework, http://mochajs.org/) 21 | - Chai (BDD/TSS assertion library, http://chaijs.com/) 22 | 23 | 24 | Requirements 25 | ------------ 26 | 27 | ToDo: 28 | 29 | - SASS variables 30 | - Angular New Router -> http://www.heise.de/developer/artikel/AngularJS-1-x-und-2-0-mit-dem-Component-Router-parallel-betreiben-2679282.html 31 | 32 | Done: 33 | 34 | - DONE: ES 6 with Angular 1.4 35 | - DONE: Bundling 36 | - DONE: Delivering static files separately -> Possible via https://webpack.github.io/docs/stylesheets.html#separate-css-bundle 37 | - DONE: Splitting large files -> Possible via https://webpack.github.io/docs/code-splitting.html 38 | - DONE: SASS support 39 | - DONE: Angular UI Router 40 | - DONE: Self-registering components 41 | - DONE: Jade support 42 | 43 | Planed: 44 | 45 | - ES7 Decorators (@Component()) 46 | - Typescript? 47 | 48 | 49 | Prerequisits 50 | ------------ 51 | 52 | Install a few dependencies globally:: 53 | 54 | $ npm install -g babel 55 | $ npm install -g webpack 56 | $ npm install -g webpack-dev-server 57 | $ npm install -g eslint 58 | 59 | 60 | Development 61 | ----------- 62 | 63 | Create a JS bundle with Webpack:: 64 | 65 | $ npm run build 66 | 67 | Start the Webpack development server on 'localhost:3000':: 68 | 69 | $ npm run start 70 | 71 | Run tests:: 72 | 73 | $ npm run test 74 | 75 | Linting:: 76 | 77 | $ npm run lint 78 | 79 | 80 | HTML Webpack Plugin 81 | ------------------- 82 | 83 | Installation:: 84 | 85 | $ npm install html-webpack-plugin --save-dev 86 | 87 | webpack.config.js:: 88 | 89 | var HtmlWebpackPlugin = require('html-webpack-plugin') 90 | 91 | module.exports = { 92 | ... 93 | plugins: [ 94 | new HtmlWebpackPlugin({ 95 | title: 'Website Starter', 96 | template: 'src/index.html', 97 | minify: { 98 | collapseWhitespace: true, 99 | removeComments: true, 100 | removeRedundantAttributes: true, 101 | removeScriptTypeAttributes: true, 102 | removeStyleLinkTypeAttributes: true 103 | } 104 | }) 105 | ], 106 | } 107 | 108 | 109 | ES 6 Imports 110 | ------------ 111 | 112 | Default import:: 113 | 114 | import localName from 'src/my_lib'; 115 | 116 | Namespace import: imports the module as an object (with one property per named export):: 117 | 118 | import * as my_lib from 'src/my_lib'; 119 | 120 | Named imports:: 121 | 122 | import { name1, name2 } from 'src/my_lib'; 123 | 124 | Renaming named imports:: 125 | 126 | // Renaming: import `name1` as `localName1` 127 | import { name1 as localName1, name2 } from 'src/my_lib'; 128 | Empty import: only loads the module, doesn’t import anything. The first such import in a program executes the body of the module. 129 | import 'src/my_lib'; 130 | 131 | Source: http://exploringjs.com/es6/ch_modules.html 132 | 133 | Imports for broken modules:: 134 | 135 | require('script!objectpath/lib/ObjectPath'); 136 | 137 | Source: https://webpack.github.io/docs/shimming-modules.html 138 | 139 | 140 | SASS Loader 141 | ----------- 142 | 143 | Installation:: 144 | 145 | $ npm install sass-loader --save-dev 146 | 147 | Webpack Configuration (webpack.config.js):: 148 | 149 | module.exports = { 150 | ... 151 | module: { 152 | loaders: [ 153 | ... 154 | { test: /\.scss$/, loaders: ["style", "css?sourceMap", "sass?sourceMap"]}, 155 | ] 156 | }, 157 | devtool: 'source-map' 158 | } 159 | 160 | Javascript:: 161 | 162 | import Styles from './styles.scss'; 163 | 164 | SASS (styles.scss):: 165 | 166 | body { 167 | padding-top: 80px; 168 | } 169 | 170 | 171 | Jade Loader 172 | ----------- 173 | 174 | Installation:: 175 | 176 | $ npm install jade-loader --save-dev 177 | 178 | Webpack Configuration (webpack.config.js):: 179 | 180 | module.exports = { 181 | ... 182 | module: { 183 | loaders: [ 184 | ... 185 | { test: /\.jade$/, loader: 'jade-loader' }, 186 | ] 187 | } 188 | } 189 | 190 | Javascript:: 191 | 192 | import template from './hero.jade'; 193 | 194 | Jade (hero.jade):: 195 | 196 | div.jumbotron 197 | h1 Angular, ES6, Webpack Starter! 198 | h3 You can find my template inside {{ vm.name }}.html 199 | 200 | 201 | Angular Schema Form 202 | ------------------- 203 | 204 | Installation:: 205 | 206 | $ npm install angular-schema-form --save 207 | $ npm install objectpath --save 208 | $ npm install tv4 --save 209 | $ npm install angular-sanitize --save 210 | 211 | Javascript:: 212 | 213 | import 'angular-sanitize'; 214 | require('script!tv4/tv4.js'); 215 | require('script!objectpath/lib/ObjectPath'); 216 | require('script!angular-schema-form/dist/schema-form'); 217 | require('script!angular-schema-form/dist/bootstrap-decorator'); 218 | 219 | let formsModule = angular.module('forms', [ 220 | uiRouter, 221 | 'schemaForm' 222 | ]) 223 | 224 | ... 225 | 226 | Controller:: 227 | 228 | class FormsController { 229 | constructor() { 230 | this.name = 'Contact Us'; 231 | this.model = {}; 232 | this.schema = { 233 | type: 'object', 234 | properties: { 235 | name: { type: 'string', minLength: 2, title: 'Name', description: 'Name or alias' }, 236 | title: { 237 | type: 'string', 238 | enum: ['dr','jr','sir','mrs','mr','NaN','dj'] 239 | } 240 | }, 241 | 'required': [ 242 | 'name' 243 | ] 244 | }; 245 | this.form = [ 246 | '*', 247 | { 248 | type: 'submit', 249 | title: 'Save' 250 | } 251 | ]; 252 | } 253 | } 254 | 255 | export default FormsController; 256 | 257 | 258 | Service 259 | ------- 260 | 261 | ... 262 | 263 | Travis CI 264 | --------- 265 | 266 | - Enable Travis for repository 267 | 268 | .travis.yml:: 269 | 270 | language: node_js 271 | node_js: 272 | - 4.2.1 273 | cache: 274 | directories: 275 | - node_modules 276 | before_install: 277 | - export CHROME_BIN=chromium-browser 278 | - export DISPLAY=:99.0 279 | - sh -e /etc/init.d/xvfb start 280 | install: 281 | - npm install -g babel 282 | - npm install -g webpack 283 | - npm install -g webpack-dev-server 284 | - npm install -g eslint 285 | - npm install 286 | script: 287 | - npm run test 288 | notifications: 289 | email: 290 | - stollenwerk@kitconcept.com 291 | 292 | webpack.config.js:: 293 | 294 | ... 295 | 296 | ESLint 297 | ------ 298 | 299 | Installation:: 300 | 301 | $ npm install eslint -g 302 | 303 | Sublime Text 3 Installation: 304 | 305 | https://github.com/roadhump/SublimeLinter-eslint 306 | 307 | Sources 308 | ------- 309 | 310 | - Webpack: https://github.com/faassen/bundle_example 311 | - Angular: https://github.com/angular-class/NG6-starter 312 | - Angular: http://angular-tips.com/blog/2015/06/using-angular-1-dot-x-with-es6-and-webpack/ 313 | - Webpack and Babel6: https://github.com/rauschma/webpack-babel-demo 314 | 315 | --------------------------------------------------------------------------------