1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-25 12:15:18 +01:00

stub out angular with webpack

This commit is contained in:
Kyle Spearrin 2018-04-03 22:14:54 -04:00
parent 848117ff86
commit 4d56d12ccb
19 changed files with 1225 additions and 38 deletions

1
.gitignore vendored
View File

@ -10,5 +10,6 @@ webfonts/
*.pem *.pem
*.zip *.zip
build/ build/
build2/
build.safariextension/ build.safariextension/
coverage/ coverage/

742
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,8 @@
"start:firefox": "web-ext run --source-dir ./dist/", "start:firefox": "web-ext run --source-dir ./dist/",
"dev": "gulp build && webpack --config webpack.dev.js", "dev": "gulp build && webpack --config webpack.dev.js",
"dev:watch": "gulp build && webpack --config webpack.dev.js --watch", "dev:watch": "gulp build && webpack --config webpack.dev.js --watch",
"dev2": "webpack --config webpack2.js",
"dev2:watch": "webpack --config webpack2.js --watch",
"prod": "gulp build && webpack --config webpack.prod.js", "prod": "gulp build && webpack --config webpack.prod.js",
"dist": "npm run prod && gulp dist", "dist": "npm run prod && gulp dist",
"dist:firefox": "npm run prod && gulp dist:firefox", "dist:firefox": "npm run prod && gulp dist:firefox",
@ -20,7 +22,18 @@
"test:watch": "karma start" "test:watch": "karma start"
}, },
"devDependencies": { "devDependencies": {
"@ngtools/webpack": "1.10.2",
"@types/angular": "^1.6.34",
"@types/chrome": "0.0.51",
"@types/jasmine": "^2.8.2", "@types/jasmine": "^2.8.2",
"@types/jquery": "^3.2.16",
"@types/lunr": "2.1.5",
"@types/node-forge": "0.6.10",
"@types/papaparse": "4.1.31",
"@types/safari-extension": "^0.0.27",
"@types/safari-extension-content": "^0.0.14",
"@types/tldjs": "1.7.1",
"@types/webcrypto": "^0.0.28",
"angular": "1.6.9", "angular": "1.6.9",
"angular-animate": "1.6.9", "angular-animate": "1.6.9",
"angular-sweetalert": "1.1.2", "angular-sweetalert": "1.1.2",
@ -59,11 +72,11 @@
"karma-typescript": "^3.0.8", "karma-typescript": "^3.0.8",
"less": "^3.0.0-alpha.3", "less": "^3.0.0-alpha.3",
"less-loader": "^4.0.5", "less-loader": "^4.0.5",
"lunr": "2.1.6",
"ng-infinite-scroll": "1.3.0", "ng-infinite-scroll": "1.3.0",
"ngclipboard": "1.1.1", "ngclipboard": "1.1.1",
"node-forge": "0.7.1", "node-sass": "^4.7.2",
"papaparse": "4.3.5", "papaparse": "4.3.5",
"sass-loader": "^6.0.6",
"style-loader": "^0.19.0", "style-loader": "^0.19.0",
"sweetalert": "1.1.3", "sweetalert": "1.1.3",
"tldjs": "2.0.0", "tldjs": "2.0.0",
@ -76,16 +89,23 @@
"webpack-merge": "^4.1.0" "webpack-merge": "^4.1.0"
}, },
"dependencies": { "dependencies": {
"@types/angular": "^1.6.34", "@angular/animations": "5.2.0",
"@types/chrome": "0.0.51", "@angular/common": "5.2.0",
"@types/jquery": "^3.2.16", "@angular/compiler": "5.2.0",
"@types/lunr": "2.1.5", "@angular/core": "5.2.0",
"@types/node-forge": "0.6.10", "@angular/forms": "5.2.0",
"@types/papaparse": "4.1.31", "@angular/http": "5.2.0",
"@types/safari-extension": "^0.0.27", "@angular/platform-browser": "5.2.0",
"@types/safari-extension-content": "^0.0.14", "@angular/platform-browser-dynamic": "5.2.0",
"@types/tldjs": "1.7.1", "@angular/router": "5.2.0",
"@types/webcrypto": "^0.0.28", "@angular/upgrade": "5.2.0",
"@uirouter/angularjs": "^1.0.10" "@uirouter/angularjs": "^1.0.10",
"angular2-toaster": "4.0.2",
"angulartics2": "5.0.1",
"core-js": "2.4.1",
"lunr": "2.1.6",
"node-forge": "0.7.1",
"rxjs": "5.5.6",
"zone.js": "0.8.19"
} }
} }

View File

@ -78,6 +78,7 @@
"webRequest", "webRequest",
"webRequestBlocking" "webRequestBlocking"
], ],
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"commands": { "commands": {
"autofill_login": { "autofill_login": {
"suggested_key": { "suggested_key": {

View File

@ -0,0 +1,3 @@
<form id="login-page" #form>
Login Form
</form>

View File

@ -0,0 +1,44 @@
import * as template from './login.component.html';
import {
Component,
ComponentFactoryResolver,
ViewChild,
ViewContainerRef,
} from '@angular/core';
import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { AuthResult } from 'jslib/models/domain/authResult';
import { AuthService } from 'jslib/abstractions/auth.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { SyncService } from 'jslib/abstractions/sync.service';
@Component({
selector: 'app-login',
template: template,
})
export class LoginComponent {
@ViewChild('environment', { read: ViewContainerRef }) environmentModal: ViewContainerRef;
email: string = '';
masterPassword: string = '';
showPassword: boolean = false;
formPromise: Promise<AuthResult>;
constructor(private router: Router, private analytics: Angulartics2,
private toasterService: ToasterService) { }
async submit() {
}
togglePassword() {
this.analytics.eventTrack.next({ action: 'Toggled Master Password on Login' });
this.showPassword = !this.showPassword;
document.getElementById('masterPassword').focus();
}
}

View File

@ -0,0 +1,21 @@
import { NgModule } from '@angular/core';
import {
RouterModule,
Routes,
} from '@angular/router';
import { LoginComponent } from './accounts/login.component';
const routes: Routes = [
{ path: '', redirectTo: '/login', pathMatch: 'full' },
{ path: 'login', component: LoginComponent },
];
@NgModule({
imports: [RouterModule.forRoot(routes, {
useHash: true,
/*enableTracing: true,*/
})],
exports: [RouterModule],
})
export class AppRoutingModule { }

View File

@ -0,0 +1,39 @@
import {
ToasterConfig,
ToasterContainerComponent,
} from 'angular2-toaster';
import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
import {
Component,
ComponentFactoryResolver,
NgZone,
OnDestroy,
OnInit,
Type,
ViewChild,
ViewContainerRef,
} from '@angular/core';
import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
@Component({
selector: 'app-root',
styles: [],
template: `
<toaster-container [toasterconfig]="toasterConfig"></toaster-container>
<router-outlet></router-outlet>`,
})
export class AppComponent {
toasterConfig: ToasterConfig = new ToasterConfig({
showCloseButton: true,
mouseoverTimerStop: true,
animation: 'flyRight',
limit: 5,
});
constructor(private angulartics2GoogleAnalytics: Angulartics2GoogleAnalytics, private analytics: Angulartics2,
private toasterService: ToasterService) { }
}

1
src/popup2/app.d.ts vendored Normal file
View File

@ -0,0 +1 @@
declare module '*.html';

44
src/popup2/app.module.ts Normal file
View File

@ -0,0 +1,44 @@
import 'core-js';
import 'zone.js/dist/zone';
import { ToasterModule } from 'angular2-toaster';
import { Angulartics2Module } from 'angulartics2';
import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
import { AppRoutingModule } from './app-routing.module';
import { ServicesModule } from './services/services.module';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AppComponent } from './app.component';
import { LoginComponent } from './accounts/login.component';
@NgModule({
imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
AppRoutingModule,
ServicesModule,
Angulartics2Module.forRoot([Angulartics2GoogleAnalytics], {
pageTracking: {
clearQueryParams: true,
},
}),
ToasterModule,
],
declarations: [
AppComponent,
LoginComponent,
],
entryComponents: [
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule { }

14
src/popup2/index.html Normal file
View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bitwarden</title>
<base href="">
</head>
<body>
<app-root>
<div id="loading"><i class="fa fa-spinner fa-spin fa-3x"></i></div>
</app-root>
</body>
</html>

15
src/popup2/main.ts Normal file
View File

@ -0,0 +1,15 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
// tslint:disable-next-line
require('../scss/popup.scss');
// tslint:disable-next-line
require('../scripts/duo.js');
import { AppModule } from './app.module';
//if (!isDev()) {
// enableProdMode();
//}
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -0,0 +1,31 @@
import { Injectable } from '@angular/core';
import {
CanActivate,
Router,
} from '@angular/router';
import { CryptoService } from 'jslib/abstractions/crypto.service';
import { MessagingService } from 'jslib/abstractions/messaging.service';
import { UserService } from 'jslib/abstractions/user.service';
@Injectable()
export class AuthGuardService implements CanActivate {
constructor(private cryptoService: CryptoService, private userService: UserService, private router: Router,
private messagingService: MessagingService) { }
async canActivate() {
const isAuthed = await this.userService.isAuthenticated();
if (!isAuthed) {
this.messagingService.send('logout');
return false;
}
const key = await this.cryptoService.getKey();
if (key == null) {
this.router.navigate(['lock']);
return false;
}
return true;
}
}

View File

@ -0,0 +1,33 @@
import { Injectable } from '@angular/core';
@Injectable()
export class BroadcasterService {
subscribers: Map<string, (message: any) => any> = new Map<string, (message: any) => any>();
send(message: any, id?: string) {
if (id != null) {
if (this.subscribers.has(id)) {
this.subscribers.get(id)(message);
}
return;
}
this.subscribers.forEach((value) => {
value(message);
});
}
subscribe(id: string, messageCallback: (message: any) => any) {
if (this.subscribers.has(id)) {
return;
}
this.subscribers.set(id, messageCallback);
}
unsubscribe(id: string) {
if (this.subscribers.has(id)) {
this.subscribers.delete(id);
}
}
}

View File

@ -0,0 +1,35 @@
import {
APP_INITIALIZER,
NgModule,
} from '@angular/core';
import { ToasterModule } from 'angular2-toaster';
import { AuthGuardService } from './auth-guard.service';
import { BroadcasterService } from './broadcaster.service';
import { ValidationService } from './validation.service';
function initFactory(): Function {
return async () => {
};
}
@NgModule({
imports: [
ToasterModule,
],
declarations: [],
providers: [
ValidationService,
AuthGuardService,
{
provide: APP_INITIALIZER,
useFactory: initFactory,
deps: [],
multi: true,
},
],
})
export class ServicesModule {
}

View File

@ -0,0 +1,37 @@
import { Injectable } from '@angular/core';
import { ToasterService } from 'angular2-toaster';
import { I18nService } from 'jslib/abstractions/i18n.service';
@Injectable()
export class ValidationService {
constructor(private toasterService: ToasterService, private i18nService: I18nService) { }
showError(data: any): string[] {
const defaultErrorMessage = this.i18nService.t('unexpectedError');
const errors: string[] = [];
if (data == null || typeof data !== 'object') {
errors.push(defaultErrorMessage);
} else if (data.validationErrors == null) {
errors.push(data.message ? data.message : defaultErrorMessage);
} else {
for (const key in data.validationErrors) {
if (!data.validationErrors.hasOwnProperty(key)) {
continue;
}
data.validationErrors[key].forEach((item: string) => {
errors.push(item);
});
}
}
if (errors.length > 0) {
this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'), errors[0]);
}
return errors;
}
}

3
src/scss/popup.scss Normal file
View File

@ -0,0 +1,3 @@
body {
color: black;
}

View File

@ -2,6 +2,8 @@
"compilerOptions": { "compilerOptions": {
"moduleResolution": "node", "moduleResolution": "node",
"noImplicitAny": true, "noImplicitAny": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"module": "es6", "module": "es6",
"target": "ES2016", "target": "ES2016",
"allowJs": true, "allowJs": true,
@ -20,6 +22,7 @@
"dist", "dist",
"jslib/dist", "jslib/dist",
"build", "build",
"build2",
"build.safariextension", "build.safariextension",
"coverage", "coverage",
"store", "store",

150
webpack2.js Normal file
View File

@ -0,0 +1,150 @@
const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const isVendorModule = (module) => {
if (!module.context) {
return false;
}
const nodeModule = module.context.indexOf('node_modules') !== -1;
const bitwardenModule = module.context.indexOf('@bitwarden') !== -1;
return nodeModule && !bitwardenModule;
};
const extractCss = new ExtractTextPlugin({
filename: '[name].css',
disable: false,
allChunks: true
});
module.exports = {
entry: {
'popup/main': './src/popup2/main.ts',
'background': './src/background.ts',
'content/autofill': './src/content/autofill.js',
'content/autofiller': './src/content/autofiller.js',
'content/notificationBar': './src/content/notificationBar.js',
'content/shortcuts': './src/content/shortcuts.js',
'downloader/downloader': './src/downloader/downloader.ts',
'2fa/2fa': './src/2fa/2fa.ts',
},
module: {
rules: [
{
test: /\.ts$/,
enforce: 'pre',
loader: 'tslint-loader'
},
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules\/(?!(@bitwarden)\/).*/
},
{
test: /\.(html)$/,
loader: 'html-loader'
},
{
test: /.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
exclude: /loading.svg/,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'popup/fonts/',
publicPath: './fonts/'
}
}]
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
exclude: /.*(fontawesome-webfont|glyphicons-halflings-regular)\.svg/,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'popup/images/',
publicPath: './images/'
}
}]
},
{
test: /\.scss$/,
use: extractCss.extract({
use: [
{
loader: 'css-loader',
},
{
loader: 'sass-loader',
}
],
publicPath: '../'
})
},
]
},
plugins: [
new CleanWebpackPlugin([
path.resolve(__dirname, 'build2/*')
]),
// ref: https://github.com/angular/angular/issues/20357
new webpack.ContextReplacementPlugin(
/\@angular(\\|\/)core(\\|\/)esm5/,
path.resolve(__dirname, './src')
),
new webpack.optimize.CommonsChunkPlugin({
name: 'popup/vendor',
chunks: ['popup/main'],
minChunks: isVendorModule
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
chunks: ['background'],
minChunks: isVendorModule
}),
new HtmlWebpackPlugin({
template: './src/popup2/index.html',
filename: 'popup/index.html',
chunks: ['popup/vendor', 'popup/main', 'fonts']
}),
new HtmlWebpackPlugin({
template: './src/background.html',
filename: 'background.html',
chunks: ['vendor', 'background']
}),
new HtmlWebpackPlugin({
template: './src/downloader/index.html',
filename: 'downloader/index.html',
chunks: ['downloader/downloader']
}),
new HtmlWebpackPlugin({
template: './src/2fa/index.html',
filename: '2fa/index.html',
chunks: ['2fa/2fa']
}),
new CopyWebpackPlugin([
'./src/manifest.json',
{ from: './src/_locales', to: '_locales' },
{ from: './src/edge', to: 'edge' },
{ from: './src/safari', to: 'safari' },
{ from: './src/images', to: 'images' },
{ from: './src/content/autofill.css', to: 'content' }
]),
extractCss
],
resolve: {
extensions: ['.tsx', '.ts', '.js'],
alias: {
jslib: path.join(__dirname, 'jslib/src')
}
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'build2')
}
};