├── src
├── app
│ ├── package.json
│ ├── components
│ │ ├── login
│ │ │ ├── login.component.scss
│ │ │ ├── login.component.html
│ │ │ └── login.component.ts
│ │ ├── home
│ │ │ ├── home.component.scss
│ │ │ ├── home.component.html
│ │ │ ├── home.spec.ts
│ │ │ └── home.component.ts
│ │ ├── app.theme.scss
│ │ └── app.component.ts
│ ├── store
│ │ ├── models
│ │ │ └── auth.model.ts
│ │ ├── index.ts
│ │ ├── actions
│ │ │ └── user.actions.ts
│ │ └── reducers
│ │ │ └── user.reducer.ts
│ ├── app.routes.ts
│ ├── app.ts
│ ├── index.html
│ └── services
│ │ └── authentication.ts
├── assets
│ └── app-icon.icns
├── vendor.ts
└── polyfills.ts
├── .angulardoc.json
├── .gitattributes
├── .editorconfig
├── typedoc.json
├── tsconfig.json
├── .gitignore
├── helpers.js
├── main.js
├── package.js
├── README.md
├── config
└── spec-bundle.js
├── tslint.json
├── karma.conf.js
├── package.json
├── webpack.config.js
└── webpack.test.js
/src/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "0.0.1",
4 | "main": "main.js"
5 | }
--------------------------------------------------------------------------------
/.angulardoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "repoId": "24bb94d3-2d2c-4d68-b41f-381f61f02fea",
3 | "lastSync": 0
4 | }
--------------------------------------------------------------------------------
/src/app/components/login/login.component.scss:
--------------------------------------------------------------------------------
1 | div {
2 | & > button {
3 | color: blue;
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/assets/app-icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joaogarin/angular-electron/HEAD/src/assets/app-icon.icns
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/src/app/store/models/auth.model.ts:
--------------------------------------------------------------------------------
1 | export interface User {
2 | authToken: any;
3 | username: string;
4 | authenticated: boolean;
5 | };
6 |
--------------------------------------------------------------------------------
/src/app/components/login/login.component.html:
--------------------------------------------------------------------------------
1 |
2 | Authenticate
3 |
4 |
5 |
--------------------------------------------------------------------------------
/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.html:
--------------------------------------------------------------------------------
1 |
2 |
{{messageForm.controls['messageText'].value}}
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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/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';
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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.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/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 |
--------------------------------------------------------------------------------
/.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
--------------------------------------------------------------------------------
/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;
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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);
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | };
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------