├── src ├── app │ ├── shared │ │ ├── constant │ │ │ ├── .gitignore │ │ │ ├── main.ts │ │ │ ├── index.ts │ │ │ └── env.model.ts │ │ ├── index.ts │ │ └── navbar │ │ │ ├── index.ts │ │ │ ├── navbar.component.ts │ │ │ ├── navbar.module.ts │ │ │ └── navbar.html │ ├── home │ │ ├── home.css │ │ ├── index.ts │ │ ├── home.routes.ts │ │ ├── home.component.ts │ │ ├── home.module.ts │ │ └── home.html │ ├── app.providers.ts │ ├── bundle.ts │ ├── app.html │ ├── todolist │ │ ├── index.ts │ │ ├── todolist.routes.ts │ │ ├── todo.model.ts │ │ ├── completed-filter.pipe.ts │ │ ├── todolist.module.ts │ │ ├── completed-filter.pipe.spec.ts │ │ ├── todolist.component.ts │ │ ├── todolist.html │ │ └── todolist.component.spec.ts │ ├── main.ts │ ├── app.component.ts │ ├── app.routing.ts │ ├── app.module.ts │ └── app.component.spec.ts ├── favicon.ico ├── assets │ ├── styles │ │ ├── fonts.scss │ │ ├── overrides.scss │ │ ├── variables.scss │ │ ├── _bootstrap.variables.scss │ │ ├── bootstrap.scss │ │ ├── main.scss │ │ ├── functions.scss │ │ ├── module.scss │ │ └── mixins.scss │ ├── images │ │ └── ng2.jpg │ └── fonts │ │ └── Roboto │ │ ├── Roboto-Regular-webfont.eot │ │ ├── Roboto-Regular-webfont.ttf │ │ └── Roboto-Regular-webfont.woff ├── manual_typings │ └── jasmine.d.ts ├── test │ └── test-helpers │ │ ├── global │ │ ├── env.ts │ │ └── matchers.ts │ │ └── setup.ts ├── index.html └── systemjs.conf.js ├── env.example.json ├── .travis.yml ├── config ├── gulp │ ├── utils │ │ ├── env-vars.js │ │ ├── env.js │ │ └── dashboard.js │ ├── tasks │ │ ├── sass.js │ │ ├── clean.js │ │ ├── serve.js │ │ ├── component.js │ │ ├── typescript.js │ │ ├── build.js │ │ └── test.js │ └── config.js ├── env │ └── env.ts └── test │ ├── protractor.conf.js │ ├── karma-test-shim.js │ └── karma.conf.js ├── .gitignore ├── gulpfile.js ├── tsconfig.json ├── e2e └── home │ └── home.spec.ts ├── .github └── ISSUE_TEMPLATE.md ├── appveyor.yml ├── LICENSE ├── tslint.json ├── package.json └── README.md /src/app/shared/constant/.gitignore: -------------------------------------------------------------------------------- 1 | env.ts -------------------------------------------------------------------------------- /env.example.json: -------------------------------------------------------------------------------- 1 | { 2 | "APP_URL": "localhost" 3 | } -------------------------------------------------------------------------------- /src/app/home/home.css: -------------------------------------------------------------------------------- 1 | h4 { 2 | color: lightseagreen; 3 | } -------------------------------------------------------------------------------- /src/app/app.providers.ts: -------------------------------------------------------------------------------- 1 | export const APP_PROVIDERS = [ 2 | ]; 3 | -------------------------------------------------------------------------------- /src/app/bundle.ts: -------------------------------------------------------------------------------- 1 | // This file is for production only, please leave it blank 2 | -------------------------------------------------------------------------------- /src/app/shared/index.ts: -------------------------------------------------------------------------------- 1 | export * from './constant/index'; 2 | export * from './navbar/index'; 3 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonybudianto/angular-starter/HEAD/src/favicon.ico -------------------------------------------------------------------------------- /src/app/shared/navbar/index.ts: -------------------------------------------------------------------------------- 1 | export * from './navbar.component'; 2 | export * from './navbar.module'; 3 | -------------------------------------------------------------------------------- /src/assets/styles/fonts.scss: -------------------------------------------------------------------------------- 1 | @include font-face(Roboto, 'Roboto/Roboto-Regular-webfont', 'eot svg ttf woff'); 2 | -------------------------------------------------------------------------------- /src/assets/styles/overrides.scss: -------------------------------------------------------------------------------- 1 | .panel-title { 2 | font-weight: bold; 3 | color: color(primary); 4 | } 5 | -------------------------------------------------------------------------------- /src/assets/styles/variables.scss: -------------------------------------------------------------------------------- 1 | $font-stack: 'Roboto'; 2 | $colors: ( 3 | primary: cornflowerblue 4 | ); 5 | -------------------------------------------------------------------------------- /src/assets/images/ng2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonybudianto/angular-starter/HEAD/src/assets/images/ng2.jpg -------------------------------------------------------------------------------- /src/app/shared/constant/main.ts: -------------------------------------------------------------------------------- 1 | export const MAIN = { 2 | APP: { 3 | BRAND: 'Angular Starter' 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /src/app/home/index.ts: -------------------------------------------------------------------------------- 1 | export * from './home.component'; 2 | export * from './home.routes'; 3 | export * from './home.module'; 4 | -------------------------------------------------------------------------------- /src/assets/styles/_bootstrap.variables.scss: -------------------------------------------------------------------------------- 1 | /* set variables for bootstrap 2 | eg. 3 | 4 | $font-size-base: 13px; 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/assets/styles/bootstrap.scss: -------------------------------------------------------------------------------- 1 | @import 'bootstrap.variables'; 2 | @import '../../../node_modules/bootstrap-sass/assets/stylesheets/bootstrap'; -------------------------------------------------------------------------------- /src/manual_typings/jasmine.d.ts: -------------------------------------------------------------------------------- 1 | declare module jasmine { 2 | interface Matchers { 3 | toContainText(text: string): boolean; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "7.0" 4 | - "6.9" 5 | script: npm run build 6 | after_script: 7 | - npm install -g codecov 8 | - codecov 9 | -------------------------------------------------------------------------------- /config/gulp/utils/env-vars.js: -------------------------------------------------------------------------------- 1 | var ENV_VARS; 2 | 3 | try { 4 | ENV_VARS = require('../../../env.json'); 5 | } catch(e) {} 6 | 7 | module.exports = ENV_VARS; 8 | -------------------------------------------------------------------------------- /src/app/app.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | -------------------------------------------------------------------------------- /src/assets/fonts/Roboto/Roboto-Regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonybudianto/angular-starter/HEAD/src/assets/fonts/Roboto/Roboto-Regular-webfont.eot -------------------------------------------------------------------------------- /src/assets/fonts/Roboto/Roboto-Regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonybudianto/angular-starter/HEAD/src/assets/fonts/Roboto/Roboto-Regular-webfont.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Roboto/Roboto-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonybudianto/angular-starter/HEAD/src/assets/fonts/Roboto/Roboto-Regular-webfont.woff -------------------------------------------------------------------------------- /src/app/shared/constant/index.ts: -------------------------------------------------------------------------------- 1 | import { MAIN } from './main'; 2 | import { ENV } from './env'; 3 | 4 | export const CONSTANTS = { 5 | MAIN, 6 | ENV 7 | }; 8 | -------------------------------------------------------------------------------- /src/test/test-helpers/global/env.ts: -------------------------------------------------------------------------------- 1 | // This lets systemjs.conf.js knows how to load the module during testing 2 | ((global) => { 3 | global.ENV = 'testing'; 4 | })(this); 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | env.json 2 | .idea/ 3 | .vscode 4 | /node_modules 5 | /bower_components 6 | /build 7 | /report 8 | /typings 9 | /src/tmp 10 | .DS_Store 11 | /src/assets/styles/*.css -------------------------------------------------------------------------------- /src/assets/styles/main.scss: -------------------------------------------------------------------------------- 1 | @import 'bootstrap'; 2 | @import 'variables'; 3 | @import 'mixins'; 4 | @import 'functions'; 5 | @import 'fonts'; 6 | @import 'overrides'; 7 | @import 'module'; 8 | -------------------------------------------------------------------------------- /src/assets/styles/functions.scss: -------------------------------------------------------------------------------- 1 | @function color($key) { 2 | @if map-has-key($colors, $key) { 3 | @return map-get($colors, $key); 4 | } 5 | 6 | @warn "Unknown `#{$key}` in $colors."; 7 | @return null; 8 | } 9 | -------------------------------------------------------------------------------- /src/app/todolist/index.ts: -------------------------------------------------------------------------------- 1 | export * from './todo.model'; 2 | export * from './todolist.component'; 3 | export * from './todolist.routes'; 4 | export * from './completed-filter.pipe'; 5 | export * from './todolist.module'; 6 | -------------------------------------------------------------------------------- /src/app/home/home.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | import { HomeComponent } from './home.component'; 4 | 5 | export const HomeRoutes: Routes = [ 6 | { path: '', component: HomeComponent } 7 | ]; 8 | -------------------------------------------------------------------------------- /src/app/todolist/todolist.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | import { TodolistComponent } from './todolist.component'; 4 | 5 | export const TodolistRoutes: Routes = [ 6 | { path: 'todolist', component: TodolistComponent } 7 | ]; 8 | -------------------------------------------------------------------------------- /src/assets/styles/module.scss: -------------------------------------------------------------------------------- 1 | .center-fix { 2 | position: fixed; 3 | top: 50%; 4 | left: 50%; 5 | transform: translate(-50%, -50%); 6 | text-align: center; 7 | } 8 | 9 | .main-spinner { 10 | font-size: 3em; 11 | color: color(primary); 12 | } 13 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | requireDir = require('require-dir'), 3 | tasks = requireDir('./config/gulp/tasks'), 4 | dashboard = require('./config/gulp/utils/dashboard'); 5 | 6 | dashboard.show(); 7 | 8 | /* Default task */ 9 | gulp.task('default', ['serve-dev']); 10 | -------------------------------------------------------------------------------- /src/app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | moduleId: module.id, 5 | selector: 'as-home', 6 | templateUrl: 'home.html', 7 | styleUrls: [ 8 | 'home.css' 9 | ] 10 | }) 11 | export class HomeComponent { 12 | } 13 | -------------------------------------------------------------------------------- /src/app/home/home.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { HomeComponent } from './index'; 3 | 4 | @NgModule({ 5 | declarations: [ 6 | HomeComponent 7 | ], 8 | exports: [ 9 | HomeComponent 10 | ] 11 | }) 12 | export class HomeModule { 13 | } 14 | -------------------------------------------------------------------------------- /config/gulp/utils/env.js: -------------------------------------------------------------------------------- 1 | var argv = require('yargs').argv; 2 | 3 | var ENVS = { 4 | DEV: 'development', 5 | PROD: 'production', 6 | TEST: 'testing' 7 | }; 8 | 9 | var ENV = argv.env || process.env.NODE_ENV || ENVS.DEV; 10 | 11 | module.exports = { 12 | ENV: ENV, 13 | ENVS: ENVS 14 | }; 15 | -------------------------------------------------------------------------------- /src/test/test-helpers/setup.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { 3 | BrowserDynamicTestingModule, 4 | platformBrowserDynamicTesting 5 | } from '@angular/platform-browser-dynamic/testing'; 6 | 7 | TestBed.initTestEnvironment( 8 | BrowserDynamicTestingModule, 9 | platformBrowserDynamicTesting() 10 | ); 11 | -------------------------------------------------------------------------------- /src/app/home/home.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Home

4 |
5 |
6 |

Welcome to Angular Starter!

7 | Ng2 8 |
9 |
-------------------------------------------------------------------------------- /src/app/shared/constant/env.model.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Only valid JSON data types can be used for types 3 | * 4 | * Make sure the keys in `env.model.ts` exist in `env.json` 5 | * otherwise it'll throw message like this 6 | * Property '' is missing in type '{}' 7 | * 8 | */ 9 | 10 | export interface AppEnv { 11 | APP_URL?: string; 12 | } 13 | -------------------------------------------------------------------------------- /src/app/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app.module'; 5 | 6 | declare var ENV: string; 7 | 8 | if (ENV === 'production') { 9 | enableProdMode(); 10 | } 11 | 12 | platformBrowserDynamic().bootstrapModule(AppModule); 13 | -------------------------------------------------------------------------------- /src/app/shared/navbar/navbar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, ChangeDetectionStrategy } from '@angular/core'; 2 | 3 | @Component({ 4 | moduleId: module.id, 5 | selector: 'as-navbar', 6 | templateUrl: 'navbar.html', 7 | changeDetection: ChangeDetectionStrategy.OnPush 8 | }) 9 | export class NavbarComponent { 10 | @Input() brand: string; 11 | } 12 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | import { CONSTANTS } from './shared'; 4 | 5 | @Component({ 6 | moduleId: module.id, 7 | selector: 'as-main-app', 8 | templateUrl: 'app.html' 9 | }) 10 | export class AppComponent { 11 | public appBrand: string; 12 | 13 | constructor() { 14 | this.appBrand = CONSTANTS.MAIN.APP.BRAND; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "skipLibCheck": true, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "removeComments": false, 11 | "noImplicitAny": false 12 | }, 13 | "exclude": [ 14 | "node_modules" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /e2e/home/home.spec.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | describe('Home page', () => { 4 | beforeAll(done => { 5 | browser.get('/') 6 | .then(done); 7 | }); 8 | 9 | it('should have image', () => { 10 | browser.sleep(1000); 11 | let ng2Img = element(by.css('img')); 12 | expect(ng2Img.isDisplayed()).toBeTruthy(); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/app/app.routing.ts: -------------------------------------------------------------------------------- 1 | import { Routes, RouterModule } from '@angular/router'; 2 | 3 | import { HomeRoutes } from './home/index'; 4 | import { TodolistRoutes } from './todolist/index'; 5 | 6 | const appRoutes: Routes = [ 7 | ...HomeRoutes, 8 | ...TodolistRoutes 9 | ]; 10 | 11 | export const appRoutingProviders: any[] = [ 12 | 13 | ]; 14 | 15 | export const routing = RouterModule.forRoot(appRoutes); 16 | -------------------------------------------------------------------------------- /src/app/shared/navbar/navbar.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | 4 | import { NavbarComponent } from './index'; 5 | 6 | @NgModule({ 7 | declarations: [ 8 | NavbarComponent 9 | ], 10 | imports: [ 11 | RouterModule 12 | ], 13 | exports: [ 14 | NavbarComponent 15 | ] 16 | }) 17 | export class NavbarModule { 18 | } 19 | -------------------------------------------------------------------------------- /src/app/todolist/todo.model.ts: -------------------------------------------------------------------------------- 1 | export class Todo { 2 | public name: string; 3 | public done: boolean; 4 | 5 | static clone(todo: Todo): Todo { 6 | return new Todo(todo.name, todo.done); 7 | } 8 | 9 | constructor(name: string, done = false) { 10 | this.name = name; 11 | this.done = done; 12 | } 13 | 14 | clear() { 15 | this.name = ''; 16 | this.done = false; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /config/gulp/tasks/sass.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var sass = require('gulp-sass'); 3 | var config = require('../config')(); 4 | 5 | gulp.task('sass', function () { 6 | return gulp.src(config.assetsPath.styles + 'main.scss') 7 | .pipe(sass().on('error', sass.logError)) 8 | .pipe(gulp.dest(config.assetsPath.styles)); 9 | }); 10 | 11 | gulp.task('watch-sass', function () { 12 | gulp.watch(config.assetsPath.styles + '**/*.scss', ['sass']); 13 | }); 14 | -------------------------------------------------------------------------------- /src/app/todolist/completed-filter.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { filter } from 'lodash'; 3 | 4 | import { Todo } from './todo.model'; 5 | 6 | @Pipe({ 7 | name: 'asCompletedFilter' 8 | }) 9 | export class CompletedFilterPipe implements PipeTransform { 10 | transform(todos: Todo[], done): Todo[] { 11 | if (done) { 12 | return todos; 13 | } 14 | 15 | return filter(todos, {done}); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /config/env/env.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * THIS FILE IS GENERATED by `gulp env` command from `env.json` 3 | * Generated on <%= new Date() %> 4 | * 5 | * Make sure the keys in `env.model.ts` exist in `env.json` 6 | * otherwise it'll throw message like this 7 | * Property '' is missing in type '{}' 8 | * 9 | * Feel free to modify for direct updates in development 10 | */ 11 | 12 | import { AppEnv } from './env.model'; 13 | 14 | export const ENV: AppEnv = {<% _.forEach(env, function(v, k) { %> 15 | <%= k %>: <%= _.isString(v) ? "\'"+v+"\'" : v %>,<% }) %> 16 | }; 17 | -------------------------------------------------------------------------------- /src/test/test-helpers/global/matchers.ts: -------------------------------------------------------------------------------- 1 | beforeEach(() => { 2 | jasmine.addMatchers({ 3 | toContainText: () => { 4 | return { 5 | compare: (actual, expectedText) => { 6 | let actualText = actual.textContent; 7 | return { 8 | pass: actualText.indexOf(expectedText) > -1, 9 | get message() { return 'Expected ' + actualText + ' to contain ' + expectedText; } 10 | }; 11 | } 12 | }; 13 | } 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/app/todolist/todolist.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { CompletedFilterPipe, TodolistComponent } from './index'; 5 | 6 | @NgModule({ 7 | declarations: [ 8 | CompletedFilterPipe, 9 | TodolistComponent 10 | ], 11 | imports: [ 12 | FormsModule, 13 | BrowserModule 14 | ], 15 | exports: [ 16 | CompletedFilterPipe, 17 | TodolistComponent 18 | ] 19 | }) 20 | export class TodolistModule { 21 | } 22 | -------------------------------------------------------------------------------- /config/test/protractor.conf.js: -------------------------------------------------------------------------------- 1 | require('ts-node/register'); 2 | 3 | var glob = require('glob'); 4 | var seleniumPath = '../node_modules/gulp-protractor/node_modules/protractor/selenium/'; 5 | var seleniumJarPath = ''; 6 | 7 | glob(seleniumPath + '*.jar', 8 | function(err, files) { 9 | seleniumJarPath = files[0]; 10 | }); 11 | 12 | exports.config = { 13 | seleniumServerJar: seleniumJarPath, 14 | capabilities: { 15 | 'browserName': 'chrome' 16 | }, 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 60000 20 | }, 21 | allScriptsTimeout: 30000, 22 | useAllAngular2AppRoots: true 23 | }; 24 | -------------------------------------------------------------------------------- /src/assets/styles/mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin font-face($name, $path, $exts: eot woff2 woff ttf svg) { 2 | $src: null; 3 | $path: '../fonts/' + $path; 4 | 5 | $extmods: ( 6 | eot: "?", 7 | svg: "#" 8 | ); 9 | 10 | $formats: ( 11 | otf: "opentype", 12 | ttf: "truetype" 13 | ); 14 | 15 | @each $ext in $exts { 16 | $extmod: if(map-has-key($extmods, $ext), $ext + map-get($extmods, $ext), $ext); 17 | $format: if(map-has-key($formats, $ext), map-get($formats, $ext), $ext); 18 | $src: append($src, url(quote($path + "." + $extmod)) format(quote($format)), comma); 19 | } 20 | 21 | @font-face { 22 | font-family: quote($name); 23 | src: $src; 24 | } 25 | } -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { BrowserModule } from '@angular/platform-browser'; 3 | 4 | import { APP_PROVIDERS } from './app.providers'; 5 | import { AppComponent } from './app.component'; 6 | import { appRoutingProviders, routing } from './app.routing'; 7 | import { NavbarModule } from './shared'; 8 | import { HomeModule } from './home/home.module'; 9 | import { TodolistModule } from './todolist/todolist.module'; 10 | 11 | @NgModule({ 12 | declarations: [ 13 | AppComponent 14 | ], 15 | imports: [ 16 | BrowserModule, 17 | NavbarModule, 18 | HomeModule, 19 | TodolistModule, 20 | routing 21 | ], 22 | providers: [ APP_PROVIDERS, appRoutingProviders ], 23 | bootstrap: [ AppComponent ] 24 | }) 25 | export class AppModule { 26 | } 27 | -------------------------------------------------------------------------------- /src/app/todolist/completed-filter.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { CompletedFilterPipe } from './completed-filter.pipe'; 2 | import { Todo } from './todo.model'; 3 | 4 | describe('CompletedFilterPipe', () => { 5 | let pipe: CompletedFilterPipe; 6 | let todos: Todo[] = [ 7 | new Todo('test1', true), 8 | new Todo('test2', false) 9 | ]; 10 | 11 | beforeEach(() => { 12 | pipe = new CompletedFilterPipe(); 13 | }); 14 | 15 | it('should return original todos when pass true', () => { 16 | let result = pipe.transform(todos, true); 17 | expect(result).toEqual(todos); 18 | }); 19 | 20 | it('should return not-completed todos when pass false', () => { 21 | let result = pipe.transform(todos, false); 22 | result.forEach(todo => expect(todo.done).toBeFalsy()); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/app/shared/navbar/navbar.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ## Issue Overview 6 | 7 | 8 | ## Issue Description 9 | 10 | 11 | ## Reproducables 12 | 13 | 14 | ## Information 15 | | | | 16 | |--------------------- |--------------------------------- | 17 | | **Operating System** | Windows/OSX/Ubuntu/orYourOSHere | 18 | | **Node version** | 4.x/5.x/orYourNodeVersionHere | 19 | | **NPM Version** | 2.x/3.x/orYourNPMVersionHere | 20 | | **Environment** | Browser/Mobile/WebWorker | 21 | -------------------------------------------------------------------------------- /src/app/todolist/todolist.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | import { Todo } from './todo.model'; 4 | 5 | @Component({ 6 | moduleId: module.id, 7 | selector: 'as-todolist', 8 | templateUrl: 'todolist.html' 9 | }) 10 | export class TodolistComponent { 11 | public todo: Todo; 12 | private list: Todo[]; 13 | private showCompleted: Boolean; 14 | 15 | constructor() { 16 | this.showCompleted = true; 17 | this.todo = new Todo('Add me to list!', false); 18 | this.list = [ 19 | new Todo('Its cool'), 20 | new Todo('Hello', true) 21 | ]; 22 | } 23 | 24 | addTodo() { 25 | this.list = this.list.concat(Todo.clone(this.todo)); 26 | this.todo.clear(); 27 | } 28 | 29 | delTodo(todoIndex: number) { 30 | this.list = this.list.filter( 31 | (todo, index) => index !== todoIndex); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /config/gulp/tasks/clean.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var config = require('../config')(); 3 | var del = require('del'); 4 | 5 | /* Run all clean tasks */ 6 | gulp.task('clean', ['clean-build', 'clean-report', 'clean-ts', 'clean-sass']); 7 | 8 | /* Clean build folder */ 9 | gulp.task('clean-build', function () { 10 | return del([config.build.path]); 11 | }); 12 | 13 | /* Clean report folder */ 14 | gulp.task('clean-report', function () { 15 | return del([config.report.path]); 16 | }); 17 | 18 | /* Clean sass compile */ 19 | gulp.task('clean-sass', function () { 20 | return del([config.assetsPath.styles + '**/*.css']); 21 | }); 22 | 23 | /* Clean js and map */ 24 | gulp.task('clean-ts', function () { 25 | return del([config.tmp]); 26 | }); 27 | 28 | gulp.task('clean-ts-app', function () { 29 | return del([config.tmpApp]); 30 | }); 31 | 32 | gulp.task('clean-ts-test', function () { 33 | return del([config.tmpTest]); 34 | }); -------------------------------------------------------------------------------- /config/gulp/tasks/serve.js: -------------------------------------------------------------------------------- 1 | var runSequence = require('run-sequence'); 2 | 3 | var envConfig = require('../utils/env'); 4 | 5 | if (envConfig.ENV === envConfig.ENVS.DEV) 6 | { 7 | var gulp = require('gulp'); 8 | var config = require('../config')(); 9 | var bs = require("browser-sync"); 10 | 11 | function startBrowsersync (config) 12 | { 13 | bsIns = bs.create(); 14 | bsIns.init(config); 15 | bsIns.reload(); 16 | } 17 | 18 | /* Start live server dev mode */ 19 | gulp.task('serve-dev', function () 20 | { 21 | runSequence( 22 | ['sass', 'tsc-app'], 23 | ['html', 'css'], 24 | ['watch-sass', 'watch-ts', 'watch-html', 'watch-css'], function() { 25 | startBrowsersync(config.browserSync.dev); 26 | }); 27 | }); 28 | 29 | /* Start live server production mode */ 30 | gulp.task('serve-build', ['build'], function () 31 | { 32 | startBrowsersync(config.browserSync.prod); 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /config/gulp/tasks/component.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var path = require('path'); 3 | 4 | var config = require('../config')(); 5 | 6 | gulp.task('html', function () { 7 | return gulp.src(config.app + '**/*.html') 8 | .pipe(gulp.dest(config.tmpApp)); 9 | }); 10 | 11 | gulp.task('watch-html', function () { 12 | gulp.watch(config.app + '**/*.html', function(file) { 13 | var des = convertToTmpPath(file); 14 | 15 | return gulp.src(file.path) 16 | .pipe(gulp.dest(path.dirname(des))); 17 | }); 18 | }); 19 | 20 | gulp.task('css', function () { 21 | return gulp.src(config.app + '**/*.css') 22 | .pipe(gulp.dest(config.tmpApp)); 23 | }); 24 | 25 | gulp.task('watch-css', function () { 26 | gulp.watch(config.app + '**/*.css', function(file) { 27 | var des = convertToTmpPath(file); 28 | 29 | return gulp.src(file.path) 30 | .pipe(gulp.dest(path.dirname(des))); 31 | }); 32 | }); 33 | 34 | function convertToTmpPath(file) { 35 | var re = new RegExp('src\\' + path.sep + 'app\\' + path.sep,"g"); 36 | return file.path.replace(re, config.tmpApp); 37 | } -------------------------------------------------------------------------------- /config/gulp/utils/dashboard.js: -------------------------------------------------------------------------------- 1 | var envVars = require('../utils/env-vars'), 2 | envConfig = require('../utils/env'), 3 | util = require('gulp-util'), 4 | _ = require('lodash'), 5 | envStatusMessage; 6 | 7 | var color; 8 | var colorMap = { 9 | 'development': 'bgGreen', 10 | 'production': 'bgCyan' 11 | }; 12 | color = colorMap[envConfig.ENV] || 'bgMagenta'; 13 | 14 | var StarterDashboard = { 15 | show: function() { 16 | if (envVars) { 17 | envStatusMessage = '- env.json is detected. ' + _.toArray(envVars).length + 18 | ' values loaded.'; 19 | } else { 20 | envStatusMessage = '- env.json is not detected. You can create one on project root'; 21 | } 22 | 23 | console.log('============ Angular 2 Starter ============'); 24 | console.log('Current environment: ' + util.colors[color](envConfig.ENV)); 25 | console.log('- Change environment via --env or NODE_ENV'); 26 | console.log(envStatusMessage); 27 | console.log('==========================================='); 28 | } 29 | }; 30 | 31 | module.exports = StarterDashboard; 32 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # AppVeyor file 2 | # http://www.appveyor.com/docs/appveyor-yml 3 | # This file: cloned from https://github.com/gruntjs/grunt/blob/master/appveyor.yml 4 | 5 | # Build version format 6 | version: "{build}" 7 | 8 | # Test against this version of Node.js 9 | environment: 10 | nodejs_version: "5.5.0" 11 | 12 | build: off 13 | 14 | clone_depth: 10 15 | 16 | # Fix line endings on Windows 17 | init: 18 | - git config --global core.autocrlf true 19 | 20 | install: 21 | - ps: Install-Product node $env:nodejs_version 22 | - npm install -g npm 23 | - ps: $env:path = $env:appdata + "\npm;" + $env:path 24 | - npm install 25 | 26 | test_script: 27 | # Output useful info for debugging. 28 | - node --version && npm --version 29 | - node -e 'console.log(process.env);' 30 | # We test multiple Windows shells because of prior stdout buffering issues 31 | # filed against Grunt. https://github.com/joyent/node/issues/3584 32 | - ps: "npm test # PowerShell" # Pass comment to PS for easier debugging 33 | - cmd: npm test 34 | 35 | cache: 36 | - node_modules -> package.json # local npm modules 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Antony Budianto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /config/test/karma-test-shim.js: -------------------------------------------------------------------------------- 1 | // Turn on full stack traces in errors to help debugging 2 | Error.stackTraceLimit=Infinity; 3 | 4 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; 5 | 6 | // Cancel Karma's synchronous start, 7 | // we will call `__karma__.start()` later, once all the specs are loaded. 8 | __karma__.loaded = function() {}; 9 | 10 | System.import('test/test-helpers/setup') 11 | .then(function() { 12 | return Promise.all( 13 | Object.keys(window.__karma__.files) 14 | .filter(onlySpecFiles) 15 | .map(file2moduleName) 16 | .map(importModules) 17 | ); 18 | }) 19 | .then(function() { 20 | __karma__.start(); 21 | }, function(error) { 22 | __karma__.error(error.name + ": " + error.message); 23 | }); 24 | 25 | // Filter spec files 26 | function onlySpecFiles(path) { 27 | return /\.spec\.js$/.test(path); 28 | } 29 | 30 | // Normalize paths to module names. 31 | function file2moduleName(filePath) { 32 | return filePath.replace(/\\/g, '/') 33 | .replace(/^\/base\//, '') 34 | .replace(/\.js/, ''); 35 | } 36 | 37 | // Import module path 38 | function importModules(path) { 39 | return System.import(path); 40 | } 41 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Angular Starter 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | RouterTestingModule 3 | } from '@angular/router/testing'; 4 | import { 5 | async, 6 | TestBed, 7 | ComponentFixture 8 | } from '@angular/core/testing'; 9 | import { provideRoutes, Routes, RouterModule } from '@angular/router'; 10 | import { Component } from '@angular/core'; 11 | 12 | import { AppComponent } from './app.component'; 13 | import { NavbarComponent } from './shared/navbar/navbar.component'; 14 | 15 | @Component({ 16 | selector: 'as-test-cmp', 17 | template: '
Hello test
' 18 | }) 19 | class TestRouterComponent { 20 | } 21 | 22 | let config: Routes = [ 23 | { 24 | path: '', component: TestRouterComponent 25 | } 26 | ]; 27 | 28 | describe('AppComponent', () => { 29 | beforeEach(() => { 30 | TestBed.configureTestingModule({ 31 | declarations: [ 32 | TestRouterComponent, 33 | AppComponent, 34 | NavbarComponent 35 | ], 36 | imports: [ RouterTestingModule, RouterModule ], 37 | providers: [ provideRoutes(config) ] 38 | }); 39 | }); 40 | 41 | it('should have title Hello world', async(() => { 42 | TestBed.compileComponents().then(() => { 43 | let fixture: ComponentFixture; 44 | fixture = TestBed.createComponent(AppComponent); 45 | fixture.detectChanges(); 46 | 47 | let compiled = fixture.debugElement.nativeElement; 48 | expect(compiled).toBeDefined(); 49 | // TODO: find a way to compile the routed component 50 | // expect(compiled.querySelector('div.title')).toMatch('Hello world'); 51 | }); 52 | })); 53 | }); 54 | -------------------------------------------------------------------------------- /src/app/todolist/todolist.html: -------------------------------------------------------------------------------- 1 | 20 |
21 |
22 |

To Do List  {{list.length}}

23 |
24 |
25 |
26 |
27 |
28 |
29 | 30 | Required 31 |
32 | 33 | 34 |
35 | 36 |
37 |
38 |
39 |
40 |
41 |
42 |
    43 |
  • 44 | x 45 | 46 |
  • 47 |
48 |
49 |
50 |
51 |
52 | -------------------------------------------------------------------------------- /src/systemjs.conf.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This config is only used during development and build phase only 3 | * It will not be available on production 4 | * 5 | */ 6 | 7 | (function(global) { 8 | // ENV 9 | global.ENV = global.ENV || 'development'; 10 | 11 | // map tells the System loader where to look for things 12 | var map = { 13 | 'app': 'src/tmp/app', 14 | 'test': 'src/tmp/test' 15 | }; 16 | 17 | // packages tells the System loader how to load when no filename and/or no extension 18 | var packages = { 19 | 'app': { 20 | defaultExtension: 'js' 21 | }, 22 | 'test': { 23 | defaultExtension: 'js' 24 | }, 25 | 'rxjs': { 26 | defaultExtension: 'js' 27 | } 28 | }; 29 | 30 | // List npm packages here 31 | var npmPackages = [ 32 | '@angular', 33 | 'rxjs', 34 | 'lodash' 35 | ]; 36 | 37 | // Add package entries for packages that expose barrels using index.js 38 | var packageNames = [ 39 | // App barrels 40 | 'app/shared', 41 | 42 | // 3rd party barrels 43 | 'lodash' 44 | ]; 45 | 46 | // Add package entries for angular packages 47 | var ngPackageNames = [ 48 | 'common', 49 | 'compiler', 50 | 'core', 51 | 'forms', 52 | 'http', 53 | 'platform-browser', 54 | 'platform-browser-dynamic', 55 | 'router' 56 | ]; 57 | 58 | npmPackages.forEach(function (pkgName) { 59 | map[pkgName] = 'node_modules/' + pkgName; 60 | }); 61 | 62 | packageNames.forEach(function(pkgName) { 63 | packages[pkgName] = { main: 'index.js', defaultExtension: 'js' }; 64 | }); 65 | 66 | ngPackageNames.forEach(function(pkgName) { 67 | map['@angular/' + pkgName] = 'node_modules/@angular/' + pkgName + 68 | '/bundles/' + pkgName + '.umd.js'; 69 | map['@angular/' + pkgName+'/testing'] = 'node_modules/@angular/' + pkgName + 70 | '/bundles/' + pkgName + '-testing.umd.js'; 71 | }); 72 | 73 | var config = { 74 | map: map, 75 | packages: packages 76 | }; 77 | 78 | // filterSystemConfig - index.html's chance to modify config before we register it. 79 | if (global.filterSystemConfig) { global.filterSystemConfig(config); } 80 | 81 | System.config(config); 82 | 83 | })(this); 84 | -------------------------------------------------------------------------------- /config/gulp/tasks/typescript.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var util = require('gulp-util'); 3 | var config = require('../config')(); 4 | var ts = require('gulp-typescript'); 5 | var tslint = require('gulp-tslint'); 6 | var sourcemaps = require('gulp-sourcemaps'); 7 | var argv = require('yargs').argv; 8 | 9 | /* Initialize TS Project */ 10 | var typingFiles = [ 11 | config.src + 'manual_typings/**/*.d.ts' 12 | ]; 13 | var tsUnitFiles = [].concat(config.tsTestFiles.unit, config.tsTestFiles.helper); 14 | var tsFiles = [].concat(config.tsFiles, tsUnitFiles); 15 | 16 | /* Watch changed typescripts file and compile it */ 17 | gulp.task('watch-ts', function () { 18 | return gulp.watch(tsFiles, function (file) { 19 | util.log('Compiling ' + file.path + '...'); 20 | return compileTs(file.path, true); 21 | }); 22 | }); 23 | 24 | /* Compile typescripts */ 25 | gulp.task('tsc', ['clean-ts', 'env'], function () { 26 | return compileTs(tsFiles); 27 | }); 28 | 29 | gulp.task('tsc-app', ['env'], function () { 30 | return compileTs(config.tsFiles); 31 | }); 32 | 33 | gulp.task('tsc-unit', ['clean-ts-test'], function () { 34 | return compileTs(tsUnitFiles); 35 | }); 36 | 37 | /* Lint typescripts */ 38 | gulp.task('tslint', function () { 39 | return lintTs(tsFiles); 40 | }); 41 | 42 | gulp.task('tslint-app', function () { 43 | return lintTs(config.tsFiles); 44 | }); 45 | 46 | gulp.task('tslint-unit', function () { 47 | return lintTs(tsUnitFiles); 48 | }); 49 | 50 | function lintTs(files) { 51 | return gulp.src(files) 52 | .pipe(tslint({ 53 | formatter: 'verbose' 54 | })) 55 | .pipe(tslint.report()); 56 | } 57 | 58 | function compileTs(files, watchMode) { 59 | watchMode = watchMode || false; 60 | 61 | var tsProject = ts.createProject('tsconfig.json'); 62 | var allFiles = [].concat(files, typingFiles); 63 | var res = gulp.src(allFiles, { 64 | base: config.src, 65 | outDir: config.tmp 66 | }) 67 | .pipe(tslint({ 68 | formatter: 'verbose' 69 | })) 70 | .pipe(tslint.report()) 71 | .pipe(sourcemaps.init()) 72 | .pipe(tsProject()) 73 | .on('error', function () { 74 | if (watchMode) { 75 | return; 76 | } 77 | process.exit(1); 78 | }); 79 | return res.js 80 | .pipe(sourcemaps.write()) 81 | .pipe(gulp.dest(config.tmp)); 82 | } -------------------------------------------------------------------------------- /config/gulp/tasks/build.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var util = require('gulp-util'); 3 | var runSequence = require('run-sequence'); 4 | var config = require('../config')(); 5 | var useref = require('gulp-useref'); 6 | var gulpif = require('gulp-if'); 7 | var rev = require('gulp-rev'); 8 | var revReplace = require('gulp-rev-replace'); 9 | var uglify = require('gulp-uglify'); 10 | var cssnano = require('gulp-cssnano'); 11 | var gulpTemplate = require('gulp-template'); 12 | var flatten = require('gulp-flatten'); 13 | 14 | var envVars = require('../utils/env-vars'); 15 | 16 | require('@ngstarter/systemjs-extension')(config); 17 | 18 | gulp.task('build', function (done) { 19 | runSequence('test', 'build-systemjs', 'build-assets', done); 20 | }); 21 | 22 | /* Concat and minify/uglify all css, js, and copy fonts */ 23 | gulp.task('build-assets', function (done) { 24 | runSequence('clean-build', ['sass', 'fonts'], function () { 25 | gulp.src(config.app + '**/*.html') 26 | .pipe(flatten()) 27 | .pipe(gulp.dest(config.build.path)); 28 | 29 | gulp.src(config.app + '**/*.css') 30 | .pipe(cssnano({zindex: false})) 31 | .pipe(flatten()) 32 | .pipe(gulp.dest(config.build.path)); 33 | 34 | gulp.src(config.src + 'favicon.ico') 35 | .pipe(gulp.dest(config.build.path)); 36 | 37 | gulp.src(config.assetsPath.images + '**/*.*', { 38 | base: config.assetsPath.images 39 | }) 40 | .pipe(gulp.dest(config.build.assetPath + 'images')); 41 | 42 | gulp.src(config.index) 43 | .pipe(useref()) 44 | .pipe(gulpif('assets/lib.js', uglify())) 45 | .pipe(gulpif('*.css', cssnano())) 46 | .pipe(gulpif('!*.html', rev())) 47 | .pipe(revReplace()) 48 | .pipe(gulp.dest(config.build.path)) 49 | .on('finish', done); 50 | }); 51 | }); 52 | 53 | /* Copy fonts in packages */ 54 | gulp.task('fonts', function () { 55 | gulp.src(config.assetsPath.fonts + '**/*.*', { 56 | base: config.assetsPath.fonts 57 | }) 58 | .pipe(gulp.dest(config.build.fonts)); 59 | 60 | gulp.src([ 61 | 'node_modules/font-awesome/fonts/*.*' 62 | ]) 63 | .pipe(gulp.dest(config.build.fonts)); 64 | }); 65 | 66 | gulp.task('env', function () { 67 | return gulp.src(config.config + 'env/env.ts') 68 | .pipe(gulpTemplate({ 69 | env: envVars || {} 70 | })) 71 | .pipe(gulp.dest(config.app + 'shared/constant')) 72 | .on('finish', function () { 73 | util.log(config.app + 'shared/constant/env.ts is generated successfully'); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /src/app/todolist/todolist.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | async, 3 | TestBed 4 | } from '@angular/core/testing'; 5 | import { Component } from '@angular/core'; 6 | 7 | import { Todo, TodolistComponent, TodolistModule } from './index'; 8 | 9 | @Component({ 10 | selector: 'as-test', 11 | template: '' 12 | }) 13 | class TestComponent { 14 | } 15 | 16 | let todoCompiled; 17 | let todolistCmp: TodolistComponent; 18 | 19 | describe('TodolistComponent', () => { 20 | beforeEach(() => { 21 | TestBed.configureTestingModule({ 22 | declarations: [ TestComponent ], 23 | imports: [ TodolistModule ] 24 | }); 25 | }); 26 | 27 | it('should have been created successfully', async(() => { 28 | TestBed.compileComponents().then(() => { 29 | let fixture = TestBed.createComponent(TestComponent); 30 | fixture.detectChanges(); 31 | 32 | todoCompiled = fixture.nativeElement; 33 | todolistCmp = fixture.debugElement 34 | .children[0].componentInstance; 35 | expect(todoCompiled).toBeDefined(); 36 | }); 37 | })); 38 | 39 | it('should add todo successfully', async(() => { 40 | TestBed.compileComponents().then(() => { 41 | let fixture = TestBed.createComponent(TestComponent); 42 | fixture.detectChanges(); 43 | todoCompiled = fixture.nativeElement; 44 | todolistCmp = fixture.debugElement 45 | .children[0].componentInstance; 46 | todolistCmp.todo = new Todo('test', true); 47 | todolistCmp.addTodo(); 48 | fixture.detectChanges(); 49 | 50 | let items = todoCompiled.querySelectorAll('.list-group-item'); 51 | expect(items.length).toEqual(3); 52 | 53 | let item = items[items.length - 1]; 54 | expect(item.querySelector('label').textContent).toEqual(' test'); 55 | expect(item.querySelector('input[type="checkbox"]').value).toBeTruthy(); 56 | }); 57 | })); 58 | 59 | it('should delete todo successfully', async(() => { 60 | TestBed.compileComponents().then(() => { 61 | let fixture = TestBed.createComponent(TestComponent); 62 | fixture.detectChanges(); 63 | 64 | todoCompiled = fixture.nativeElement; 65 | todolistCmp = fixture.debugElement 66 | .children[0].componentInstance; 67 | 68 | todolistCmp.delTodo(0); 69 | fixture.detectChanges(); 70 | expect(todoCompiled.querySelectorAll('.list-group-item').length) 71 | .toEqual(1); 72 | }); 73 | })); 74 | }); 75 | -------------------------------------------------------------------------------- /config/gulp/tasks/test.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var util = require('gulp-util'); 3 | var config = require('../config')(); 4 | var Server = require('karma').Server; 5 | var gulpProtractor = require('gulp-protractor'); 6 | var remapIstanbul = require('remap-istanbul/lib/gulpRemapIstanbul'); 7 | var runSequence = require('run-sequence'); 8 | var argv = require('yargs') 9 | .alias('w', 'watch') 10 | .argv; 11 | 12 | gulp.task('test', ['clean-report', 'unit-test']); 13 | 14 | gulp.task('unit-test', ['tsc'], function (done) { 15 | var watch = argv.watch || false; 16 | 17 | runSequence(['html', 'css'], copyComponentFilesDone); 18 | 19 | if (watch) { 20 | runSequence(['watch-ts', 'watch-html', 'watch-css']); 21 | console.log('=== Unit Test Watch Mode ==='); 22 | console.log('- It will autowatch the changed files and re-run the test'); 23 | console.log('- Press Cmd/Ctrl + C to exit and get the coverage result'); 24 | console.log('- Press Cmd/Ctrl + C again to close the TSC watch.'); 25 | } 26 | 27 | function copyComponentFilesDone() { 28 | new Server({ 29 | configFile: __dirname + '/../../test/karma.conf.js', 30 | singleRun: !watch 31 | }, karmaDone).start(); 32 | } 33 | 34 | function karmaDone (exitCode) { 35 | remapCoverage(done, exitCode); 36 | } 37 | }); 38 | 39 | gulp.task('e2e', ['e2e-test']); 40 | gulp.task('driver-update', gulpProtractor['webdriver_update']); 41 | gulp.task('e2e-test', ['driver-update'], function () { 42 | gulp.src(config.e2e + '**/*.spec.ts') 43 | .pipe(gulpProtractor.protractor({ 44 | configFile: 'config/test/protractor.conf.js', 45 | args: ['--baseUrl', config.e2eConfig.seleniumTarget] 46 | })) 47 | .on('error', function(e) { 48 | util.log('Error running E2E testing'); 49 | process.exit(1); 50 | }); 51 | }); 52 | 53 | function remapCoverage (done, exitCode) { 54 | util.log('Remapping coverage to TypeScript format...'); 55 | gulp.src(config.report.path + 'report-json/coverage-final.json') 56 | .pipe(remapIstanbul({ 57 | reports: { 58 | 'lcovonly': config.report.path + 'remap/lcov.info', 59 | 'json': config.report.path + 'remap/coverage.json', 60 | 'html': config.report.path + 'remap/html-report', 61 | 'text-summary': config.report.path + 'remap/text-summary.txt' 62 | } 63 | })) 64 | .on('finish', function () { 65 | util.log('Test Done with exit code: ' + exitCode); 66 | done(exitCode); 67 | util.log('Remapping done! View the result in report/remap/html-report'); 68 | }); 69 | } 70 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "class-name": true, 7 | "comment-format": [true, "check-space"], 8 | "curly": true, 9 | "eofline": true, 10 | "forin": true, 11 | "indent": [true, "spaces"], 12 | "label-position": true, 13 | "max-line-length": [true, 140], 14 | "member-access": false, 15 | "member-ordering": [true, 16 | "public-before-private", 17 | "static-before-instance", 18 | "variables-before-functions" 19 | ], 20 | "no-arg": true, 21 | "no-bitwise": true, 22 | "no-console": [true, 23 | "debug", 24 | "info", 25 | "time", 26 | "timeEnd", 27 | "trace" 28 | ], 29 | "no-construct": true, 30 | "no-debugger": true, 31 | "no-duplicate-variable": true, 32 | "no-empty": true, 33 | "no-eval": true, 34 | "no-inferrable-types": true, 35 | "no-shadowed-variable": true, 36 | "no-string-literal": true, 37 | "no-switch-case-fall-through": true, 38 | "trailing-comma": true, 39 | "no-trailing-whitespace": true, 40 | "no-unused-expression": true, 41 | "no-use-before-declare": true, 42 | "no-var-keyword": true, 43 | "object-literal-sort-keys": false, 44 | "one-line": [true, 45 | "check-open-brace", 46 | "check-catch", 47 | "check-else", 48 | "check-whitespace" 49 | ], 50 | "quotemark": [true, "single"], 51 | "radix": true, 52 | "semicolon": [true, "always"], 53 | "triple-equals": [true, "allow-null-check"], 54 | "typedef-whitespace": [true, { 55 | "call-signature": "nospace", 56 | "index-signature": "nospace", 57 | "parameter": "nospace", 58 | "property-declaration": "nospace", 59 | "variable-declaration": "nospace" 60 | }], 61 | "variable-name": false, 62 | "whitespace": [true, 63 | "check-branch", 64 | "check-decl", 65 | "check-operator", 66 | "check-separator", 67 | "check-type" 68 | ], 69 | 70 | "directive-selector": [true, "attribute", ["as"], "camelCase"], 71 | "component-selector": [true, "element", ["as"], "kebab-case"], 72 | "use-input-property-decorator": true, 73 | "use-output-property-decorator": true, 74 | "use-host-property-decorator": true, 75 | "no-attribute-parameter-decorator": true, 76 | "no-input-rename": true, 77 | "no-output-rename": true, 78 | "no-forward-ref": true, 79 | "use-life-cycle-interface": true, 80 | "use-pipe-transform-interface": true, 81 | "component-class-suffix": [true, "Component"], 82 | "directive-class-suffix": [true, "Directive"], 83 | "import-destructuring-spacing": true, 84 | "templates-use-public": false, 85 | "no-access-missing-member": true, 86 | "invoke-injectable": true 87 | } 88 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-starter", 3 | "version": "1.1.1", 4 | "author": "Antony Budianto", 5 | "description": "Angular Starter with extensible features", 6 | "main": "src/tmp/app/main.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/antonybudianto/angular-starter.git" 10 | }, 11 | "engines": { 12 | "node": ">=6.9", 13 | "npm": "~3.0.0" 14 | }, 15 | "scripts": { 16 | "start": "gulp serve-dev", 17 | "lint": "gulp tslint", 18 | "test": "gulp test", 19 | "e2e": "gulp e2e", 20 | "env": "gulp env", 21 | "build": "gulp build", 22 | "serve-build": "gulp serve-build", 23 | "clean": "gulp clean", 24 | "coverage": "coveralls < ./report/remap/lcov.info" 25 | }, 26 | "keywords": [ 27 | "angular", 28 | "starter", 29 | "seed", 30 | "gulp", 31 | "webpack", 32 | "extensible", 33 | "systemjs", 34 | "systemjs builder", 35 | "travis", 36 | "karma", 37 | "jasmine", 38 | "istanbul" 39 | ], 40 | "license": "MIT", 41 | "bugs": { 42 | "url": "https://github.com/antonybudianto/angular-starter/issues" 43 | }, 44 | "dependencies": { 45 | "@angular/common": "~4.4.3", 46 | "@angular/compiler": "~4.4.3", 47 | "@angular/core": "~4.4.3", 48 | "@angular/forms": "~4.4.3", 49 | "@angular/http": "~4.4.3", 50 | "@angular/platform-browser": "~4.4.3", 51 | "@angular/platform-browser-dynamic": "~4.4.3", 52 | "@angular/router": "~4.4.3", 53 | "@ngstarter/systemjs-extension": "2.0.1", 54 | "@types/core-js": "0.9.43", 55 | "@types/jasmine": "2.6.0", 56 | "@types/lodash": "4.14.74", 57 | "@types/selenium-webdriver": "^3.0.7", 58 | "bootstrap-sass": "^3.3.7", 59 | "codelyzer": "3.2.0", 60 | "core-js": "^2.5.1", 61 | "del": "~3.0.0", 62 | "font-awesome": "^4.7.0", 63 | "glob": "^7.1.2", 64 | "gulp": "^3.9.1", 65 | "gulp-cssnano": "^2.1.2", 66 | "gulp-flatten": "^0.3.1", 67 | "gulp-if": "~2.0.2", 68 | "gulp-protractor": "^4.1.0", 69 | "gulp-rev": "^8.0.0", 70 | "gulp-rev-replace": "^0.4.3", 71 | "gulp-sass": "^3.1.0", 72 | "gulp-sourcemaps": "^2.6.1", 73 | "gulp-template": "^4.0.0", 74 | "gulp-tslint": "8.1.2", 75 | "gulp-typescript": "~3.2.2", 76 | "gulp-uglify": "~3.0.0", 77 | "gulp-useref": "~3.1.2", 78 | "jasmine-core": "~2.8.0", 79 | "jquery": "^3.2.1", 80 | "karma": "~1.7.1", 81 | "karma-coverage": "~1.1.1", 82 | "karma-ie-launcher": "^1.0.0", 83 | "karma-jasmine": "~1.1.0", 84 | "karma-phantomjs-launcher": "~1.0.4", 85 | "karma-sourcemap-loader": "^0.3.7", 86 | "lodash": "^4.17.4", 87 | "phantomjs-prebuilt": "^2.1.15", 88 | "remap-istanbul": "0.9.5", 89 | "require-dir": "~0.3.2", 90 | "run-sequence": "~2.2.0", 91 | "rxjs": "5.4.3", 92 | "systemjs": "^0.20.19", 93 | "tslint": "~5.7.0", 94 | "typescript": "~2.5.2", 95 | "yargs": "^9.0.1", 96 | "zone.js": "^0.8.17" 97 | }, 98 | "devDependencies": { 99 | "browser-sync": "^2.18.13", 100 | "connect-history-api-fallback": "^1.3.0", 101 | "ts-node": "^3.3.0" 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /config/test/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | var gulpConfig = require('../gulp/config')(); 3 | 4 | /** 5 | * List of npm packages that imported via `import` syntax 6 | */ 7 | var dependencies = [ 8 | '@angular', 9 | 'lodash', 10 | 'rxjs' 11 | ]; 12 | 13 | var configuration = { 14 | basePath: '../../', 15 | 16 | frameworks: ['jasmine'], 17 | browsers: ['PhantomJS'], 18 | reporters: ['progress', 'coverage'], 19 | 20 | preprocessors: {}, 21 | 22 | // Generate json used for remap-istanbul 23 | coverageReporter: { 24 | dir: 'report/', 25 | reporters: [ 26 | { type: 'json', subdir: 'report-json' } 27 | ] 28 | }, 29 | 30 | files: [ 31 | 'node_modules/core-js/client/shim.min.js', 32 | 'node_modules/zone.js/dist/zone.js', 33 | 'node_modules/zone.js/dist/long-stack-trace-zone.js', 34 | 'node_modules/zone.js/dist/proxy.js', 35 | 'node_modules/zone.js/dist/sync-test.js', 36 | 'node_modules/zone.js/dist/jasmine-patch.js', 37 | 'node_modules/zone.js/dist/async-test.js', 38 | 'node_modules/zone.js/dist/fake-async-test.js', 39 | 'node_modules/systemjs/dist/system.src.js' 40 | ], 41 | 42 | // proxied base paths 43 | proxies: { 44 | // required for component assests fetched by Angular's compiler 45 | "/src/": "/base/src/", 46 | "/app/": "/base/src/app/", 47 | "/tmp/": "/base/src/tmp/", 48 | "/node_modules/": "/base/node_modules/" 49 | }, 50 | 51 | port: 9876, 52 | colors: true, 53 | logLevel: config.LOG_INFO, 54 | autoWatch: true 55 | }; 56 | 57 | configuration.preprocessors[gulpConfig.tmpApp + '**/!(*.spec)+(.js)'] = ['coverage']; 58 | configuration.preprocessors[gulpConfig.tmpApp + '**/*.js'] = ['sourcemap']; 59 | configuration.preprocessors[gulpConfig.tmpTest + '**/*.js'] = ['sourcemap']; 60 | 61 | var files = [ 62 | gulpConfig.tmpTest + 'test-helpers/global/**/*.js', 63 | gulpConfig.src + 'systemjs.conf.js', 64 | 'config/test/karma-test-shim.js', 65 | createFilePattern(gulpConfig.tmpApp + '**/*.js', { included: false }), 66 | createFilePattern(gulpConfig.tmpTest + 'test-helpers/*.js', { included: false }), 67 | createFilePattern(gulpConfig.app + '**/*.html', { included: false }), 68 | createFilePattern(gulpConfig.tmpApp + '**/*.html', { included: false }), 69 | createFilePattern(gulpConfig.app + '**/*.css', { included: false }), 70 | createFilePattern(gulpConfig.app + '**/*.ts', { included: false, watched: false }), 71 | createFilePattern(gulpConfig.tmpApp + '**/*.js.map', { included: false, watched: false }) 72 | ]; 73 | 74 | configuration.files = configuration.files.concat(files); 75 | 76 | dependencies.forEach(function(key) { 77 | configuration.files.push({ 78 | pattern: 'node_modules/' + key + '/**/!(*.spec)+(.js)', 79 | included: false, 80 | watched: false 81 | }); 82 | }); 83 | 84 | if (process.env.APPVEYOR) { 85 | configuration.browsers = ['IE']; 86 | configuration.singleRun = true; 87 | configuration.browserNoActivityTimeout = 90000; // Note: default value (10000) is not enough 88 | } 89 | 90 | config.set(configuration); 91 | 92 | // Helpers 93 | function createFilePattern(path, config) { 94 | config.pattern = path; 95 | return config; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /config/gulp/config.js: -------------------------------------------------------------------------------- 1 | var envConfig = require('./utils/env'); 2 | 3 | module.exports = function () { 4 | var root = '', 5 | src = root + 'src/', 6 | config = root + 'config/', 7 | app = src + 'app/', 8 | test = src + 'test/', 9 | tmp = src + 'tmp/', 10 | tmpApp = tmp + 'app/', 11 | tmpTest = tmp + 'test/', 12 | testHelper = test + 'test-helpers/', 13 | e2e = root + 'e2e/', 14 | assets = src + 'assets/', 15 | assetsPath = { 16 | styles: assets + 'styles/', 17 | images: assets + 'images/', 18 | fonts: assets + 'fonts/' 19 | }, 20 | index = src + 'index.html', 21 | tsFiles = [ 22 | app + '**/!(*.spec)+(.ts)' 23 | ], 24 | tsTestFiles = { 25 | unit: [app + '**/*.spec.ts'], 26 | e2e: [e2e + '**/*.ts'], 27 | helper: [testHelper + '**/*.ts'] 28 | }, 29 | build = { 30 | path: 'build/', 31 | app: 'build/app/', 32 | fonts: 'build/fonts', 33 | assetPath: 'build/assets/', 34 | assets: { 35 | lib: { 36 | js: 'lib.js', 37 | css: 'lib.css' 38 | } 39 | } 40 | }, 41 | report = { 42 | path: 'report/' 43 | }; 44 | 45 | var e2eConfig = { 46 | seleniumTarget: 'http://127.0.0.1:3000' 47 | }; 48 | 49 | var systemJs = { 50 | builder: { 51 | normalize: true, 52 | minify: true, 53 | mangle: true, 54 | runtime: false, 55 | globalDefs: { 56 | DEBUG: false, 57 | ENV: 'production' 58 | } 59 | } 60 | }; 61 | 62 | var gulpConfig = { 63 | root: root, 64 | config: config, 65 | src: src, 66 | app: app, 67 | test: test, 68 | tmp: tmp, 69 | tmpApp: tmpApp, 70 | tmpTest: tmpTest, 71 | testHelper: testHelper, 72 | e2e: e2e, 73 | e2eConfig: e2eConfig, 74 | assets: assets, 75 | index: index, 76 | build: build, 77 | report: report, 78 | assetsPath: assetsPath, 79 | tsFiles: tsFiles, 80 | tsTestFiles: tsTestFiles, 81 | systemJs: systemJs 82 | }; 83 | 84 | if (envConfig.ENV === envConfig.ENVS.DEV) 85 | { 86 | var historyApiFallback = require('connect-history-api-fallback'); 87 | var browserSync = { 88 | dev: { 89 | port: 3000, 90 | injectChanges: false, 91 | server: { 92 | baseDir: './src/', 93 | middleware: [historyApiFallback()], 94 | routes: { 95 | "/node_modules": "node_modules", 96 | "/src": "src" 97 | } 98 | }, 99 | files: [ 100 | src + "index.html", 101 | src + "systemjs.conf.js", 102 | assetsPath.styles + "main.css", 103 | tmpApp + "**/*.js", 104 | app + "**/*.css", 105 | app + "**/*.html" 106 | ] 107 | }, 108 | prod: { 109 | port: 3001, 110 | server: { 111 | baseDir: './' + build.path, 112 | middleware: [historyApiFallback()] 113 | } 114 | } 115 | }; 116 | 117 | gulpConfig.browserSync = browserSync; 118 | } 119 | 120 | return gulpConfig; 121 | }; 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular Starter 2 | 3 | [![Build Status](https://travis-ci.org/antonybudianto/angular-starter.svg?branch=master)](https://travis-ci.org/antonybudianto/angular-starter) 4 | [![codecov](https://codecov.io/gh/antonybudianto/angular-starter/branch/master/graph/badge.svg)](https://codecov.io/gh/antonybudianto/angular-starter) 5 | [![Dependency Status](https://david-dm.org/antonybudianto/angular-starter.svg)](https://david-dm.org/antonybudianto/angular-starter) 6 | [![devDependency Status](https://david-dm.org/antonybudianto/angular-starter/dev-status.svg)](https://david-dm.org/antonybudianto/angular-starter#info=devDependencies) 7 | 8 | > Live Production Build [Demo](https://antonybudianto.github.io/angular-starter/) 9 | 10 | > [Angular Webpack Starter](https://github.com/antonybudianto/angular-webpack-starter) is out! Featuring [AoT compilation](https://angular.io/docs/ts/latest/cookbook/aot-compiler.html), [Lazy loaded module](https://angular.io/docs/ts/latest/api/router/index/Routes-type-alias.html#!#sts=Lazy%20Loading), [Tree-shaking](https://medium.com/@Rich_Harris/tree-shaking-versus-dead-code-elimination-d3765df85c80#.103r6vl29) with [Webpack 2](https://webpack.github.io/docs/roadmap.html#2) 11 | 12 | ## Introduction 13 | Welcome to Angular Starter! 14 | This starter contains almost everything you need to start developing [Angular 2](https://angular.io/). 15 | 16 | ### Why choose this starter? 17 | - Extensible via [ngstarter extensions](https://github.com/ngstarter) 18 | - Complete workflow from serve, lint, unit test, e2e test, to bundling 19 | - Support file-based and strong-typed [Environment Variables](https://github.com/antonybudianto/angular-starter/wiki/Environment-Variables) 20 | - 100% code coverage 21 | - 100% [CI/CD](https://github.com/antonybudianto/angular-starter/wiki/Continuous-Integration) pipeline ready 22 | - No global package installation 23 | - No module bundler coupling 24 | 25 | ### What's included? 26 | * [npm](https://www.npmjs.com/) for package manager 27 | * [TypeScript](http://www.typescriptlang.org/) for the base language 28 | * with [Typings](https://github.com/typings/typings) for TypeScript definition manager 29 | * [Gulp](http://gulpjs.com/) for workflow (from *serve*, *watch*, *compile*, *test* to *build*) 30 | * [Browsersync](https://www.browsersync.io/) for development server & reload capability 31 | * [SystemJS](https://github.com/systemjs/systemjs) for module loader 32 | * [Codelyzer](https://github.com/mgechev/codelyzer) for static code analyzer 33 | * [Karma](http://karma-runner.github.io/) for test-runner 34 | * [Jasmine](http://jasmine.github.io/) for test framework 35 | * [Istanbul](https://github.com/gotwarlost/istanbul) for test coverage 36 | * with [Remap Istanbul](https://github.com/SitePen/remap-istanbul) for remapping Javascript to TypeScript coverage 37 | * [SystemJS Builder](https://github.com/systemjs/builder) or [Webpack](https://webpack.github.io/) for module bundling in production 38 | 39 | Please visit the [wiki](https://github.com/antonybudianto/angular2-starter/wiki) for more details. 40 | 41 | ## Prerequisites 42 | You need to have [Node.js and npm](https://nodejs.org/en/) 43 | - Support Node v6.9 - latest 44 | - Support npm v3 - latest 45 | 46 | [Global Gulp CLI](https://github.com/gulpjs/gulp/blob/master/docs/getting-started.md) is not required, since you can map them to npm scripts, but a nice to have for development purpose. 47 | 48 | ## Installation 49 | Download the starter from [releases page](https://github.com/antonybudianto/angular-starter/releases) 50 | 51 | Go to the starter directory and install the packages: 52 | ```bash 53 | npm install 54 | ``` 55 | 56 | ## Start 57 | Let's start up the server, run: 58 | ```bash 59 | npm start 60 | ``` 61 | 62 | and done! The browser will popup and you can start trying Angular! 63 | Every changes to the file will refresh the browser automatically 64 | and it'll also compile your changed TypeScripts files to Javascript files. 65 | 66 | ## Testing 67 | This starter comes with testing gulp workflow 68 | 69 | ### Unit testing 70 | Just run 71 | ```bash 72 | npm test 73 | ``` 74 | and it'll compile all TypeScript files, start Karma, then remap Istanbul coverage so that it shows TypeScript coverage, not the transpiled JavaScript coverage. 75 | 76 | ![Coverage result](http://s33.postimg.org/w7m9ckdkf/Screen_Shot_2016_06_04_at_8_15_53_AM.png) 77 | 78 | ### E2E testing 79 | Firstly start the server: 80 | ```bash 81 | npm start 82 | ``` 83 | To begin testing, run: 84 | ```bash 85 | npm run e2e 86 | ``` 87 | and it'll compile all E2E spec files in `/src/test/e2e/*.spec.ts`, boot up Selenium server then launches Chrome browser. 88 | 89 | ## Production 90 | > All build tasks will run the `gulp test`, the bundle will only be created if the test passed. 91 | 92 | > For more details, visit [Continuous Integration wiki](https://github.com/antonybudianto/angular-starter/wiki/Continuous-Integration) 93 | 94 | You can create production build by running: 95 | ```bash 96 | npm run build 97 | ``` 98 | or you can create production build and then serve it using Browsersync by running: 99 | ```bash 100 | npm run serve-build 101 | ``` 102 | The starter defaults to bundle using [SystemJS Builder extension](https://github.com/ngstarter/systemjs-extension). 103 | There is [Webpack extension](https://github.com/ngstarter/webpack-extension) available too, feel free to swap it as you like. 104 | 105 | ## Extension 106 | You can extend this starter with many extensions built by the community. Browse the extensions [here](https://github.com/ngstarter) 107 | 108 | ## Contributing 109 | Feel free to submit a PR if there are any issues or new features, please read [this](https://github.com/antonybudianto/angular-starter/wiki/Contributing) before 110 | 111 | ## Special thanks 112 | * For all contributors who have helped this starter improvement 113 | * John Papa for his awesome [angular-styleguide](https://github.com/johnpapa/angular-styleguide) and [Tour of Heroes](https://github.com/johnpapa/angular2-tour-of-heroes) 114 | * Julie Ralph for her [ng2-test-seed](https://github.com/juliemr/ng2-test-seed) which helped me a lot to get started with testing feature 115 | * Minko Gechev for his [angular2-seed](https://github.com/mgechev/angular2-seed) and [angular2-ngc-rollup-build](https://github.com/mgechev/angular2-ngc-rollup-build) which helped a lot 116 | 117 | ## License 118 | MIT 119 | --------------------------------------------------------------------------------