├── .gitignore
├── .nvmrc
├── karma.conf.js
├── package.json
├── readme.md
├── src
├── app
│ ├── components
│ │ ├── app
│ │ │ ├── app.component.js
│ │ │ └── app.template.html
│ │ ├── index.js
│ │ ├── todo-footer
│ │ │ ├── todo-footer.component.js
│ │ │ └── todo-footer.template.html
│ │ ├── todo-header
│ │ │ ├── todo-header.component.js
│ │ │ └── todo-header.template.html
│ │ ├── todo-item
│ │ │ ├── todo-item.component.js
│ │ │ └── todo-item.template.html
│ │ └── todo-list
│ │ │ ├── todo-list.component.js
│ │ │ └── todo-list.template.html
│ ├── main.js
│ ├── main.module.js
│ ├── models
│ │ └── todo.model.js
│ ├── pipes
│ │ ├── index.js
│ │ └── trim
│ │ │ └── trim.pipe.js
│ ├── polyfill.js
│ ├── routes.js
│ ├── services
│ │ ├── todo-store.service.js
│ │ └── todo-store.service.spec.js
│ ├── style.js
│ ├── test.js
│ └── vendor.js
├── favicon.ico
└── index.html
├── webpack.common.config.js
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | .idea
3 | node_modules
4 | npm-debug.log
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 6.5.0
2 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | let path = require('path');
3 |
4 | let npmLifecycleEvent = process.env.npm_lifecycle_event || '';
5 | let isSingleRun = npmLifecycleEvent === 'test';
6 |
7 | module.exports = function(config) {
8 | config.set({
9 | basePath: '',
10 |
11 | frameworks: ['jasmine'],
12 |
13 | files: [
14 | { pattern: 'src/app/test.js' }
15 | ],
16 |
17 | preprocessors: {
18 | 'src/app/test.js': ['webpack', 'sourcemap']
19 | },
20 |
21 | reporters: ['dots'],
22 |
23 | webpack: require('./webpack.config'),
24 |
25 | webpackServer: {
26 | stats: 'errors-only',
27 | noInfo: true
28 | },
29 |
30 | port: 9876,
31 |
32 | colors: true,
33 |
34 | logLevel: config.LOG_INFO,
35 |
36 | browsers: ['PhantomJS'],
37 |
38 | autoWatch: !isSingleRun,
39 | singleRun: isSingleRun
40 | });
41 | };
42 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular2-esnext-todomvc",
3 | "version": "3.0.0",
4 | "main": "client/app/main.js",
5 | "scripts": {
6 | "build": "webpack",
7 | "deploy": "gh-pages -d dist",
8 | "start": "webpack-dev-server",
9 | "test": "karma start",
10 | "test-watch": "karma start"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/blacksonic/angular2-esnext-todomvc.git"
15 | },
16 | "author": {
17 | "name": "blacksonic",
18 | "email": "soos.gabor86@gmail.com"
19 | },
20 | "license": "ISC",
21 | "keywords": [
22 | "angular2",
23 | "todomvc",
24 | "es6"
25 | ],
26 | "description": "Angular TodoMVC application in Javascript (ES6/ES7).",
27 | "engines": {
28 | "node": "6.5.0"
29 | },
30 | "dependencies": {
31 | "@angular/common": "~4.1.2",
32 | "@angular/compiler": "~4.1.2",
33 | "@angular/core": "~4.1.2",
34 | "@angular/forms": "~4.1.2",
35 | "@angular/http": "~4.1.2",
36 | "@angular/platform-browser": "~4.1.2",
37 | "@angular/platform-browser-dynamic": "~4.1.2",
38 | "@angular/router": "~4.1.2",
39 | "core-js": "~2.4.1",
40 | "rxjs": "~5.4.0",
41 | "todomvc-app-css": "~2.1.0",
42 | "todomvc-common": "~1.0.3",
43 | "uuid": "~3.0.1",
44 | "zone.js": "~0.8.5"
45 | },
46 | "devDependencies": {
47 | "babel-core": "~6.24.0",
48 | "babel-loader": "~7.0.0",
49 | "babel-preset-angular2": "~0.0.2",
50 | "babel-preset-env": "~1.4.0",
51 | "copy-webpack-plugin": "~4.0.1",
52 | "css-loader": "~0.28.0",
53 | "extract-text-webpack-plugin": "~2.1.0",
54 | "gh-pages": "~1.0.0",
55 | "html-loader": "~0.4.5",
56 | "jasmine-core": "~2.6.1",
57 | "karma": "~1.7.0",
58 | "karma-jasmine": "~1.1.0",
59 | "karma-phantomjs-launcher": "~1.0.4",
60 | "karma-sourcemap-loader": "~0.3.7",
61 | "karma-webpack": "~2.0.3",
62 | "source-map-loader": "~0.2.1",
63 | "style-loader": "~0.17.0",
64 | "webpack": "~2.5.1",
65 | "webpack-dev-server": "~2.4.2",
66 | "webpack-merge": "~4.1.0"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Angular ESNext TodoMVC
2 | [](https://david-dm.org/blacksonic/angular2-esnext-todomvc)
3 | [](https://david-dm.org/blacksonic/angular2-esnext-todomvc?type=dev)
4 |
5 | TodoMVC application built with Angular in Javascript (ES6/ES7).
6 |
7 | Layout and namings based on the [official Angular style guide](https://angular.io/styleguide).
8 |
9 | ### Concepts it covers
10 |
11 | - Creating @Component with directives (ngIf, ngFor)
12 | - Creating child @Component and communicating with parent (@Input, @Output)
13 | - Using Dependency Injection for service
14 | - Use @Pipe inside @Component
15 | - Routing
16 |
17 | ### Quick Start
18 |
19 | ```bash
20 |
21 | git clone https://github.com/blacksonic/angular2-esnext-todomvc.git
22 | cd angular2-esnext-todomvc
23 |
24 | npm install
25 | npm start
26 |
27 | ```
28 |
29 | Open it in your browser [http://localhost:3000](http://localhost:3000).
30 |
--------------------------------------------------------------------------------
/src/app/components/app/app.component.js:
--------------------------------------------------------------------------------
1 | import { Component, Inject } from '@angular/core';
2 |
3 | import template from './app.template.html';
4 |
5 | @Component({
6 | selector: 'todo-app',
7 | template: template
8 | })
9 | export class AppComponent {
10 | constructor(@Inject('AUTHOR') author) {
11 | this.author = author;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/app/components/app/app.template.html:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/src/app/components/index.js:
--------------------------------------------------------------------------------
1 | export * from './app/app.component';
2 | export * from './todo-footer/todo-footer.component';
3 | export * from './todo-header/todo-header.component';
4 | export * from './todo-item/todo-item.component';
5 | export * from './todo-list/todo-list.component';
6 |
--------------------------------------------------------------------------------
/src/app/components/todo-footer/todo-footer.component.js:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { ActivatedRoute } from '@angular/router';
3 |
4 | import { TodoStoreService } from '../../services/todo-store.service';
5 | import template from './todo-footer.template.html';
6 |
7 | @Component({
8 | selector: 'todo-footer',
9 | template: template
10 | })
11 | export class TodoFooterComponent {
12 | constructor(todoStore: TodoStoreService, route: ActivatedRoute) {
13 | this._todoStore = todoStore;
14 | this._route = route;
15 | this.currentStatus = '';
16 | }
17 |
18 | ngOnInit() {
19 | this._route.params
20 | .map(params => params.status)
21 | .subscribe((status) => {
22 | this.currentStatus = status || '';
23 | });
24 | }
25 |
26 | removeCompleted() {
27 | this._todoStore.removeCompleted();
28 | }
29 |
30 | getCount() {
31 | return this._todoStore.todos.length;
32 | }
33 |
34 | getRemainingCount() {
35 | return this._todoStore.getRemaining().length;
36 | }
37 |
38 | hasCompleted() {
39 | return this._todoStore.getCompleted().length > 0;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/app/components/todo-footer/todo-footer.template.html:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/src/app/components/todo-header/todo-header.component.js:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | import { TodoStoreService } from '../../services/todo-store.service';
4 | import template from './todo-header.template.html';
5 |
6 | @Component({
7 | selector: 'todo-header',
8 | template: template
9 | })
10 | export class TodoHeaderComponent {
11 | newTodo = '';
12 |
13 | constructor(todoStore: TodoStoreService) {
14 | this._todoStore = todoStore;
15 | }
16 |
17 | addTodo() {
18 | if (this.newTodo.trim().length) {
19 | this._todoStore.add(this.newTodo);
20 | this.newTodo = '';
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/app/components/todo-header/todo-header.template.html:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/app/components/todo-item/todo-item.component.js:
--------------------------------------------------------------------------------
1 | import { Component, EventEmitter, Output, Input } from '@angular/core';
2 |
3 | import template from './todo-item.template.html';
4 |
5 | @Component({
6 | selector: 'todo-item',
7 | template: template
8 | })
9 | export class TodoItemComponent {
10 | @Input() todo;
11 |
12 | @Output() itemModified = new EventEmitter();
13 |
14 | @Output() itemRemoved = new EventEmitter();
15 |
16 | editing = false;
17 |
18 | cancelEditing() {
19 | this.editing = false;
20 | }
21 |
22 | stopEditing(editedTitle) {
23 | this.todo.setTitle(editedTitle.value);
24 | this.editing = false;
25 |
26 | if (this.todo.title.length === 0) {
27 | this.remove();
28 | } else {
29 | this.update();
30 | }
31 | }
32 |
33 | edit() {
34 | this.editing = true;
35 | }
36 |
37 | toggleCompletion() {
38 | this.todo.completed = !this.todo.completed;
39 | this.update();
40 | }
41 |
42 | remove() {
43 | this.itemRemoved.next(this.todo.uid);
44 | }
45 |
46 | update() {
47 | this.itemModified.next(this.todo.uid);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/app/components/todo-item/todo-item.template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/app/components/todo-list/todo-list.component.js:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { ActivatedRoute } from '@angular/router';
3 |
4 | import { TodoStoreService } from '../../services/todo-store.service';
5 | import template from './todo-list.template.html';
6 |
7 | @Component({
8 | selector: 'todo-list',
9 | template: template
10 | })
11 | export class TodoListComponent {
12 | constructor(todoStore: TodoStoreService, route: ActivatedRoute) {
13 | this._todoStore = todoStore;
14 | this._route = route;
15 | this._currentStatus = '';
16 | }
17 |
18 | ngOnInit() {
19 | this._route.params
20 | .map(params => params.status)
21 | .subscribe((status) => {
22 | this._currentStatus = status;
23 | });
24 | }
25 |
26 | remove(uid) {
27 | this._todoStore.remove(uid);
28 | }
29 |
30 | update() {
31 | this._todoStore.persist();
32 | }
33 |
34 | getTodos() {
35 | if (this._currentStatus == 'completed') {
36 | return this._todoStore.getCompleted();
37 | }
38 | else if (this._currentStatus == 'active') {
39 | return this._todoStore.getRemaining();
40 | }
41 | else {
42 | return this._todoStore.todos;
43 | }
44 | }
45 |
46 | allCompleted() {
47 | return this._todoStore.allCompleted();
48 | }
49 |
50 | setAllTo(toggleAll) {
51 | this._todoStore.setAllTo(toggleAll.checked);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/app/components/todo-list/todo-list.template.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/main.js:
--------------------------------------------------------------------------------
1 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
2 |
3 | import { MainModule } from './main.module';
4 |
5 | platformBrowserDynamic().bootstrapModule(MainModule);
6 |
--------------------------------------------------------------------------------
/src/app/main.module.js:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { FormsModule } from '@angular/forms';
3 | import { HttpModule } from '@angular/http';
4 | import { RouterModule } from '@angular/router';
5 | import { NgModule } from '@angular/core';
6 |
7 | import { TodoStoreService } from './services/todo-store.service';
8 | import {
9 | AppComponent,
10 | TodoListComponent,
11 | TodoFooterComponent,
12 | TodoHeaderComponent,
13 | TodoItemComponent
14 | } from './components';
15 | import { TrimPipe } from './pipes';
16 | import { routes } from './routes';
17 |
18 | @NgModule({
19 | bootstrap: [AppComponent],
20 | declarations: [
21 | AppComponent,
22 | TodoListComponent,
23 | TodoFooterComponent,
24 | TodoHeaderComponent,
25 | TodoItemComponent,
26 | TrimPipe
27 | ],
28 | imports: [
29 | BrowserModule,
30 | FormsModule,
31 | HttpModule,
32 | RouterModule.forRoot(routes, {
33 | useHash: true
34 | })
35 | ],
36 | providers: [
37 | TodoStoreService,
38 | { provide: 'AUTHOR', useValue: 'Soós Gábor' }
39 | ]
40 | })
41 | export class MainModule {}
42 |
--------------------------------------------------------------------------------
/src/app/models/todo.model.js:
--------------------------------------------------------------------------------
1 | import * as uuid from 'uuid';
2 |
3 | export class TodoModel {
4 | completed;
5 | title;
6 | uid;
7 |
8 | setTitle(title) {
9 | this.title = title.trim();
10 | }
11 |
12 | constructor(title) {
13 | this.uid = uuid.v4();
14 | this.completed = false;
15 | this.title = title.trim();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/app/pipes/index.js:
--------------------------------------------------------------------------------
1 | export * from './trim/trim.pipe';
2 |
--------------------------------------------------------------------------------
/src/app/pipes/trim/trim.pipe.js:
--------------------------------------------------------------------------------
1 | import { Pipe, PipeTransform } from '@angular/core';
2 |
3 | @Pipe({ name: 'trim' })
4 | export class TrimPipe implements PipeTransform {
5 | transform(value, args) {
6 | return value.trim();
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/app/polyfill.js:
--------------------------------------------------------------------------------
1 | import 'core-js/client/shim';
2 |
3 | import 'zone.js/dist/zone';
4 | import 'zone.js/dist/long-stack-trace-zone';
5 |
--------------------------------------------------------------------------------
/src/app/routes.js:
--------------------------------------------------------------------------------
1 | import { TodoListComponent } from './components/todo-list/todo-list.component';
2 |
3 | export let routes = [
4 | { path: '', component: TodoListComponent, pathMatch: 'full' },
5 | { path: ':status', component: TodoListComponent }
6 | ];
7 |
--------------------------------------------------------------------------------
/src/app/services/todo-store.service.js:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | import { TodoModel } from '../models/todo.model';
4 |
5 | @Injectable()
6 | export class TodoStoreService {
7 | todos = [];
8 |
9 | constructor() {
10 | let persistedTodos = JSON.parse(localStorage.getItem('angular2-todos')) || [];
11 |
12 | this.todos = persistedTodos.map( (todo) => {
13 | let ret = new TodoModel(todo.title);
14 | ret.completed = todo.completed;
15 | ret.uid = todo.uid;
16 | return ret;
17 | });
18 | }
19 |
20 | get(state) {
21 | return this.todos.filter((todo) => todo.completed === state.completed);
22 | }
23 |
24 | allCompleted() {
25 | return this.todos.length === this.getCompleted().length;
26 | }
27 |
28 | setAllTo(completed) {
29 | this.todos.forEach((todo) => todo.completed = completed);
30 | this.persist();
31 | }
32 |
33 | removeCompleted() {
34 | this.todos = this.get({completed: false});
35 | this.persist();
36 | }
37 |
38 | getRemaining() {
39 | if (!this.remainingTodos) {
40 | this.remainingTodos = this.get({completed: false});
41 | }
42 |
43 | return this.remainingTodos;
44 | }
45 |
46 | getCompleted() {
47 | if (!this.completedTodos) {
48 | this.completedTodos = this.get({completed: true});
49 | }
50 |
51 | return this.completedTodos;
52 | }
53 |
54 | toggleCompletion(uid) {
55 | let todo = this._findByUid(uid);
56 |
57 | if (todo) {
58 | todo.completed = !todo.completed;
59 | this.persist();
60 | }
61 | }
62 |
63 | remove(uid) {
64 | let todo = this._findByUid(uid);
65 |
66 | if (todo) {
67 | this.todos.splice(this.todos.indexOf(todo), 1);
68 | this.persist();
69 | }
70 | }
71 |
72 | add(title) {
73 | this.todos.push(new TodoModel(title));
74 | this.persist();
75 | }
76 |
77 | persist() {
78 | this._clearCache();
79 | localStorage.setItem('angular2-todos', JSON.stringify(this.todos));
80 | }
81 |
82 | _findByUid(uid) {
83 | return this.todos.find((todo) => todo.uid == uid);
84 | }
85 |
86 | _clearCache() {
87 | this.completedTodos = null;
88 | this.remainingTodos = null;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/app/services/todo-store.service.spec.js:
--------------------------------------------------------------------------------
1 | import { TestBed, inject } from '@angular/core/testing';
2 |
3 | import { TodoStoreService } from './todo-store.service';
4 |
5 | describe('TodoStoreService', function() {
6 | let subject;
7 |
8 | beforeEach(() => {
9 | TestBed.configureTestingModule({
10 | providers: [TodoStoreService]
11 | });
12 | });
13 |
14 | beforeEach(inject([TodoStoreService], (storage) => {
15 | subject = storage;
16 | }));
17 |
18 | it('should persist todos to localStorage', function() {
19 | spyOn(localStorage, 'setItem');
20 |
21 | subject.persist();
22 |
23 | expect(localStorage.setItem).toHaveBeenCalledWith('angular2-todos', '[]');
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/app/style.js:
--------------------------------------------------------------------------------
1 | require('todomvc-common/base.css');
2 | require('todomvc-app-css/index.css');
3 |
--------------------------------------------------------------------------------
/src/app/test.js:
--------------------------------------------------------------------------------
1 | import './polyfill';
2 |
3 | import 'zone.js/dist/async-test';
4 | import 'zone.js/dist/fake-async-test';
5 | import 'zone.js/dist/sync-test';
6 | import 'zone.js/dist/proxy';
7 | import 'zone.js/dist/jasmine-patch';
8 |
9 | import { TestBed } from '@angular/core/testing';
10 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
11 |
12 | TestBed.initTestEnvironment(
13 | BrowserDynamicTestingModule,
14 | platformBrowserDynamicTesting()
15 | );
16 |
17 | let testContext = require.context('./', true, /\.spec\.js/);
18 | testContext.keys().forEach(testContext);
19 |
--------------------------------------------------------------------------------
/src/app/vendor.js:
--------------------------------------------------------------------------------
1 | import './polyfill';
2 | import 'rxjs';
3 |
4 | import 'uuid';
5 |
6 | import '@angular/platform-browser';
7 | import '@angular/platform-browser-dynamic';
8 | import '@angular/forms';
9 | import '@angular/http';
10 | import '@angular/router';
11 | import '@angular/core';
12 | import '@angular/common';
13 | import '@angular/compiler';
14 |
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sonicoder86/angular2-esnext-todomvc/2477ca256635b55cf6a4c28dd1348c25c861892d/src/favicon.ico
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Angular 2 TodoMVC
6 |
7 |
8 |
9 |
10 |
11 |
12 | Loading...
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/webpack.common.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | let path = require('path');
3 |
4 | module.exports = {
5 | context: path.join(process.cwd(), 'src'),
6 |
7 | resolve: {
8 | modules: [
9 | 'node_modules',
10 | path.resolve(process.cwd(), 'src')
11 | ],
12 | extensions: ['.js', '.json']
13 | },
14 |
15 | module: {
16 | rules: [
17 | {
18 | test: /\.js$/,
19 | loader: 'babel-loader',
20 | exclude: /(node_modules)/,
21 | query: {
22 | presets: ['env', 'angular2']
23 | }
24 | },
25 | {
26 | test: /\.js$/,
27 | use: 'source-map-loader',
28 | exclude: [
29 | path.join(process.cwd(), 'node_modules/rxjs'),
30 | path.join(process.cwd(), 'node_modules/@angular')
31 | ]
32 | },
33 | {
34 | test: /\.html$/,
35 | use: 'html-loader?attrs=false&caseSensitive&removeAttributeQuotes=false'
36 | }
37 | ]
38 | },
39 |
40 | stats: {
41 | errorDetails: true,
42 | colors: true,
43 | modules: true,
44 | reasons: true
45 | },
46 |
47 | node: {
48 | global: true,
49 | crypto: 'empty',
50 | process: true,
51 | module: false,
52 | clearImmediate: false,
53 | setImmediate: false
54 | }
55 | };
56 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | let commonConfig = require('./webpack.common.config');
3 | let webpack = require('webpack');
4 | let path = require('path');
5 | let merge = require('webpack-merge');
6 | let CopyWebpackPlugin = require('copy-webpack-plugin');
7 | let ExtractTextPlugin = require('extract-text-webpack-plugin');
8 |
9 | let npmLifecycleEvent = process.env.npm_lifecycle_event || '';
10 | let environment = process.env.NODE_ENV || '';
11 |
12 | let contextReplacementPlugin = new webpack.ContextReplacementPlugin(
13 | // The (\\|\/) piece accounts for path separators in *nix and Windows
14 | /angular(\\|\/)core(\\|\/)@angular/,
15 | path.join(process.cwd(), 'src')
16 | );
17 | let commonPlugins = [
18 | new webpack.optimize.CommonsChunkPlugin({ name: ['main', 'vendor'], minChunks: Infinity }),
19 | new ExtractTextPlugin('style.bundle.css'),
20 | new webpack.ProgressPlugin(),
21 | contextReplacementPlugin
22 | ];
23 |
24 | let cssLoader = {
25 | test: /\.css$/,
26 | loader: ExtractTextPlugin.extract({
27 | fallback: 'style-loader',
28 | use: 'css-loader'
29 | })
30 | };
31 |
32 | let entry = {
33 | 'main': './app/main.js',
34 | 'vendor': './app/vendor.js',
35 | 'style': './app/style.js',
36 | };
37 |
38 | let output = {
39 | path: path.resolve(process.cwd(), 'dist'),
40 | filename: '[name].bundle.js',
41 | sourceMapFilename: '[name].map',
42 | chunkFilename: '[id].chunk.js'
43 | };
44 |
45 | if (npmLifecycleEvent == 'build' || environment == 'production') {
46 | module.exports = merge.smart(commonConfig, {
47 | entry: entry,
48 | output: output,
49 |
50 | plugins: commonPlugins.concat([
51 | new webpack.LoaderOptionsPlugin({
52 | debug: false,
53 | minimize: true
54 | }),
55 | new webpack.optimize.UglifyJsPlugin({
56 | sourceMap: true,
57 | mangle: { screw_ie8 : true },
58 | compress: {
59 | screw_ie8: true,
60 | warnings: false
61 | }
62 | }),
63 | new CopyWebpackPlugin([
64 | { from: 'index.html' },
65 | { from: 'favicon.ico' }
66 | ])
67 | ]),
68 |
69 | module: {
70 | rules: [cssLoader]
71 | },
72 |
73 | devtool: 'source-map'
74 | });
75 | } else if (npmLifecycleEvent.indexOf('test') !== -1 || environment == 'test') {
76 | module.exports = merge.smart(commonConfig, {
77 | devtool: 'inline-source-map',
78 |
79 | plugins: [contextReplacementPlugin, new webpack.ProgressPlugin()]
80 | });
81 | } else {
82 | module.exports = merge.smart(commonConfig, {
83 | entry: entry,
84 | output: output,
85 |
86 | plugins: commonPlugins,
87 |
88 | module: {
89 | rules: [cssLoader]
90 | },
91 |
92 | devServer: {
93 | contentBase: './src',
94 | port: 3000,
95 | inline: true,
96 | historyApiFallback: true,
97 | watchOptions: {
98 | aggregateTimeout: 300,
99 | poll: 500
100 | },
101 | stats: 'errors-only'
102 | },
103 |
104 | devtool: 'cheap-module-source-map'
105 | });
106 | }
107 |
--------------------------------------------------------------------------------