├── .angulardoc.json ├── .editorconfig ├── .gitattributes ├── .gitignore ├── README.md ├── config └── spec-bundle.js ├── helpers.js ├── karma.conf.js ├── main.js ├── package-lock.json ├── package.js ├── package.json ├── src ├── app │ ├── app.routes.ts │ ├── app.ts │ ├── components │ │ ├── app.component.ts │ │ ├── app.theme.scss │ │ ├── home │ │ │ ├── home.component.html │ │ │ ├── home.component.scss │ │ │ ├── home.component.ts │ │ │ └── home.spec.ts │ │ └── login │ │ │ ├── login.component.html │ │ │ ├── login.component.scss │ │ │ └── login.component.ts │ ├── index.html │ ├── package.json │ ├── services │ │ └── authentication.ts │ └── store │ │ ├── actions │ │ └── user.actions.ts │ │ ├── index.ts │ │ ├── models │ │ └── auth.model.ts │ │ └── reducers │ │ └── user.reducer.ts ├── assets │ └── app-icon.icns ├── polyfills.ts └── vendor.ts ├── tsconfig.json ├── tslint.json ├── typedoc.json ├── webpack.config.js ├── webpack.test.js └── yarn.lock /.angulardoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "repoId": "24bb94d3-2d2c-4d68-b41f-381f61f02fea", 3 | "lastSync": 0 4 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # @joaogarin 2 | # http://editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | indent_style = space 9 | indent_size = 2 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.md] 15 | insert_final_newline = false 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # @joaogarin 2 | *.ts eol=lf 3 | *.js eol=lf 4 | *.html eol=lf 5 | *.css eol=lf 6 | *.md eol=lf 7 | *.json eol=lf 8 | *.yml eol=lf 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # @joaogarin 2 | 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # Compiled binary addons (http://nodejs.org/api/addons.html) 22 | build/Release 23 | 24 | # Users Environment Variables 25 | .lock-wscript 26 | 27 | # OS generated files # 28 | .DS_Store 29 | ehthumbs.db 30 | Icon? 31 | Thumbs.db 32 | 33 | # Node Files # 34 | /node_modules 35 | /bower_components 36 | 37 | # Typing # 38 | /src/typings/tsd/ 39 | /typings/ 40 | /tsd_typings/ 41 | 42 | # Dist # 43 | /dist 44 | /public/__build__/ 45 | /src/*/__build__/ 46 | __build__/** 47 | .webpack.json 48 | 49 | #doc 50 | /doc 51 | 52 | # IDE # 53 | .idea/ 54 | *.swp 55 | 56 | #releases 57 | release 58 | 59 | #dist folder in app 60 | src/app/dist 61 | 62 | #Remove configurations from version control 63 | config.json 64 | 65 | #Remove vscode files 66 | .vscode -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular 2 electron starterkit featuring webpack 2 | 3 | ## DEPRECATED 4 | 5 | This repo makes no longer much sense as I would recommend using [Electron forge template for Angular](https://github.com/electron-userland/electron-forge-templates) instead. for that reason I am no longer maintaining this repository. 6 | 7 | A working demo of [electron] with [angular] using [Webpack], [ngrx] and [material2] 8 | 9 | This is a starter of angular (2 and above) and electron. Its a demo of oauth with github using angular and electron. It uses [ngrx] to manage state. You should create a config file as following : 10 | 11 | ```javascript 12 | { 13 | "github": { 14 | "client_id": "yourclientID", 15 | "client_secret": "yoursecretkey", 16 | "scopes": [ 17 | "user:email", 18 | "notifications" 19 | ] 20 | } 21 | } 22 | ``` 23 | 24 | and place this file inside the "app" folder.Dont use this in production as for production you should have a safe server side URI and not have your secret key in the app folder. 25 | 26 | When running it authenticates the user and goes to a page showing the username received from the authentication oauth workflow. 27 | 28 | ## Run the example 29 | 30 | ```bash 31 | $ npm install 32 | $ npm run build 33 | $ npm run watch 34 | $ npm run electron 35 | ``` 36 | 37 | ## Packaging 38 | 39 | The app has support for packaging using 'electron-packager' 40 | 41 | ```bash 42 | $ npm run package 43 | ``` 44 | 45 | Will run the package for OSX. You can also provide additional options to the package command such as 46 | 47 | * --name : The package name 48 | * --all : Will packaget the application to all the platforms 49 | * --arch : Arches to be provided 50 | * --icon : The icon for the app 51 | 52 | ## License 53 | 54 | [MIT] 55 | 56 | [Webpack]: http://webpack.github.io 57 | [MIT]: http://markdalgleish.mit-license.org 58 | [angular]: http://angular.io 59 | [electron]: http://electron.atom.io/ 60 | [ngrx]: https://github.com/ngrx/store 61 | [material2]: https://github.com/angular/material2 62 | [electron-packager]: https://github.com/electron-userland/electron-packager 63 | -------------------------------------------------------------------------------- /config/spec-bundle.js: -------------------------------------------------------------------------------- 1 | /* 2 | * When testing with webpack and ES6, we have to do some extra 3 | * things to get testing to work right. Because we are gonna write tests 4 | * in ES6 too, we have to compile those as well. That's handled in 5 | * karma.conf.js with the karma-webpack plugin. This is the entry 6 | * file for webpack test. Just like webpack will create a bundle.js 7 | * file for our client, when we run test, it will compile and bundle them 8 | * all here! Crazy huh. So we need to do some setup 9 | */ 10 | Error.stackTraceLimit = Infinity; 11 | 12 | require('core-js/es6'); 13 | require('core-js/es7/reflect'); 14 | 15 | require('zone.js/dist/zone'); 16 | require('zone.js/dist/long-stack-trace-zone'); 17 | require('zone.js/dist/proxy'); // since zone.js 0.6.15 18 | require('zone.js/dist/sync-test'); 19 | require('zone.js/dist/jasmine-patch'); // put here since zone.js 0.6.14 20 | require('zone.js/dist/async-test'); 21 | require('zone.js/dist/fake-async-test'); 22 | 23 | // RxJS 24 | require('rxjs/Rx'); 25 | 26 | var testing = require('@angular/core/testing'); 27 | var browser = require('@angular/platform-browser-dynamic/testing'); 28 | 29 | testing.TestBed.initTestEnvironment( 30 | browser.BrowserDynamicTestingModule, 31 | browser.platformBrowserDynamicTesting() 32 | ); 33 | 34 | /* 35 | * Ok, this is kinda crazy. We can use the context method on 36 | * require that webpack created in order to tell webpack 37 | * what files we actually want to require or import. 38 | * Below, context will be a function/object with file names as keys. 39 | * Using that regex we are saying look in ../src then find 40 | * any file that ends with spec.ts and get its path. By passing in true 41 | * we say do this recursively 42 | */ 43 | var testContext = require.context('../src', true, /\.spec\.ts/); 44 | 45 | /* 46 | * get all the files, for each file, call the context function 47 | * that will require the file and load it up here. Context will 48 | * loop and require those spec files here 49 | */ 50 | function requireAll(requireContext) { 51 | return requireContext.keys().map(requireContext); 52 | } 53 | 54 | // requires and returns all modules that match 55 | var modules = requireAll(testContext); -------------------------------------------------------------------------------- /helpers.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var zlib = require('zlib'); 3 | 4 | 5 | // Helper functions 6 | 7 | function hasProcessFlag(flag) { 8 | return process.argv.join('').indexOf(flag) > -1; 9 | } 10 | 11 | function gzipMaxLevel(buffer, callback) { 12 | return zlib['gzip'](buffer, {level: 9}, callback); 13 | } 14 | 15 | function root(args) { 16 | args = Array.prototype.slice.call(arguments, 0); 17 | return path.join.apply(path, [__dirname].concat(args)); 18 | } 19 | 20 | function rootNode(args) { 21 | args = Array.prototype.slice.call(arguments, 0); 22 | return root.apply(path, ['node_modules'].concat(args)); 23 | } 24 | 25 | function prependExt(extensions, args) { 26 | args = args || []; 27 | if (!Array.isArray(args)) { args = [args] } 28 | return extensions.reduce(function(memo, val) { 29 | return memo.concat(val, args.map(function(prefix) { 30 | return prefix + val; 31 | })); 32 | }, ['']); 33 | } 34 | 35 | exports.hasProcessFlag = hasProcessFlag; 36 | exports.gzipMaxLevel = gzipMaxLevel; 37 | exports.root = root; 38 | exports.rootNode = rootNode; 39 | exports.prependExt = prependExt; 40 | exports.prepend = prependExt; -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function (config) { 2 | var testWebpackConfig = require('./webpack.test.js')({ env: 'test' }); 3 | 4 | var configuration = { 5 | 6 | // base path that will be used to resolve all patterns (e.g. files, exclude) 7 | basePath: '', 8 | 9 | /* 10 | * Frameworks to use 11 | * 12 | * available frameworks: https://npmjs.org/browse/keyword/karma-adapter 13 | */ 14 | frameworks: ['jasmine'], 15 | 16 | // list of files to exclude 17 | exclude: [], 18 | 19 | client: { 20 | captureConsole: false 21 | }, 22 | 23 | /* 24 | * list of files / patterns to load in the browser 25 | * 26 | * we are building the test environment in ./spec-bundle.js 27 | */ 28 | files: [ 29 | { pattern: './config/spec-bundle.js', watched: false }, 30 | { pattern: './src/assets/**/*', watched: false, included: false, served: true, nocache: false } 31 | ], 32 | 33 | /* 34 | * By default all assets are served at http://localhost:[PORT]/base/ 35 | */ 36 | proxies: { 37 | "/assets/": "/base/src/assets/" 38 | }, 39 | 40 | /* 41 | * preprocess matching files before serving them to the browser 42 | * available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 43 | */ 44 | preprocessors: { './config/spec-bundle.js': ['coverage', 'webpack', 'sourcemap'] }, 45 | 46 | // Webpack Config at ./webpack.test.js 47 | webpack: testWebpackConfig, 48 | 49 | coverageReporter: { 50 | type: 'in-memory' 51 | }, 52 | 53 | remapCoverageReporter: { 54 | 'text-summary': null, 55 | json: './coverage/coverage.json', 56 | html: './coverage/html' 57 | }, 58 | 59 | // Webpack please don't spam the console when running in karma! 60 | webpackMiddleware: { 61 | // webpack-dev-middleware configuration 62 | // i.e. 63 | noInfo: true, 64 | // and use stats to turn off verbose output 65 | stats: { 66 | // options i.e. 67 | chunks: false 68 | } 69 | }, 70 | 71 | /* 72 | * test results reporter to use 73 | * 74 | * possible values: 'dots', 'progress' 75 | * available reporters: https://npmjs.org/browse/keyword/karma-reporter 76 | */ 77 | reporters: ['mocha', 'coverage', 'remap-coverage'], 78 | 79 | // web server port 80 | port: 9876, 81 | 82 | // enable / disable colors in the output (reporters and logs) 83 | colors: true, 84 | 85 | /* 86 | * level of logging 87 | * possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 88 | */ 89 | logLevel: config.LOG_WARN, 90 | 91 | // enable / disable watching file and executing tests whenever any file changes 92 | autoWatch: false, 93 | 94 | /* 95 | * start these browsers 96 | * available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 97 | */ 98 | browsers: [ 99 | 'Chrome' 100 | ], 101 | 102 | customLaunchers: { 103 | ChromeTravisCi: { 104 | base: 'Chrome', 105 | flags: ['--no-sandbox'] 106 | } 107 | }, 108 | 109 | /* 110 | * Continuous Integration mode 111 | * if true, Karma captures browsers, runs the tests and exits 112 | */ 113 | singleRun: true 114 | }; 115 | 116 | if (process.env.TRAVIS) { 117 | configuration.browsers = [ 118 | 'ChromeTravisCi' 119 | ]; 120 | } 121 | 122 | config.set(configuration); 123 | }; -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Include our app 3 | */ 4 | const {app, BrowserWindow } = require('electron'); 5 | 6 | // browser-window creates a native window 7 | let mainWindow = null; 8 | 9 | app.on('window-all-closed', () => { 10 | // On macOS it is common for applications and their menu bar 11 | // to stay active until the user quits explicitly with Cmd + Q 12 | if (process.platform !== 'darwin') { 13 | app.quit(); 14 | } 15 | }); 16 | 17 | const createWindow = () => { 18 | // Initialize the window to our specified dimensions 19 | mainWindow = new BrowserWindow({ width: 1200, height: 900 }); 20 | 21 | // Tell Electron where to load the entry point from 22 | mainWindow.loadURL('file://' + __dirname + '/src/app/index.html'); 23 | 24 | // Open the DevTools. 25 | mainWindow.webContents.openDevTools(); 26 | 27 | // Clear out the main window when the app is closed 28 | mainWindow.on('closed', () => { 29 | mainWindow = null; 30 | }); 31 | }; 32 | 33 | app.on('ready', createWindow); 34 | 35 | app.on('activate', () => { 36 | // On macOS it's common to re-create a window in the app when the 37 | // dock icon is clicked and there are no other windows open. 38 | if (mainWindow === null) { 39 | createWindow(); 40 | } 41 | }); 42 | -------------------------------------------------------------------------------- /package.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var packager = require('electron-packager'); 4 | const pkg = require('./package.json'); 5 | const argv = require('minimist')(process.argv.slice(2)); 6 | const devDeps = Object.keys(pkg.devDependencies); 7 | 8 | const appName = argv.name || pkg.productName; 9 | const shouldUseAsar = argv.asar || false; 10 | const shouldBuildAll = argv.all || false; 11 | const arch = argv.arch || 'all'; 12 | const platform = argv.platform || 'darwin'; 13 | 14 | const DEFAULT_OPTS = { 15 | dir: './src/app', 16 | name: appName, 17 | asar: shouldUseAsar, 18 | ignore: [ 19 | ].concat(devDeps.map(name => `/node_modules/${name}($|/)`)) 20 | }; 21 | 22 | const icon = './src/app/dist/assets/app-icon'; 23 | 24 | if (icon) { 25 | DEFAULT_OPTS.icon = icon; 26 | } 27 | 28 | pack(platform, arch, function done(err, appPath) { 29 | console.log(err); 30 | }); 31 | 32 | function pack(plat, arch, cb) { 33 | // there is no darwin ia32 electron 34 | if (plat === 'darwin' && arch === 'ia32') return; 35 | 36 | const iconObj = { 37 | icon: DEFAULT_OPTS.icon + (() => { 38 | let extension = '.png'; 39 | if (plat === 'darwin') { 40 | extension = '.icns'; 41 | } else if (plat === 'win32') { 42 | extension = '.ico'; 43 | } 44 | return extension; 45 | })() 46 | }; 47 | 48 | const opts = Object.assign({}, DEFAULT_OPTS, iconObj, { 49 | platform: plat, 50 | arch, 51 | prune: true, 52 | all: shouldBuildAll, 53 | 'app-version': pkg.version || DEFAULT_OPTS.version, 54 | out: `release/${plat}-${arch}` 55 | }); 56 | 57 | packager(opts, cb); 58 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular2-electron", 3 | "version": "0.0.0", 4 | "description": "Angular 2 with Electron and Webpack", 5 | "main": "main.js", 6 | "scripts": { 7 | "watch": "npm run watch:dev", 8 | "watch:dev": "webpack --watch --progress --profile", 9 | "build": "npm run build:dev", 10 | "build:dev": "webpack --progress --profile", 11 | "package": "node package.js", 12 | "package-all": "npm run package -- --all", 13 | "electron": "electron .", 14 | "webpack-test": "webpack --config webpack.test.js --progress --profile", 15 | "test": "karma start" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/joaogarin/angular2-electron.git" 20 | }, 21 | "author": "joaogarin ", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/joaogarin/angular2-electron/issues" 25 | }, 26 | "homepage": "", 27 | "dependencies": { 28 | "@angular/animations": "^4.3.6", 29 | "@angular/cdk": "^2.0.0-beta.10", 30 | "@angular/common": "^4.3.6", 31 | "@angular/compiler": "^4.3.6", 32 | "@angular/core": "^4.3.6", 33 | "@angular/forms": "^4.3.6", 34 | "@angular/http": "^4.3.6", 35 | "@angular/material": "^2.0.0-beta.10", 36 | "@angular/platform-browser": "^4.3.6", 37 | "@angular/platform-browser-dynamic": "^4.3.6", 38 | "@angular/platform-server": "^4.3.6", 39 | "@angular/router": "^4.3.6", 40 | "@ngrx/core": "^1.2.0", 41 | "@ngrx/store": "^4.0.0", 42 | "copy-webpack-plugin": "^4.0.1", 43 | "core-js": "^2.5.1", 44 | "electron": "^1.8.0", 45 | "hammerjs": "^2.0.8", 46 | "rxjs": "^5.4.3", 47 | "webpack-target-electron-renderer": "^0.4.0", 48 | "zone.js": "^0.8.17" 49 | }, 50 | "devDependencies": { 51 | "@types/hammerjs": "^2.0.35", 52 | "@types/jasmine": "^2.5.54", 53 | "@types/moment-timezone": "^0.5.0", 54 | "@types/node": "^8.0.28", 55 | "@types/source-map": "^0.5.1", 56 | "@types/uglify-js": "^2.0.27", 57 | "@types/webpack": "^3.0.10", 58 | "angular2-template-loader": "^0.6.0", 59 | "awesome-typescript-loader": "^3.2.3", 60 | "codelyzer": "^3.1.2", 61 | "css-loader": "^0.28.7", 62 | "electron-packager": "^9.0.0", 63 | "es6-promise-loader": "^1.0.1", 64 | "extract-text-webpack-plugin": "^3.0.0", 65 | "file-loader": "^0.11.0", 66 | "imports-loader": "^0.7.0", 67 | "istanbul-instrumenter-loader": "^3.0.0", 68 | "jasmine-core": "^2.8.0", 69 | "json-loader": "^0.5.7", 70 | "karma": "^1.7.1", 71 | "karma-chrome-launcher": "^2.0.0", 72 | "karma-coverage": "^1.0.0", 73 | "karma-jasmine": "^1.0.2", 74 | "karma-mocha-reporter": "^2.2.4", 75 | "karma-phantomjs-launcher": "^1.0.0", 76 | "karma-remap-coverage": "^0.1.4", 77 | "karma-sourcemap-loader": "^0.3.7", 78 | "karma-webpack": "2.0.5", 79 | "node-sass": "^4.0.0", 80 | "phantomjs-polyfill": "0.0.2", 81 | "phantomjs-prebuilt": "^2.1.15", 82 | "raw-loader": "1.0.0-beta.0", 83 | "reflect-metadata": "0.1.10", 84 | "remap-istanbul": "^0.9.0", 85 | "rimraf": "^2.5.2", 86 | "sass-loader": "^6.0.1", 87 | "source-map-loader": "^0.2.1", 88 | "style-loader": "^0.19.0", 89 | "svg-url-loader": "^2.1.1", 90 | "ts-helpers": "^1.1.1", 91 | "tsconfig-lint": "^0.12.0", 92 | "tslint": "^5.7.0", 93 | "tslint-loader": "^3.2.0", 94 | "typescript": "~2.6.1", 95 | "url-loader": "^0.6.1", 96 | "webpack": "2.3.3", 97 | "webpack-dev-middleware": "^1.12.0", 98 | "webpack-dev-server": "2.9.3" 99 | }, 100 | "engines": { 101 | "node": ">= 4.2.1 <= 6", 102 | "npm": ">= 3" 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | import { HomeComponent } from './components/home/home.component'; 3 | import { LoginComponent } from './components/login/login.component'; 4 | 5 | export const routes: Routes = [ 6 | { path: '', component: LoginComponent }, 7 | { path: 'home', component: HomeComponent }, 8 | { path: 'login', component: LoginComponent }, 9 | ]; 10 | -------------------------------------------------------------------------------- /src/app/app.ts: -------------------------------------------------------------------------------- 1 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 2 | /* 3 | * Angular Modules 4 | */ 5 | import { enableProdMode, NgModule, Component } from '@angular/core'; 6 | import { BrowserModule } from '@angular/platform-browser'; 7 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 8 | import { LocationStrategy, HashLocationStrategy } from '@angular/common'; 9 | import { RouterModule, Router } from '@angular/router'; 10 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 11 | import { HttpModule } from '@angular/http'; 12 | 13 | 14 | // Setup redux with ngrx 15 | import { Store, StoreModule } from '@ngrx/store'; 16 | import { reducers, initialState } from './store/index'; 17 | 18 | /** 19 | * Import our child components 20 | */ 21 | import { LoginComponent } from './components/login/login.component'; 22 | import { HomeComponent } from './components/home/home.component'; 23 | import { AppComponent } from './components/app.component'; 24 | 25 | /** 26 | * Import material UI Components 27 | */ 28 | import { MdButtonModule, MdSlideToggleModule } from '@angular/material'; 29 | 30 | import { routes } from './app.routes'; 31 | 32 | /** 33 | * Import the authentication service to be injected into our component 34 | */ 35 | import { Authentication } from './services/authentication'; 36 | 37 | /* 38 | * provide('AppStore', { useValue: appStore }), 39 | */ 40 | @NgModule({ 41 | imports: [ 42 | BrowserModule, 43 | FormsModule, 44 | ReactiveFormsModule, 45 | HttpModule, 46 | BrowserAnimationsModule, 47 | MdButtonModule, 48 | MdSlideToggleModule, 49 | RouterModule.forRoot(routes, { useHash: true }), 50 | StoreModule.forRoot(reducers, initialState), 51 | ], 52 | providers: [Authentication], 53 | declarations: [AppComponent, HomeComponent, LoginComponent], 54 | bootstrap: [AppComponent] 55 | }) 56 | export class AppModule { } 57 | platformBrowserDynamic().bootstrapModule(AppModule); 58 | -------------------------------------------------------------------------------- /src/app/components/app.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Import decorators and services from angular 3 | */ 4 | import { Component, OnInit, ViewEncapsulation } from '@angular/core'; 5 | 6 | /* 7 | * App Component 8 | * Top Level Component 9 | */ 10 | @Component({ 11 | // The selector is what angular internally uses 12 | selector: 'ae-app', // 13 | styleUrls: ['./app.theme.scss'], 14 | encapsulation: ViewEncapsulation.None, 15 | template: ` 16 |
17 |
18 | 19 |
20 | 21 | Set Dark theme 22 | 23 |
24 |
25 | ` 26 | }) 27 | export class AppComponent implements OnInit { 28 | //component initialization 29 | isDarkTheme: boolean = false; 30 | 31 | ngOnInit() { 32 | //check authentication 33 | } 34 | 35 | checkAuthentication() { } 36 | } 37 | -------------------------------------------------------------------------------- /src/app/components/app.theme.scss: -------------------------------------------------------------------------------- 1 | @import '~@angular/material/theming'; 2 | 3 | // NOTE: Theming is currently experimental and not yet publically released! 4 | 5 | @include mat-core(); 6 | 7 | //Here the existing theme is being redfined 8 | $primary: mat-palette($mat-deep-purple); 9 | $accent: mat-palette($mat-amber, A200, A100, A400); 10 | 11 | $theme: mat-light-theme($primary, $accent); 12 | 13 | @include angular-material-theme($theme); 14 | 15 | // This is where you define your custom them 16 | 17 | // mat-palette takes, color, default, lighter and darker params 18 | .m2app-dark { 19 | $dark-primary: mat-palette($mat-cyan, 700, 500, 900); 20 | $dark-accent: mat-palette($mat-yellow, A200, A100, A400); 21 | $dark-warn: mat-palette($mat-amber, A200, A100, A400); 22 | 23 | $dark-theme: mat-dark-theme($dark-primary, $dark-accent, $dark-warn); 24 | 25 | @include angular-material-theme($dark-theme); 26 | } 27 | -------------------------------------------------------------------------------- /src/app/components/home/home.component.html: -------------------------------------------------------------------------------- 1 |
2 |

{{messageForm.controls['messageText'].value}}

3 |
4 |
5 | 6 |
-------------------------------------------------------------------------------- /src/app/components/home/home.component.scss: -------------------------------------------------------------------------------- 1 | div { 2 | & > input { 3 | color: blue; 4 | } 5 | } 6 | 7 | // Fix for warning with electron - see https://github.com/electron/electron/issues/4420 8 | ::selection { 9 | background:rgba(255, 255, 125, 0.99); 10 | } -------------------------------------------------------------------------------- /src/app/components/home/home.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Import decorators and services from angular 3 | */ 4 | import { Component, OnInit } from '@angular/core'; 5 | import { FormControl, FormGroup } from '@angular/forms'; 6 | /** 7 | * Import the ngrx configured store 8 | */ 9 | import { Store } from '@ngrx/store'; 10 | import { State } from '../../store/index'; 11 | 12 | import * as path from 'path'; 13 | 14 | // Allow us to use Notification API here. 15 | declare var Notification: any; 16 | 17 | @Component({ 18 | selector: 'ae-home', 19 | templateUrl: './home.component.html', 20 | styleUrls: ['./home.component.scss'], 21 | }) 22 | export class HomeComponent implements OnInit { 23 | name: string; 24 | 25 | messageForm = new FormGroup({ 26 | messageText: new FormControl('Angular2'), 27 | }); 28 | 29 | constructor(public store: Store) { } 30 | 31 | ngOnInit() { 32 | let state = this.store.select('user').subscribe((userState: any) => { 33 | this.name = userState.username; 34 | }); 35 | } 36 | doNotify() { 37 | let message = { 38 | title: "Content-Image Notification", 39 | body: "Short message plus a custom content image", 40 | }; 41 | new Notification(message.title, message); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/app/components/home/home.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | async, 3 | inject, 4 | TestBed, 5 | } from '@angular/core/testing'; 6 | import { Component } from '@angular/core'; 7 | import { HomeComponent } from './home.component'; 8 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 9 | 10 | // Setup redux with ngrx 11 | import { Store, StoreModule } from '@ngrx/store'; 12 | import { reducers, initialState } from './../../store/index'; 13 | 14 | describe('App component', () => { 15 | beforeEach(() => TestBed.configureTestingModule({ 16 | imports: [ 17 | FormsModule, 18 | ReactiveFormsModule, 19 | StoreModule.forRoot(reducers, initialState), 20 | ], 21 | providers: [ 22 | HomeComponent, 23 | ], 24 | })); 25 | 26 | it('should have default data', inject([HomeComponent], (home: HomeComponent) => { 27 | expect(home.messageForm.controls['messageText'].value).toEqual('Angular2'); 28 | })); 29 | }); 30 | -------------------------------------------------------------------------------- /src/app/components/login/login.component.html: -------------------------------------------------------------------------------- 1 |
2 | Authenticate 3 | 4 |
5 | -------------------------------------------------------------------------------- /src/app/components/login/login.component.scss: -------------------------------------------------------------------------------- 1 | div { 2 | & > button { 3 | color: blue; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/app/components/login/login.component.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Import decorators and services from angular 3 | */ 4 | import { Component, NgZone, OnInit } from '@angular/core'; 5 | import { Router } from '@angular/router'; 6 | 7 | /** 8 | * Import the ngrx configured store 9 | */ 10 | import { Store } from '@ngrx/store'; 11 | import { State } from '../../store/index'; 12 | 13 | import { User } from './../../store/models/auth.model'; 14 | 15 | /** 16 | * Import the authentication service to be injected into our component 17 | */ 18 | import { Authentication } from '../../services/authentication'; 19 | 20 | @Component({ 21 | selector: 'ae-login', 22 | templateUrl: './login.component.html', 23 | styleUrls: ['./login.component.scss'], 24 | }) 25 | export class LoginComponent implements OnInit { 26 | unsubscribe: any; 27 | authenticated: boolean; 28 | 29 | //Inject Authentication service on construction 30 | constructor(private _router: Router, private _ngZone: NgZone, private auth: Authentication, public store: Store) { } 31 | 32 | ngOnInit() { 33 | this.checkAuth(); 34 | 35 | this.store.map((state: State) => state.user).subscribe((userState: User) => { 36 | this.authenticated = userState.authenticated; 37 | //Because the BrowserWindow runs outside angular for some reason we need to call Zone.run() 38 | this._ngZone.run(() => { 39 | if (userState.username != '') { 40 | this._router.navigate(['home']); 41 | } 42 | }); 43 | }); 44 | } 45 | 46 | /** 47 | * Checks for authentication 48 | * If existing auth in localstorage just gets the user data immediately 49 | */ 50 | checkAuth() { 51 | let storageToken = window.localStorage.getItem('authToken'); 52 | if (storageToken) { 53 | this.auth.requestUserData(storageToken); 54 | } 55 | } 56 | 57 | authenticate() { 58 | this.auth.githubHandShake(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Angular 2 Electron 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 | Loading... 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "0.0.1", 4 | "main": "main.js" 5 | } -------------------------------------------------------------------------------- /src/app/services/authentication.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Include angular2 dependencies including HTTP dependencies 3 | * and Injectable and Inject 4 | */ 5 | import { Injectable } from '@angular/core'; 6 | import { Http, Headers } from '@angular/http'; 7 | 8 | /** 9 | * Import the ngrx configured store 10 | */ 11 | import { Store } from '@ngrx/store'; 12 | import { State } from './../store/index'; 13 | import { AUTH_ACTION_TYPES } from './../store/actions/user.actions'; 14 | 15 | // Our custom actions. 16 | import { GithubAuth, ChangeName } from './../store/actions/user.actions'; 17 | 18 | /** 19 | * Include electron browser so that a new windows can be triggered for auth 20 | * Information about browserWindow on electron 21 | * https://github.com/electron/electron/blob/master/docs/api/browser-window.md 22 | */ 23 | const remote = require('electron').remote; 24 | const BrowserWindow = remote.BrowserWindow; 25 | 26 | /** 27 | * Basic configuration like Endpoint URL's, API version.. 28 | */ 29 | const options = require('./../config.json'); 30 | 31 | @Injectable() 32 | export class Authentication { 33 | authWindow: any; 34 | http: Http; 35 | 36 | //Inject the store to make sure state changes go through the store 37 | constructor(public store: Store, http: Http) { 38 | //authenticate and call the store to update the token 39 | const webPreferences = { 40 | nodeIntegration: false 41 | } 42 | this.authWindow = new BrowserWindow({ width: 800, height: 600, show: false, webPreferences }); 43 | this.http = http; 44 | } 45 | 46 | /** 47 | * Fires the Github Auth process by calling the github api with 48 | * https://github.com/login/oauth/authorize 49 | * 50 | * Listens to specific redirects ont he BrowserWindow object to handle the callback from envato 51 | * On will-navigate and did-get-redirect-request methods invocation will call the handleGitHubCallback(url) 52 | * with the url to make sure a code was received 53 | * 54 | * OnClose will reset the browserWindow object 55 | */ 56 | githubHandShake() { 57 | 58 | // Build the OAuth consent page URL 59 | let githubUrl = 'https://github.com/login/oauth/authorize?'; 60 | let authUrl = githubUrl + 'client_id=' + options.github.client_id + '&scope=' + options.github.scopes; 61 | this.authWindow.loadURL(authUrl); 62 | this.authWindow.show(); 63 | 64 | // Handle the response from GitHub 65 | this.authWindow.webContents.on('will-navigate', (event, url) => { 66 | this.handleGitHubCallback(url); 67 | }); 68 | 69 | this.authWindow.webContents.on('did-get-redirect-request', (event, oldUrl, newUrl) => { 70 | this.handleGitHubCallback(newUrl); 71 | }); 72 | 73 | // Reset the authWindow on close 74 | this.authWindow.on('close', function () { 75 | this.authWindow = null; 76 | }, false); 77 | } 78 | 79 | /** 80 | * Handles the callback from the browserWindow object 81 | * Checks for a code in the url and a refresh token received. When token and refresh 82 | * token are received calls requestGithubToken 83 | * 84 | * @param {string} url 85 | * The url that was just called by one of the events : 86 | * will-navigate 87 | * did-get-redirect-request 88 | * 89 | */ 90 | handleGitHubCallback(url) { 91 | let raw_code = /code=([^&]*)/.exec(url) || null; 92 | let code = (raw_code && raw_code.length > 1) ? raw_code[1] : null; 93 | let error = /\?error=(.+)$/.exec(url); 94 | 95 | if (code || error) { 96 | // Close the browser if code found or error 97 | this.authWindow.destroy(); 98 | } 99 | 100 | // If there is a code, proceed to get token from github 101 | if (code) { 102 | this.requestGithubToken(options.github, code); 103 | } else if (error) { 104 | alert('Oops! Something went wrong and we couldn\'t' + 105 | 'log you in using Github. Please try again.'); 106 | } 107 | } 108 | 109 | /** 110 | * Requests a git token from the github api given the 111 | * code received in the authentication step before 112 | * 113 | * @param {Object} githubOptions 114 | * The options to be sent to this request (received from the config file) 115 | * 116 | * @param {string} githubCode 117 | * The code received by the authentication method 118 | */ 119 | requestGithubToken(githubOptions, githubCode) { 120 | let creds = 'client_id=' + githubOptions.client_id + '&client_secret=' + githubOptions.client_secret + '&code=' + githubCode; 121 | 122 | let headers = new Headers(); 123 | headers.append('Accept', 'application/json'); 124 | 125 | this.http.post('https://github.com/login/oauth/access_token?' + creds, '', { headers: headers }) 126 | .subscribe( 127 | response => { 128 | //call the store to update the authToken 129 | let body_object = JSON.parse(response['_body']); 130 | this.requestUserData(body_object.access_token); 131 | }, 132 | err => console.log(err), 133 | () => console.log('Authentication Complete') 134 | ); 135 | 136 | } 137 | 138 | /** 139 | * API Request to get information of a user from the Github API 140 | * 141 | * @param {string} token 142 | * The token to be used in the request 143 | */ 144 | requestUserData(token) { 145 | //set the token 146 | this.store.dispatch(new GithubAuth({ 147 | 'token': token 148 | })); 149 | 150 | let headers = new Headers(); 151 | headers.append('Accept', 'application/json'); 152 | 153 | this.http.get('https://api.github.com/user?access_token=' + token, { headers: headers }) 154 | .subscribe( 155 | response => { 156 | //call the store to update the authToken 157 | let body_object = JSON.parse(response['_body']); 158 | console.log(body_object); 159 | this.store.dispatch(new ChangeName({ 160 | 'username': body_object.name 161 | })); 162 | }, 163 | err => console.log(err), 164 | () => console.log('Request Complete') 165 | ); 166 | } 167 | 168 | } 169 | -------------------------------------------------------------------------------- /src/app/store/actions/user.actions.ts: -------------------------------------------------------------------------------- 1 | import { Action } from '@ngrx/store'; 2 | 3 | export const AUTH_ACTION_TYPES = { 4 | GITHUB_AUTH: 'GITHUB_AUTH', 5 | CHANGE_NAME: 'CHANGE_NAME', 6 | }; 7 | 8 | // actions 9 | export class GithubAuth implements Action { 10 | type: string = AUTH_ACTION_TYPES.GITHUB_AUTH; 11 | constructor(public payload: any) { } 12 | }; 13 | 14 | export class ChangeName implements Action { 15 | type: string = AUTH_ACTION_TYPES.CHANGE_NAME; 16 | constructor(public payload: any) { } 17 | }; 18 | 19 | export type Actions = GithubAuth | ChangeName; 20 | -------------------------------------------------------------------------------- /src/app/store/index.ts: -------------------------------------------------------------------------------- 1 | import { User } from './models/auth.model'; 2 | import { authInitialState, userReducer } from './reducers/user.reducer'; 3 | import { ActionReducerMap } from '@ngrx/store'; 4 | 5 | export interface State { 6 | user: User; 7 | }; 8 | 9 | export const initialState: State = { 10 | user: authInitialState, 11 | }; 12 | 13 | export const reducers: ActionReducerMap = { 14 | user: userReducer, 15 | }; 16 | -------------------------------------------------------------------------------- /src/app/store/models/auth.model.ts: -------------------------------------------------------------------------------- 1 | export interface User { 2 | authToken: any; 3 | username: string; 4 | authenticated: boolean; 5 | }; 6 | -------------------------------------------------------------------------------- /src/app/store/reducers/user.reducer.ts: -------------------------------------------------------------------------------- 1 | import { AUTH_ACTION_TYPES, Actions } from './../actions/user.actions'; 2 | import { User } from './../models/auth.model'; 3 | 4 | export const authInitialState = { 5 | authToken: window.localStorage.getItem('authToken') || false, 6 | authenticated: false, 7 | username: '', 8 | }; 9 | 10 | export function userReducer(state: User = authInitialState, action: Actions) { 11 | 12 | switch (action.type) { 13 | 14 | case AUTH_ACTION_TYPES.GITHUB_AUTH: 15 | localStorage.setItem('authToken', action.payload.token); 16 | return Object.assign(state, { authToken: action.payload.token, authenticated: true }); 17 | 18 | case AUTH_ACTION_TYPES.CHANGE_NAME: 19 | return Object.assign(state, { username: action.payload.username }); 20 | 21 | default: 22 | return state; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /src/assets/app-icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaogarin/angular-electron/9e722afcb23b86b6536cd8d7a67e44a3bb6459e0/src/assets/app-icon.icns -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | // Polyfills 2 | // (these modules are what are in 'angular2/bundles/angular2-polyfills' so don't use that here) 3 | 4 | // import 'ie-shim'; // Internet Explorer 5 | // import 'es6-shim'; 6 | // import 'es6-promise'; 7 | // import 'es7-reflect-metadata'; 8 | 9 | // Prefer CoreJS over the polyfills above 10 | import 'core-js/es6'; 11 | import 'core-js/es7/reflect'; 12 | require('zone.js/dist/zone.js'); 13 | //Error['stackTraceLimit'] = Infinity; 14 | 15 | require('zone.js/dist/long-stack-trace-zone'); 16 | 17 | // RxJS 18 | // to include every operator uncomment 19 | // require('rxjs/Rx'); 20 | 21 | require('rxjs/add/operator/map'); 22 | require('rxjs/add/operator/mergeMap'); 23 | -------------------------------------------------------------------------------- /src/vendor.ts: -------------------------------------------------------------------------------- 1 | // For vendors for example jQuery, Lodash, angular2-jwt just import them here unless you plan on 2 | // chunking vendors files for async loading. You would need to import the async loaded vendors 3 | // at the entry point of the async loaded file. Also see custom-typings.d.ts as you also need to 4 | // run `typings install x` where `x` is your module 5 | 6 | // Angular 2 7 | import '@angular/platform-browser'; 8 | import '@angular/core'; 9 | import '@angular/router'; 10 | import '@angular/http'; 11 | 12 | // RxJS 13 | import 'rxjs/add/operator/map'; 14 | import 'rxjs/add/operator/filter'; 15 | import 'rxjs/add/operator/mergeMap'; 16 | 17 | // Hammer 18 | import 'hammerjs'; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "sourceMap": true, 10 | "strictNullChecks": false, 11 | "baseUrl": "./src", 12 | "paths": {}, 13 | "lib": [ 14 | "dom", 15 | "es6" 16 | ], 17 | "typeRoots": [ 18 | "node_modules/@types" 19 | ] 20 | }, 21 | "exclude": [ 22 | "node_modules", 23 | "dist" 24 | ], 25 | "awesomeTypescriptLoaderOptions": { 26 | "forkChecker": true, 27 | "useWebpackText": true 28 | }, 29 | "compileOnSave": false, 30 | "buildOnSave": false, 31 | "atom": { 32 | "rewriteTsconfig": false 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "directive-selector-name": [true, "camelCase"], 7 | "component-selector-name": [true, "kebab-case"], 8 | "directive-selector-type": [true, "attribute"], 9 | "component-selector-type": [true, "element"], 10 | "directive-selector-prefix": [true, "ae"], 11 | "component-selector-prefix": [true, "ae"], 12 | "use-input-property-decorator": true, 13 | "use-output-property-decorator": true, 14 | "use-host-property-decorator": true, 15 | "no-attribute-parameter-decorator": true, 16 | "no-input-rename": true, 17 | "no-output-rename": true, 18 | "no-forward-ref" :true, 19 | "use-life-cycle-interface": true, 20 | "use-pipe-transform-interface": true, 21 | "pipe-naming": [true, "camelCase", "sg"], 22 | "component-class-suffix": true, 23 | "directive-class-suffix": true, 24 | "import-destructuring-spacing": true, 25 | "class-name": true, 26 | "curly": false, 27 | "eofline": true, 28 | "indent": [ 29 | true, 30 | "spaces" 31 | ], 32 | "max-line-length": [ 33 | true, 34 | 300 35 | ], 36 | "member-ordering": [ 37 | true, 38 | "public-before-private", 39 | "static-before-instance", 40 | "variables-before-functions" 41 | ], 42 | "no-arg": true, 43 | "no-construct": true, 44 | "no-duplicate-key": true, 45 | "no-duplicate-variable": true, 46 | "no-empty": false, 47 | "no-eval": true, 48 | "trailing-comma": true, 49 | "no-trailing-whitespace": false, 50 | "no-unused-expression": true, 51 | "no-unused-variable": false, 52 | "no-unreachable": true, 53 | "no-use-before-declare": true, 54 | "one-line": [ 55 | true, 56 | "check-open-brace", 57 | "check-catch", 58 | "check-else", 59 | "check-whitespace" 60 | ], 61 | "quotemark": [ 62 | true, 63 | "single" 64 | ], 65 | "semicolon": true, 66 | "triple-equals": [ 67 | false, 68 | "allow-null-check" 69 | ], 70 | "variable-name": false, 71 | "whitespace": [ 72 | true, 73 | "check-branch", 74 | "check-decl", 75 | "check-operator", 76 | "check-separator", 77 | "check-type" 78 | ] 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "mode": "modules", 3 | "out": "doc", 4 | "theme": "default", 5 | "ignoreCompilerErrors": "true", 6 | "experimentalDecorators": "true", 7 | "emitDecoratorMetadata": "true", 8 | "target": "ES5", 9 | "moduleResolution": "node", 10 | "preserveConstEnums": "true", 11 | "stripInternal": "true", 12 | "suppressExcessPropertyErrors": "true", 13 | "suppressImplicitAnyIndexErrors": "true", 14 | "module": "commonjs" 15 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | // @joaogarin 2 | 3 | /* 4 | * Helper: root(), and rootDir() are defined at the bottom 5 | */ 6 | const webpack = require('webpack'); 7 | const helpers = require('./helpers'); 8 | const path = require('path'); 9 | 10 | var CopyWebpackPlugin = require('copy-webpack-plugin'); 11 | const LoaderOptionsPlugin = require('webpack/lib/LoaderOptionsPlugin'); 12 | 13 | /* 14 | * Config 15 | */ 16 | var config = { 17 | // for faster builds use 'eval' 18 | devtool: 'source-map', 19 | // cache: false, 20 | 21 | // our angular app 22 | entry: { 23 | 'polyfills': './src/polyfills.ts', 24 | 'vendor': './src/vendor.ts', 25 | 'app': './src/app/app', 26 | }, 27 | 28 | // Config for our build files 29 | output: { 30 | path: helpers.root('src/app/dist'), 31 | filename: '[name].js', 32 | sourceMapFilename: '[name].map', 33 | chunkFilename: '[id].chunk.js' 34 | }, 35 | /* 36 | * Options affecting the resolving of modules. 37 | * 38 | * See: http://webpack.github.io/docs/configuration.html#resolve 39 | */ 40 | resolve: { 41 | /* 42 | * An array of extensions that should be used to resolve modules. 43 | * 44 | * See: http://webpack.github.io/docs/configuration.html#resolve-extensions 45 | */ 46 | extensions: ['.ts', '.js', '.json', '.css', '.html'], 47 | 48 | // An array of directory names to be resolved to the current directory 49 | modules: [helpers.root('src'), 'node_modules'], 50 | 51 | }, 52 | /* 53 | * Options affecting the resolving of modules. 54 | * 55 | * See: http://webpack.github.io/docs/configuration.html#resolve 56 | */ 57 | module: { 58 | rules: [ 59 | // Support for .ts files. 60 | { 61 | test: /\.ts$/, 62 | loaders: ['awesome-typescript-loader', 'angular2-template-loader'], 63 | exclude: [/\.(spec|e2e)\.ts$/] 64 | }, 65 | 66 | // Support for *.json files. 67 | { 68 | test: /\.json$/, 69 | loader: 'json-loader' 70 | }, 71 | { 72 | test: /\.scss$/, 73 | exclude: /node_modules/, 74 | loaders: ['raw-loader', 'sass-loader'] // sass-loader not scss-loader 75 | }, 76 | 77 | // support for .html antd .css as raw text 78 | { 79 | test: /\.html$/, 80 | loader: 'raw-loader', 81 | exclude: [helpers.root('app/index.html')] 82 | }, 83 | 84 | // support for fonts 85 | { 86 | test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/, 87 | loader: 'file-loader?name=dist/[name]-[hash].[ext]' 88 | }, 89 | 90 | // support for svg icons 91 | { 92 | test: /\.svg/, 93 | loader: 'svg-url-loader' 94 | } 95 | ] 96 | }, 97 | plugins: [ 98 | 99 | // Plugin: CommonsChunkPlugin 100 | // Description: Shares common code between the pages. 101 | // It identifies common modules and put them into a commons chunk. 102 | // 103 | // See: https://webpack.github.io/docs/list-of-plugins.html#commonschunkplugin 104 | // See: https://github.com/webpack/docs/wiki/optimization#multi-page-app 105 | new webpack.optimize.CommonsChunkPlugin({ name: ['vendor', 'polyfills'], minChunks: Infinity }), 106 | // Plugin: CopyWebpackPlugin 107 | // Description: Copy files and directories in webpack. 108 | // 109 | // Copies project static assets. 110 | // 111 | // See: https://www.npmjs.com/package/copy-webpack-plugin 112 | new CopyWebpackPlugin([{ from: 'src/assets', to: 'assets' }]), 113 | /** 114 | * Plugin LoaderOptionsPlugin (experimental) 115 | * 116 | * See: https://gist.github.com/sokra/27b24881210b56bbaff7 117 | */ 118 | new LoaderOptionsPlugin({ 119 | debug: true, 120 | options: { 121 | /** 122 | * Static analysis linter for TypeScript advanced options configuration 123 | * Description: An extensible linter for the TypeScript language. 124 | * 125 | * See: https://github.com/wbuchwalter/tslint-loader 126 | */ 127 | tslint: { 128 | emitErrors: false, 129 | failOnHint: false, 130 | resourcePath: 'src' 131 | }, 132 | } 133 | }), 134 | ], 135 | // we need this due to problems with es6-shim 136 | node: { 137 | global: true, 138 | progress: false, 139 | crypto: 'empty', 140 | module: false, 141 | clearImmediate: false, 142 | setImmediate: false 143 | } 144 | }; 145 | 146 | /** 147 | * Target Electron 148 | */ 149 | config.target = 'electron-renderer'; 150 | module.exports = config; 151 | -------------------------------------------------------------------------------- /webpack.test.js: -------------------------------------------------------------------------------- 1 | const helpers = require('./helpers'); 2 | 3 | /** 4 | * Webpack Plugins 5 | */ 6 | const ProvidePlugin = require('webpack/lib/ProvidePlugin'); 7 | const DefinePlugin = require('webpack/lib/DefinePlugin'); 8 | const LoaderOptionsPlugin = require('webpack/lib/LoaderOptionsPlugin'); 9 | const ContextReplacementPlugin = require('webpack/lib/ContextReplacementPlugin'); 10 | 11 | /** 12 | * Webpack Constants 13 | */ 14 | const ENV = process.env.ENV = process.env.NODE_ENV = 'test'; 15 | 16 | /** 17 | * Webpack configuration 18 | * 19 | * See: http://webpack.github.io/docs/configuration.html#cli 20 | */ 21 | module.exports = function (options) { 22 | return { 23 | 24 | /** 25 | * Source map for Karma from the help of karma-sourcemap-loader & karma-webpack 26 | * 27 | * Do not change, leave as is or it wont work. 28 | * See: https://github.com/webpack/karma-webpack#source-maps 29 | */ 30 | devtool: 'inline-source-map', 31 | 32 | /** 33 | * Options affecting the resolving of modules. 34 | * 35 | * See: http://webpack.github.io/docs/configuration.html#resolve 36 | */ 37 | resolve: { 38 | 39 | /** 40 | * An array of extensions that should be used to resolve modules. 41 | * 42 | * See: http://webpack.github.io/docs/configuration.html#resolve-extensions 43 | */ 44 | extensions: ['.ts', '.js'], 45 | 46 | /** 47 | * Make sure root is src 48 | */ 49 | modules: [helpers.root('src'), 'node_modules'] 50 | 51 | }, 52 | 53 | /** 54 | * Options affecting the normal modules. 55 | * 56 | * See: http://webpack.github.io/docs/configuration.html#module 57 | * 58 | * 'use:' revered back to 'loader:' as a temp. workaround for #1188 59 | * See: https://github.com/AngularClass/angular2-webpack-starter/issues/1188#issuecomment-262872034 60 | */ 61 | module: { 62 | 63 | rules: [ 64 | 65 | /** 66 | * Source map loader support for *.js files 67 | * Extracts SourceMaps for source files that as added as sourceMappingURL comment. 68 | * 69 | * See: https://github.com/webpack/source-map-loader 70 | */ 71 | { 72 | enforce: 'pre', 73 | test: /\.js$/, 74 | loader: 'source-map-loader', 75 | exclude: [ 76 | // these packages have problems with their sourcemaps 77 | helpers.root('node_modules/rxjs'), 78 | helpers.root('node_modules/@angular') 79 | ] 80 | }, 81 | 82 | /** 83 | * Typescript loader support for .ts and Angular 2 async routes via .async.ts 84 | * 85 | * See: https://github.com/s-panferov/awesome-typescript-loader 86 | */ 87 | { 88 | test: /\.ts$/, 89 | use: [ 90 | { 91 | loader: 'awesome-typescript-loader', 92 | query: { 93 | // use inline sourcemaps for "karma-remap-coverage" reporter 94 | sourceMap: false, 95 | inlineSourceMap: true, 96 | compilerOptions: { 97 | 98 | // Remove TypeScript helpers to be injected 99 | // below by DefinePlugin 100 | removeComments: true 101 | 102 | } 103 | }, 104 | }, 105 | 'angular2-template-loader' 106 | ], 107 | exclude: [/\.e2e\.ts$/] 108 | }, 109 | 110 | /** 111 | * Json loader support for *.json files. 112 | * 113 | * See: https://github.com/webpack/json-loader 114 | */ 115 | { 116 | test: /\.json$/, 117 | loader: 'json-loader', 118 | exclude: [helpers.root('src/index.html')] 119 | }, 120 | 121 | /** 122 | * Raw loader support for *.css files 123 | * Returns file content as string 124 | * 125 | * See: https://github.com/webpack/raw-loader 126 | */ 127 | { 128 | test: /\.css$/, 129 | loader: ['to-string-loader', 'css-loader'], 130 | exclude: [helpers.root('src/index.html')] 131 | }, 132 | 133 | /** 134 | * Raw loader support for *.scss files 135 | * 136 | * See: https://github.com/webpack/raw-loader 137 | */ 138 | { 139 | test: /\.scss$/, 140 | loader: ['raw-loader', 'sass-loader'], 141 | exclude: [helpers.root('src/index.html')] 142 | }, 143 | 144 | /** 145 | * Raw loader support for *.html 146 | * Returns file content as string 147 | * 148 | * See: https://github.com/webpack/raw-loader 149 | */ 150 | { 151 | test: /\.html$/, 152 | loader: 'raw-loader', 153 | exclude: [helpers.root('src/index.html')] 154 | }, 155 | 156 | /** 157 | * Instruments JS files with Istanbul for subsequent code coverage reporting. 158 | * Instrument only testing sources. 159 | * 160 | * See: https://github.com/deepsweet/istanbul-instrumenter-loader 161 | */ 162 | { 163 | enforce: 'post', 164 | test: /\.(js|ts)$/, 165 | loader: 'istanbul-instrumenter-loader', 166 | include: helpers.root('src'), 167 | exclude: [ 168 | /\.(e2e|spec)\.ts$/, 169 | /node_modules/ 170 | ] 171 | } 172 | 173 | ] 174 | }, 175 | 176 | /** 177 | * Add additional plugins to the compiler. 178 | * 179 | * See: http://webpack.github.io/docs/configuration.html#plugins 180 | */ 181 | plugins: [ 182 | 183 | /** 184 | * Plugin: DefinePlugin 185 | * Description: Define free variables. 186 | * Useful for having development builds with debug logging or adding global constants. 187 | * 188 | * Environment helpers 189 | * 190 | * See: https://webpack.github.io/docs/list-of-plugins.html#defineplugin 191 | */ 192 | // NOTE: when adding more properties make sure you include them in custom-typings.d.ts 193 | new DefinePlugin({ 194 | 'ENV': JSON.stringify(ENV), 195 | 'HMR': false, 196 | 'process.env': { 197 | 'ENV': JSON.stringify(ENV), 198 | 'NODE_ENV': JSON.stringify(ENV), 199 | 'HMR': false, 200 | } 201 | }), 202 | 203 | /** 204 | * Plugin: ContextReplacementPlugin 205 | * Description: Provides context to Angular's use of System.import 206 | * 207 | * See: https://webpack.github.io/docs/list-of-plugins.html#contextreplacementplugin 208 | * See: https://github.com/angular/angular/issues/11580 209 | */ 210 | new ContextReplacementPlugin( 211 | // The (\\|\/) piece accounts for path separators in *nix and Windows 212 | /angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/, 213 | helpers.root('src'), // location of your src 214 | { 215 | // your Angular Async Route paths relative to this root directory 216 | } 217 | ), 218 | 219 | /** 220 | * Plugin LoaderOptionsPlugin (experimental) 221 | * 222 | * See: https://gist.github.com/sokra/27b24881210b56bbaff7 223 | */ 224 | new LoaderOptionsPlugin({ 225 | debug: false, 226 | options: { 227 | // legacy options go here 228 | } 229 | }), 230 | 231 | ], 232 | /** 233 | * Include polyfills or mocks for various node stuff 234 | * Description: Node configuration 235 | * 236 | * See: https://webpack.github.io/docs/configuration.html#node 237 | */ 238 | node: { 239 | global: true, 240 | process: false, 241 | crypto: 'empty', 242 | module: false, 243 | clearImmediate: false, 244 | setImmediate: false 245 | } 246 | 247 | }; 248 | } 249 | --------------------------------------------------------------------------------