diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..3ced40923 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,12 @@ +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + plugins: [ + '@typescript-eslint', + ], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + ], +}; diff --git a/Makefile b/Makefile index 4190b14d0..0be32c4b8 100644 --- a/Makefile +++ b/Makefile @@ -1,39 +1,26 @@ src = src poname = taler-wallet-webex -gulp = node_modules/gulp/bin/gulp.js tsc = node_modules/typescript/bin/tsc pogen = node_modules/pogen/bin/pogen.js typedoc = node_modules/typedoc/bin/typedoc -ava = node_modules/ava/cli.js +ava = node_modules/.bin/ava nyc = node_modules/nyc/bin/nyc.js -tslint = node_modules/tslint/bin/tslint include config.mk .PHONY: tsc -tsc: tsconfig.json yarn-install +tsc: yarn-install $(tsc) -.PHONY: webex-stable -webex-stable: i18n - $(gulp) stable - -.PHONY: webex-unstable -webex-unstable: i18n - $(gulp) unstable - -tsconfig.json: gulpfile.js yarn-install - $(gulp) tsconfig - .PHONY: dist dist: - $(gulp) srcdist + git archive --format=tar.gz HEAD -o taler-wallet.tar.gz # make documentation from docstrings .PHONY: typedoc typedoc: - $(typedoc) --out build/typedoc --readme README + $(typedoc) --out dist/typedoc --readme README .PHONY: clean clean: @@ -45,20 +32,23 @@ submodules-update: .PHONY: check check: tsc yarn-install - find dist/node -name '*-test.js' | xargs $(ava) + $(ava) .PHONY: coverage coverage: tsc yarn-install $(nyc) --all $(ava) 'build/**/*-test.js' -.PHONY: lint -lint: tsc yarn-install - $(tslint) -e src/i18n/strings.ts --project tsconfig.json -t verbose 'src/**/*.ts' 'src/**/*.tsx' - .PHONY: yarn-install yarn-install: $(yarn) install +.PHONY: webextensions +webextensions: rollup + rm -rf dist/wx + mkdir dist/wx + cp webextension/manifest.json dist/wx/ + cp -r webextension/static/* dist/wx/ + cp -r dist/webextension/* dist/wx/ .PHONY: i18n i18n: yarn-install @@ -74,7 +64,7 @@ i18n: yarn-install msgmerge -o $$pofile $$pofile src/i18n/$(poname).pot; \ done; # generate .ts file containing all translations - $(gulp) po2js + ./contrib/po2ts # Some commands are only available when ./configure has been run @@ -90,20 +80,7 @@ install: tsc $(yarn) global add file://$(CURDIR) --prefix $(prefix) endif -.PHONY: watch -watch: tsconfig.json - ./node_modules/.bin/webpack --watch - .PHONY: rollup rollup: tsc ./node_modules/.bin/rollup -c -# Create the node_modules directory for the android wallet -package-android: - rm -rf dist/android - mkdir -p dist/android - yarn pack --filename dist/android/taler-wallet.tar.gz - cp contrib/package-android.json dist/android/package.json - cd dist/android && yarn install - #cd dist/android && npm install --global --prefix $(CURDIR)/dist/android $(CURDIR) - diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index 15f6ff10f..000000000 --- a/gulpfile.js +++ /dev/null @@ -1,352 +0,0 @@ -/* - This file is part of TALER - (C) 2015-2016 GNUnet e.V. - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, see - */ - -"use strict"; - -/** - * Run with - * $ gulp - * - * The important tasks are: - * - tsconfig: generate tsconfig.json file for - * development - * - package: create Chrome extension zip file in - * dist/. - * - * @author Florian Dold - */ - -const gulp = require("gulp"); -const map = require("map-stream"); -const zip = require("gulp-zip"); -const gzip = require("gulp-gzip"); -const rename = require("gulp-rename"); -const tar = require("gulp-tar"); -const glob = require("glob"); -const jsonTransform = require("gulp-json-transform"); -const fs = require("fs"); -const through = require("through2"); -const File = require("vinyl"); -const Stream = require("stream").Stream; -const vfs = require("vinyl-fs"); -const webpack = require("webpack"); -const po2json = require("po2json"); -const path = require("path"); - -const paths = { - ts: { - src: [ - "src/**/*.{ts,tsx,js}", - "!src/**/*-test*.ts", - ], - test: [ - "src/**/*-test*.ts", - ], - }, - // distributed in the chrome extension - dist: [ - "dist/*-bundle.js", - "dist/*-bundle.js.map", - "img/icon.png", - "img/logo.png", - "src/webex/**/*.{js,css,html}", - ], - // for the source distribution - extra: [ - "AUTHORS", - "COPYING", - "Makefile", - "README", - "configure", - "gulpfile.js", - "manifest.json", - "package.json", - "src/i18n/*.po", - "src/i18n/*.pot", - "src/i18n/poheader", - "src/i18n/strings-prelude", - "tooling/**", - "tsconfig.json", - "webpack.config.js", - ], -}; - - -const tsBaseArgs = { - target: "es6", - jsx: "react", - reactNamespace: "React", - experimentalDecorators: true, - module: "commonjs", - sourceMap: true, - lib: ["es6", "dom"], - noImplicitReturns: true, - noFallthroughCasesInSwitch: true, - strict: true, - strictPropertyInitialization: false, - outDir: "dist/node", - noImplicitAny: true, - noImplicitThis: true, - allowJs: true, - checkJs: true, - incremental: true, - esModuleInterop: true, -}; - - -const manifest = JSON.parse(fs.readFileSync("manifest.json", "utf8")); - - -// Concatenate node streams, -// taken from dominictarr's event-stream module -function concatStreams (...streams) { - var stream = new Stream(); - stream.setMaxListeners(0); // allow adding more than 11 streams - var endCount = 0; - stream.writable = stream.readable = true; - - streams.forEach(function (e) { - e.pipe(stream, {end: false}); - var ended = false; - e.on('end', function () { - if (ended) return; - ended = true; - endCount++; - if (endCount == streams.length) - stream.emit('end'); - }) - }); - stream.write = function (data) { - this.emit('data', data); - }; - stream.destroy = function () { - streams.forEach(function (e) { - if (e.destroy) e.destroy(); - }) - }; - return stream; -} - - - -function dist_prod() { - return vfs.src(paths.dist, {base: ".", stripBOM: false}) - .pipe(gulp.dest("dist/ext/")); -} - -function compile_prod(callback) { - let config = require("./webpack.config.js")({ mode: "production" }); - webpack(config, function(err, stats) { - if (err) { - throw new gutil.PluginError("webpack", err); - } - if (stats.hasErrors() || stats.hasWarnins) { - console.log("[webpack]", stats.toString({ - colors: true, - })); - } - if (stats.hasErrors()) { - callback(Error("webpack completed with errors")) - } else { - callback(); - } - }); -} - - -function manifest_stable() { - return gulp.src("manifest.json") - .pipe(jsonTransform((data) => { - data.name = "GNU Taler Wallet"; - return data; - }, 2)) - .pipe(gulp.dest("dist/ext/")); -} - - -function manifest_unstable() { - return gulp.src("manifest.json") - .pipe(jsonTransform((data) => { - data.name = "GNU Taler Wallet (unstable)"; - return data; - }, 2)) - .pipe(gulp.dest("dist/ext/")); -} - - -function package_stable () { - let basename = String.prototype.concat("taler-wallet-stable-", manifest.version_name, "-", manifest.version); - let zipname = basename + ".zip"; - let xpiname = basename + ".xpi"; - return gulp.src("dist/ext/**", {buffer: false, stripBOM: false}) - .pipe(zip(zipname)) - .pipe(gulp.dest("dist/")); -} - -function package_unstable () { - let basename = String.prototype.concat("taler-wallet-unstable-", manifest.version_name, "-", manifest.version); - let zipname = basename + ".zip"; - let xpiname = basename + ".xpi"; - return gulp.src("dist/ext/**", {buffer: false, stripBOM: false}) - .pipe(zip(zipname)) - .pipe(gulp.dest("dist/")); -} - - -/** - * Create source distribution. - */ -function srcdist() { - const name = String.prototype.concat("taler-wallet-webex-", manifest.version_name); - const opts = {buffer: false, stripBOM: false, base: "."}; - // We can't just concat patterns due to exclude patterns - const files = concatStreams( - gulp.src(paths.ts.src, opts), - gulp.src(paths.ts.test, opts), - gulp.src(paths.dist, opts), - gulp.src(paths.extra, opts)); - - return files - .pipe(rename(function (p) { p.dirname = name + "/" + p.dirname; })) - .pipe(tar(name + "-src.tar")) - .pipe(gzip()) - .pipe(gulp.dest(".")); -} - - -/** - * Extract .po files from source code - */ -gulp.task("pogen", function (cb) { - throw Error("not yet implemented"); -}); - - -/** - * Generate a tsconfig.json with the - * given compiler options that compiles - * all files piped into it. - */ -function genTSConfig(confBase) { - let conf = { - compileOnSave: true, - compilerOptions: {}, - files: [] - }; - Object.assign(conf.compilerOptions, confBase); - return through.obj(function (file, enc, cb) { - conf.files.push(file.relative); - cb(); - }, function (cb) { - conf.files.sort(); - let x = JSON.stringify(conf, null, 2); - let f = new File({ - path: "tsconfig.json", - contents: Buffer.from(x), - }); - this.push(f); - cb(); - }); -} - - -/** - * Get the content of a Vinyl file object as a buffer. - */ -function readContentsBuffer(file, cb) { - if (file.isBuffer()) { - cb(file.contents); - return; - } - if (!file.isStream()) { - throw Error("file must be stream or buffer"); - } - const chunks = []; - file.contents.on("data", function (chunk) { - if (!Buffer.isBuffer(chunk)) { - throw Error("stream data must be a buffer"); - } - chunks.push(chunk); - }); - file.contents.on("end", function (chunk) { - cb(Buffer.concat(chunks)); - }); - file.contents.on("error", function (err) { - cb(undefined, err); - }); -} - - -/** - * Combine multiple translations (*.po files) into - * one JavaScript file. - */ -function pofilesToJs(targetPath) { - const outStream = through(); - const f = new File({ - path: targetPath, - contents: outStream, - }); - const prelude = fs.readFileSync("./src/i18n/strings-prelude"); - outStream.write(prelude); - return through.obj(function (file, enc, cb) { - console.log("processing file", file); - readContentsBuffer(file, function (buf, error) { - console.log("got contents"); - if (error) { - throw error; - } - const lang = path.basename(file.path, ".po"); - if (!lang) { - throw Error(); - } - console.log("lang", lang); - const pojson = po2json.parse(buf, {format: "jed1.x", fuzzy: true}); - outStream.write("strings['" + lang + "'] = " + JSON.stringify(pojson, null, " ") + ";\n"); - console.log("...done"); - cb(); - }); - }, function (cb) { - console.log("processing done"); - outStream.end(); - this.push(f); - cb(); - }); -} - - -function tsconfig() { - let opts = {base: "."}; - const files = concatStreams( - vfs.src(paths.ts.src, opts), - vfs.src(paths.ts.test, opts)); - return files.pipe(genTSConfig(tsBaseArgs)) - .pipe(gulp.dest(".")); -} - - -function po2js() { - return gulp.src("src/i18n/*.po", {base: "."}) - .pipe(pofilesToJs("src/i18n/strings.ts")) - .pipe(gulp.dest(".")); -} - - -exports.srcdist = srcdist -exports.tsconfig = tsconfig -exports.po2js = po2js -exports.stable = gulp.series(tsconfig, manifest_stable, compile_prod, dist_prod, package_stable) -exports.default = exports.stable diff --git a/package.json b/package.json index 85bc31a64..91e263af5 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "scripts": { "build": "make tsc", "pretty": "prettier --config .prettierrc --write src", - "test": "ava" + "test": "ava", + "coverage": "nyc ava" }, "files": [ "AUTHORS", @@ -29,52 +30,42 @@ "src/" ], "devDependencies": { - "@rollup/plugin-json": "^4.0.0", - "@types/chrome": "^0.0.91", - "@types/react": "^16.4.0", - "@types/react-dom": "^16.0.0", + "@ava/typescript": "^1.1.1", + "@rollup/plugin-json": "^4.0.2", + "@rollup/plugin-replace": "^2.3.1", + "@types/chrome": "^0.0.103", + "@types/react": "^16.9.6", + "@types/react-dom": "^16.9.6", + "@typescript-eslint/eslint-plugin": "^2.26.0", + "@typescript-eslint/parser": "^2.26.0", "ava": "^3.6.0", - "awesome-typescript-loader": "^5.2.1", - "glob": "^7.1.1", - "gulp": "^4.0.0", - "gulp-gzip": "^1.2.0", - "gulp-json-transform": "^0.4.2", - "gulp-rename": "^1.2.2", - "gulp-tar": "^3.0.0", - "gulp-zip": "^5.0.0", + "eslint": "^6.8.0", + "eslint-plugin-import": "^2.20.2", + "eslint-plugin-react": "^7.19.0", + "eslint-plugin-react-hooks": "^3.0.0", "jed": "^1.1.1", - "map-stream": "^0.0.7", "moment": "^2.18.1", - "nyc": "^14.1.1", - "po2json": "^1.0.0-alpha", + "nyc": "^15.0.1", + "po2json": "^0.4.5", "pogen": "^0.0.5", - "prettier": "^2.0.3", - "react": "^16.8.5", - "react-dom": "^16.8.5", - "rollup": "^1.27.8", + "prettier": "^2.0.4", + "react": "^16.13.1", + "react-dom": "^16.13.1", + "rollup": "^2.3.3", "rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-node-resolve": "^5.2.0", + "rollup-plugin-terser": "^5.3.0", "structured-clone": "^0.2.2", - "terser-webpack-plugin": "^2.2.1", - "through2": "3.0.1", - "tslint": "^5.19.0", - "typedoc": "^0.15.0", - "typescript": "^3.8.3", - "uglify-js": "^3.0.27", - "vinyl": "^2.2.0", - "vinyl-fs": "^3.0.3", - "webpack": "^4.39.3", - "webpack-bundle-analyzer": "^3.0.2", - "webpack-cli": "^3.1.0", - "webpack-merge": "^4.2.2" + "typedoc": "^0.17.4", + "typescript": "^3.8.3" }, "dependencies": { - "@ava/typescript": "^1.1.1", "axios": "^0.19.2", "big-integer": "^1.6.48", "idb-bridge": "^0.0.15", "qrcode-generator": "^1.4.3", - "source-map-support": "^0.5.12" + "source-map-support": "^0.5.12", + "tslib": "^1.11.1" }, "ava": { "files": [ diff --git a/rollup.config.js b/rollup.config.js index cfe0a4b8a..e88ad5ce3 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,56 +1,192 @@ // rollup.config.js -import commonjs from 'rollup-plugin-commonjs'; -import nodeResolve from 'rollup-plugin-node-resolve'; -import json from '@rollup/plugin-json'; -import builtins from 'builtin-modules' +import commonjs from "rollup-plugin-commonjs"; +import nodeResolve from "rollup-plugin-node-resolve"; +import json from "@rollup/plugin-json"; +import replace from "@rollup/plugin-replace"; +import builtins from "builtin-modules"; +import { terser } from "rollup-plugin-terser"; const walletCli = { - input: 'dist/node/headless/taler-wallet-cli.js', + input: "dist/node/headless/taler-wallet-cli.js", output: { - file: 'dist/standalone/taler-wallet-cli.js', - format: 'cjs' + file: "dist/standalone/taler-wallet-cli.js", + format: "cjs", }, external: builtins, plugins: [ + commonjs({ + include: ["node_modules/**", "src/**"], + extensions: [".js", ".ts"], + ignoreGlobal: false, // Default: false + sourceMap: false, + ignore: ["taler-wallet"], + }), nodeResolve({ preferBuiltins: true, }), - commonjs({ - include: ['node_modules/**', 'dist/node/**'], - extensions: [ '.js' ], - ignoreGlobal: false, // Default: false - sourceMap: false, - ignore: [ 'taler-wallet' ] - }), json(), - - ] + ], }; const walletAndroid = { - input: 'dist/node/android/index.js', + input: "dist/node/android/index.js", output: { - file: 'dist/standalone/taler-wallet-android.js', - format: 'cjs', - exports: 'named', + file: "dist/standalone/taler-wallet-android.js", + format: "cjs", + exports: "named", }, external: builtins, plugins: [ json(), nodeResolve({ - preferBuiltins: true + preferBuiltins: true, }), commonjs({ - include: ['node_modules/**', 'dist/node/**'], - extensions: [ '.js' ], - ignoreGlobal: false, // Default: false + include: ["node_modules/**", "dist/node/**"], + extensions: [".js"], + ignoreGlobal: false, // Default: false sourceMap: false, - ignore: [ 'taler-wallet' ] - }) - ] + ignore: ["taler-wallet"], + }), + ], }; -export default [walletCli, walletAndroid]; +const webExtensionPageEntryPoint = { + input: "dist/node/webex/pageEntryPoint.js", + output: { + file: "dist/webextension/pageEntryPoint.js", + format: "iife", + exports: "default", + name: "webExtensionPageEntry", + }, + external: builtins, + plugins: [ + json(), + + nodeResolve({ + preferBuiltins: true, + }), + + terser(), + + replace({ + "process.env.NODE_ENV": JSON.stringify("production"), + }), + + commonjs({ + include: ["node_modules/**", "dist/node/**"], + extensions: [".js"], + ignoreGlobal: false, // Default: false + sourceMap: false, + ignore: ["taler-wallet"], + }), + ], +}; + +const webExtensionBackgroundPageScript = { + input: "dist/node/webex/background.js", + output: { + file: "dist/webextension/background.js", + format: "iife", + exports: "default", + name: "webExtensionBackgroundScript", + }, + external: builtins, + plugins: [ + json(), + + nodeResolve({ + preferBuiltins: true, + }), + + terser(), + + replace({ + "process.env.NODE_ENV": JSON.stringify("production"), + }), + + commonjs({ + include: ["node_modules/**", "dist/node/**"], + extensions: [".js"], + ignoreGlobal: false, // Default: false + sourceMap: false, + ignore: ["taler-wallet", "crypto"], + }), + ], +}; + +const webExtensionCryptoWorker = { + input: "dist/node/crypto/workers/browserWorkerEntry.js", + output: { + file: "dist/webextension/browserWorkerEntry.js", + format: "iife", + exports: "default", + name: "webExtensionCryptoWorker", + }, + external: builtins, + plugins: [ + json(), + + nodeResolve({ + preferBuiltins: true, + }), + + terser(), + + replace({ + "process.env.NODE_ENV": JSON.stringify("production"), + }), + + commonjs({ + include: ["node_modules/**", "dist/node/**"], + extensions: [".js"], + ignoreGlobal: false, // Default: false + sourceMap: false, + ignore: ["taler-wallet", "crypto"], + }), + ], +}; + +const webExtensionContentScript = { + input: "dist/node/webex/notify.js", + output: { + file: "dist/webextension/contentScript.js", + format: "iife", + exports: "default", + name: "webExtensionContentScript", + }, + external: builtins, + plugins: [ + json(), + + nodeResolve({ + preferBuiltins: true, + }), + + terser(), + + replace({ + "process.env.NODE_ENV": JSON.stringify("production"), + }), + + commonjs({ + include: ["node_modules/**", "dist/node/**"], + extensions: [".js"], + ignoreGlobal: false, // Default: false + sourceMap: false, + ignore: ["taler-wallet"], + }), + ], +}; + +export default [ + walletCli, + walletAndroid, + webExtensionPageEntryPoint, + webExtensionBackgroundPageScript, + webExtensionCryptoWorker, + webExtensionContentScript, +]; diff --git a/src/android/index.ts b/src/android/index.ts index a42b55a2c..e984ce4db 100644 --- a/src/android/index.ts +++ b/src/android/index.ts @@ -24,7 +24,7 @@ import { DefaultNodeWalletArgs, } from "../headless/helpers"; import { openPromise, OpenedPromise } from "../util/promiseUtils"; -import fs = require("fs"); +import fs from "fs"; import { HttpRequestLibrary, HttpResponse, diff --git a/src/crypto/primitives/kdf.ts b/src/crypto/primitives/kdf.ts index e1baed408..03deb3727 100644 --- a/src/crypto/primitives/kdf.ts +++ b/src/crypto/primitives/kdf.ts @@ -14,7 +14,7 @@ GNU Taler; see the file COPYING. If not, see */ -import nacl = require("./nacl-fast"); +import * as nacl from "./nacl-fast"; import { sha256 } from "./sha256"; export function sha512(data: Uint8Array): Uint8Array { diff --git a/src/crypto/primitives/nacl-fast.ts b/src/crypto/primitives/nacl-fast.ts index 14cf569db..23d8378b4 100644 --- a/src/crypto/primitives/nacl-fast.ts +++ b/src/crypto/primitives/nacl-fast.ts @@ -3078,16 +3078,16 @@ export function sign_ed25519_pk_to_curve25519( (function () { // Initialize PRNG if environment provides CSPRNG. // If not, methods calling randombytes will throw. - const crypto = + const cr = typeof self !== "undefined" ? self.crypto || (self as any).msCrypto : null; - if (crypto && crypto.getRandomValues) { + if (cr && cr.getRandomValues) { // Browsers. var QUOTA = 65536; setPRNG(function (x: Uint8Array, n: number) { var i, v = new Uint8Array(n); for (i = 0; i < n; i += QUOTA) { - crypto.getRandomValues(v.subarray(i, i + Math.min(n - i, QUOTA))); + cr.getRandomValues(v.subarray(i, i + Math.min(n - i, QUOTA))); } for (i = 0; i < n; i++) x[i] = v[i]; cleanup(v); diff --git a/src/crypto/talerCrypto-test.ts b/src/crypto/talerCrypto-test.ts index 85c22d608..11068f7d4 100644 --- a/src/crypto/talerCrypto-test.ts +++ b/src/crypto/talerCrypto-test.ts @@ -30,7 +30,7 @@ import { rsaVerify, } from "./talerCrypto"; import { sha512, kdf } from "./primitives/kdf"; -import nacl = require("./primitives/nacl-fast"); +import * as nacl from "./primitives/nacl-fast"; function hexToBytes(hex: string) { for (var bytes = [], c = 0; c < hex.length; c += 2) diff --git a/src/crypto/talerCrypto.ts b/src/crypto/talerCrypto.ts index 317b1af55..3da4f4a47 100644 --- a/src/crypto/talerCrypto.ts +++ b/src/crypto/talerCrypto.ts @@ -18,7 +18,7 @@ * Native implementation of GNU Taler crypto. */ -import nacl = require("./primitives/nacl-fast"); +import * as nacl from "./primitives/nacl-fast"; import bigint from "big-integer"; import { kdf } from "./primitives/kdf"; diff --git a/src/crypto/workers/cryptoApi.ts b/src/crypto/workers/cryptoApi.ts index d3b12e26d..46dc7dfaf 100644 --- a/src/crypto/workers/cryptoApi.ts +++ b/src/crypto/workers/cryptoApi.ts @@ -110,7 +110,7 @@ export interface CryptoWorkerFactory { export class BrowserCryptoWorkerFactory implements CryptoWorkerFactory { startWorker(): CryptoWorker { const workerCtor = Worker; - const workerPath = "/dist/cryptoWorker-bundle.js"; + const workerPath = "/dist/webextension/browserWorkerEntry.js"; return new workerCtor(workerPath) as CryptoWorker; } @@ -206,7 +206,7 @@ export class CryptoApi { }; this.resetWorkerTimeout(ws); work.startTime = timer.performanceNow(); - setImmediate(() => ws.w!.postMessage(msg)); + setTimeout(() => ws.w!.postMessage(msg), 0); } resetWorkerTimeout(ws: WorkerState) { diff --git a/src/crypto/workers/nodeThreadWorker.ts b/src/crypto/workers/nodeThreadWorker.ts index 1247900f9..d289c14b2 100644 --- a/src/crypto/workers/nodeThreadWorker.ts +++ b/src/crypto/workers/nodeThreadWorker.ts @@ -19,7 +19,7 @@ import { CryptoWorkerFactory } from "./cryptoApi"; // tslint:disable:no-var-requires import { CryptoWorker } from "./cryptoWorker"; -import os = require("os"); +import os from "os"; import { CryptoImplementation } from "./cryptoImplementation"; const f = __filename; diff --git a/src/crypto/workers/synchronousWorker.ts b/src/crypto/workers/synchronousWorker.ts index b453468d5..f4bcf396c 100644 --- a/src/crypto/workers/synchronousWorker.ts +++ b/src/crypto/workers/synchronousWorker.ts @@ -93,7 +93,7 @@ export class SynchronousCryptoWorker { } try { - setImmediate(() => this.dispatchMessage({ result, id })); + setTimeout(() => this.dispatchMessage({ result, id }), 0); } catch (e) { console.log("got error during dispatch", e); } diff --git a/src/headless/clk.ts b/src/headless/clk.ts index 7d7417ea9..7c99384bb 100644 --- a/src/headless/clk.ts +++ b/src/headless/clk.ts @@ -17,9 +17,9 @@ /** * Imports. */ -import process = require("process"); -import path = require("path"); -import readline = require("readline"); +import process from "process"; +import path from "path"; +import readline from "readline"; class Converter {} diff --git a/src/headless/helpers.ts b/src/headless/helpers.ts index 92452e78f..053332b37 100644 --- a/src/headless/helpers.ts +++ b/src/headless/helpers.ts @@ -28,7 +28,7 @@ import { openTalerDatabase } from "../db"; import { HttpRequestLibrary } from "../util/http"; import * as amounts from "../util/amounts"; import { Bank } from "./bank"; -import fs = require("fs"); +import fs from "fs"; import { NodeThreadCryptoWorkerFactory } from "../crypto/workers/nodeThreadWorker"; import { WalletNotification, NotificationType } from "../types/notifications"; import { Database } from "../util/query"; diff --git a/src/headless/taler-wallet-cli.ts b/src/headless/taler-wallet-cli.ts index d183ef316..c7909948f 100644 --- a/src/headless/taler-wallet-cli.ts +++ b/src/headless/taler-wallet-cli.ts @@ -14,13 +14,13 @@ GNU Taler; see the file COPYING. If not, see */ -import os = require("os"); -import fs = require("fs"); +import os from "os"; +import fs from "fs"; import { getDefaultNodeWallet, withdrawTestBalance } from "./helpers"; import { MerchantBackendConnection } from "./merchant"; import { runIntegrationTest, runIntegrationTestBasic } from "./integrationtest"; import { Wallet } from "../wallet"; -import qrcodeGenerator = require("qrcode-generator"); +import qrcodeGenerator from "qrcode-generator"; import * as clk from "./clk"; import { BridgeIDBFactory, MemoryBackend } from "idb-bridge"; import { Logger } from "../util/logging"; @@ -29,17 +29,11 @@ import { decodeCrock } from "../crypto/talerCrypto"; import { OperationFailedAndReportedError } from "../operations/errors"; import { Bank } from "./bank"; import { classifyTalerUri, TalerUriType } from "../util/taleruri"; -import util = require("util"); import { Configuration } from "../util/talerconfig"; import { setDangerousTimetravel } from "../util/time"; import { makeCodecForList, codecForString } from "../util/codec"; import { NodeHttpLib } from "./NodeHttpLib"; -// Backwards compatibility with nodejs<0.11, where TextEncoder and TextDecoder -// are not globals yet. -(global as any).TextEncoder = util.TextEncoder; -(global as any).TextDecoder = util.TextDecoder; - const logger = new Logger("taler-wallet-cli.ts"); const defaultWalletDbPath = os.homedir + "/" + ".talerwalletdb.json"; diff --git a/src/operations/state.ts b/src/operations/state.ts index f571fe93a..246a71266 100644 --- a/src/operations/state.ts +++ b/src/operations/state.ts @@ -53,9 +53,9 @@ export class InternalWalletState { logger.trace("Notification", n); for (const l of this.listeners) { const nc = JSON.parse(JSON.stringify(n)); - setImmediate(() => { + setTimeout(() => { l(nc); - }); + }, 0); } } diff --git a/src/webex/notify.ts b/src/webex/notify.ts index 910a20999..d76d121a0 100644 --- a/src/webex/notify.ts +++ b/src/webex/notify.ts @@ -24,7 +24,7 @@ /** * Imports. */ -import wxApi = require("./wxApi"); +import * as wxApi from "./wxApi"; declare var cloneInto: any; diff --git a/src/webex/pageEntryPoint.ts b/src/webex/pageEntryPoint.ts new file mode 100644 index 000000000..04e3dd8d1 --- /dev/null +++ b/src/webex/pageEntryPoint.ts @@ -0,0 +1,64 @@ +/* + This file is part of GNU Taler + (C) 2020 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * Main entry point for extension pages. + * + * @author Florian Dold + */ + +import ReactDOM from "react-dom"; +import { createPopup } from "./pages/popup"; +import { createWithdrawPage } from "./pages/withdraw"; +import { createWelcomePage } from "./pages/welcome"; + +function main() { + try { + let mainElement; + const m = location.pathname.match(/([^/]+)$/); + if (!m) { + throw Error("can't parse page URL"); + } + const page = m[1]; + switch (page) { + case "popup.html": + mainElement = createPopup(); + break; + case "withdraw.html": + mainElement = createWithdrawPage(); + break; + case "welcome.html": + mainElement = createWelcomePage(); + break; + default: + throw Error(`page '${page}' not implemented`); + } + const container = document.getElementById("container"); + if (!container) { + throw Error("container not found, can't mount page contents"); + } + ReactDOM.render(mainElement, container); + } catch (e) { + console.error("got error", e); + document.body.innerText = `Fatal error: "${e.message}". Please report this bug at https://bugs.gnunet.org/.`; + } +} + +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", main); +} else { + main(); +} diff --git a/src/webex/pages/add-auditor.tsx b/src/webex/pages/add-auditor.tsx index 5840f4d63..dbe84cde4 100644 --- a/src/webex/pages/add-auditor.tsx +++ b/src/webex/pages/add-auditor.tsx @@ -23,7 +23,6 @@ import { CurrencyRecord } from "../../types/dbTypes"; import { getCurrencies, updateCurrency } from "../wxApi"; import React, { useState } from "react"; -import { registerMountPage } from "../renderHtml"; interface ConfirmAuditorProps { url: string; @@ -114,7 +113,7 @@ function ConfirmAuditor(props: ConfirmAuditorProps) { ); } -registerMountPage(() => { +export function makeAddAuditorPage() { const walletPageUrl = new URL(document.location.href); const url = walletPageUrl.searchParams.get("url"); if (!url) { @@ -135,4 +134,4 @@ registerMountPage(() => { const expirationStamp = Number.parseInt(auditorStampStr); const args = { url, currency, auditorPub, expirationStamp }; return ; -}); +} diff --git a/src/webex/pages/auditors.tsx b/src/webex/pages/auditors.tsx index e2bb29af1..e933aeace 100644 --- a/src/webex/pages/auditors.tsx +++ b/src/webex/pages/auditors.tsx @@ -151,8 +151,6 @@ class CurrencyList extends React.Component<{}, CurrencyListState> { } } -function main() { - ReactDOM.render(, document.getElementById("container")!); +function makeAuditorsPage() { + return ; } - -document.addEventListener("DOMContentLoaded", main); diff --git a/src/webex/pages/benchmark.tsx b/src/webex/pages/benchmark.tsx index 7de546bb0..1efe7898d 100644 --- a/src/webex/pages/benchmark.tsx +++ b/src/webex/pages/benchmark.tsx @@ -27,8 +27,6 @@ import { BenchmarkResult } from "../../types/walletTypes"; import * as wxApi from "../wxApi"; import * as React from "react"; -import * as ReactDOM from "react-dom"; -import { registerMountPage } from "../renderHtml"; interface BenchmarkRunnerState { repetitions: number; @@ -101,6 +99,6 @@ class BenchmarkRunner extends React.Component { } } -registerMountPage(() => { +export function makeBenchmarkPage() { return ; -}); +} diff --git a/src/webex/pages/pay.tsx b/src/webex/pages/pay.tsx index 714e3b0a3..09aa595c3 100644 --- a/src/webex/pages/pay.tsx +++ b/src/webex/pages/pay.tsx @@ -26,7 +26,7 @@ import * as i18n from "../i18n"; import { PreparePayResult } from "../../types/walletTypes"; -import { renderAmount, ProgressButton, registerMountPage } from "../renderHtml"; +import { renderAmount, ProgressButton } from "../renderHtml"; import * as wxApi from "../wxApi"; import React, { useState, useEffect } from "react"; @@ -178,11 +178,11 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }) { ); } -registerMountPage(() => { +export function makePayPage() { const url = new URL(document.location.href); const talerPayUri = url.searchParams.get("talerPayUri"); if (!talerPayUri) { throw Error("invalid parameter"); } return ; -}); +} \ No newline at end of file diff --git a/src/webex/pages/payback.tsx b/src/webex/pages/payback.tsx index 96d43ff49..9c53aac91 100644 --- a/src/webex/pages/payback.tsx +++ b/src/webex/pages/payback.tsx @@ -23,13 +23,8 @@ /** * Imports. */ -import { ReserveRecord } from "../../types/dbTypes"; -import { renderAmount, registerMountPage } from "../renderHtml"; import * as React from "react"; -import { useState } from "react"; -function Payback() { +export function makePaybackPage() { return
not implemented
; } - -registerMountPage(() => ); diff --git a/src/webex/pages/popup.tsx b/src/webex/pages/popup.tsx index 17880db58..f4a2bf568 100644 --- a/src/webex/pages/popup.tsx +++ b/src/webex/pages/popup.tsx @@ -35,7 +35,6 @@ import { abbrev, renderAmount, PageLink, - registerMountPage, } from "../renderHtml"; import * as wxApi from "../wxApi"; @@ -807,7 +806,7 @@ function WalletPopup() { ); } -registerMountPage(() => { +export function createPopup() { chrome.runtime.connect({ name: "popup" }); return ; -}); +} \ No newline at end of file diff --git a/src/webex/pages/redirect.html b/src/webex/pages/redirect.html deleted file mode 100644 index 67fddb527..000000000 --- a/src/webex/pages/redirect.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - Redirecting to extension page ... - - diff --git a/src/webex/pages/redirect.js b/src/webex/pages/redirect.js deleted file mode 100644 index 547b225bd..000000000 --- a/src/webex/pages/redirect.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * This is the entry point for redirects, and should be the only - * web-accessible resource declared in the manifest. This prevents - * malicious websites from embedding wallet pages in them. - * - * We still need this redirect page since a webRequest can only directly - * redirect to pages inside the extension that are a web-accessible resource. - */ - -const myUrl = new URL(window.location.href); -const redirectUrl = myUrl.searchParams.get("url"); -if (!redirectUrl) { - console.error("missing redirect URL"); -} else { - window.location.replace(redirectUrl); -} diff --git a/src/webex/pages/refund.tsx b/src/webex/pages/refund.tsx index 389d5e569..8263ceace 100644 --- a/src/webex/pages/refund.tsx +++ b/src/webex/pages/refund.tsx @@ -73,7 +73,7 @@ function RefundStatusView(props: { talerRefundUri: string }) { ); } -async function main() { +export function createRefundPage() { const url = new URL(document.location.href); const container = document.getElementById("container"); @@ -88,10 +88,5 @@ async function main() { return; } - ReactDOM.render( - , - container, - ); + return ; } - -document.addEventListener("DOMContentLoaded", () => main()); diff --git a/src/webex/pages/reset-required.tsx b/src/webex/pages/reset-required.tsx index 81f21f459..e58243b34 100644 --- a/src/webex/pages/reset-required.tsx +++ b/src/webex/pages/reset-required.tsx @@ -21,7 +21,6 @@ */ import * as React from "react"; -import * as ReactDOM from "react-dom"; import * as wxApi from "../wxApi"; @@ -89,6 +88,6 @@ class ResetNotification extends React.Component { } } -document.addEventListener("DOMContentLoaded", () => { - ReactDOM.render(, document.getElementById("container")!); -}); +export function createResetRequiredPage() { + return ; +} diff --git a/src/webex/pages/return-coins.tsx b/src/webex/pages/return-coins.tsx index 3786697c6..06a3ba169 100644 --- a/src/webex/pages/return-coins.tsx +++ b/src/webex/pages/return-coins.tsx @@ -310,8 +310,6 @@ class ReturnCoins extends React.Component<{}, ReturnCoinsState> { } } -function main() { - ReactDOM.render(, document.getElementById("container")!); +export function createReturnCoinsPage() { + return ; } - -document.addEventListener("DOMContentLoaded", main); diff --git a/src/webex/pages/show-db.html b/src/webex/pages/show-db.html deleted file mode 100644 index ae77e3fb0..000000000 --- a/src/webex/pages/show-db.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Taler Wallet: Reserve Created - - - - - - -

DB Dump

- - - -

-  
-
diff --git a/src/webex/pages/show-db.ts b/src/webex/pages/show-db.ts
deleted file mode 100644
index 16ea80d2d..000000000
--- a/src/webex/pages/show-db.ts
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- This file is part of TALER
- (C) 2015 GNUnet e.V.
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING.  If not, see 
- */
-
-/**
- * Wallet database dump for debugging.
- *
- * @author Florian Dold
- */
-
-function replacer(
-  match: string,
-  pIndent: string,
-  pKey: string,
-  pVal: string,
-  pEnd: string,
-) {
-  const key = "";
-  const val = "";
-  const str = "";
-  let r = pIndent || "";
-  if (pKey) {
-    r = r + key + '"' + pKey.replace(/[": ]/g, "") + '": ';
-  }
-  if (pVal) {
-    r = r + (pVal[0] === '"' ? str : val) + pVal + "";
-  }
-  return r + (pEnd || "");
-}
-
-function prettyPrint(obj: any) {
-  const jsonLine = /^( *)("[\w]+": )?("[^"]*"|[\w.+-]*)?([,[{])?$/gm;
-  return JSON.stringify(obj, null as any, 3)
-    .replace(/&/g, "&")
-    .replace(/\\"/g, """)
-    .replace(//g, ">")
-    .replace(jsonLine, replacer);
-}
-
-document.addEventListener("DOMContentLoaded", () => {
-  chrome.runtime.sendMessage({ type: "dump-db" }, (resp) => {
-    const el = document.getElementById("dump");
-    if (!el) {
-      throw Error();
-    }
-    el.innerHTML = prettyPrint(resp);
-
-    document.getElementById("download")!.addEventListener("click", (evt) => {
-      console.log("creating download");
-      const element = document.createElement("a");
-      element.setAttribute(
-        "href",
-        "data:text/plain;charset=utf-8," +
-          encodeURIComponent(JSON.stringify(resp)),
-      );
-      element.setAttribute("download", "wallet-dump.txt");
-      element.style.display = "none";
-      document.body.appendChild(element);
-      element.click();
-    });
-  });
-
-  const fileInput = document.getElementById("fileInput")! as HTMLInputElement;
-  fileInput.onchange = (evt) => {
-    if (!fileInput.files || fileInput.files.length !== 1) {
-      alert("please select exactly one file to import");
-      return;
-    }
-    const file = fileInput.files[0];
-    const fr = new FileReader();
-    fr.onload = (e: any) => {
-      console.log("got file");
-      const dump = JSON.parse(e.target.result);
-      console.log("parsed contents", dump);
-      chrome.runtime.sendMessage(
-        { type: "import-db", detail: { dump } },
-        (resp) => {
-          alert("loaded");
-        },
-      );
-    };
-    console.log("reading file", file);
-    fr.readAsText(file);
-  };
-
-  document.getElementById("import")!.addEventListener("click", (evt) => {
-    fileInput.click();
-    evt.preventDefault();
-  });
-});
diff --git a/src/webex/pages/tip.tsx b/src/webex/pages/tip.tsx
index 35e033c0d..10e12d590 100644
--- a/src/webex/pages/tip.tsx
+++ b/src/webex/pages/tip.tsx
@@ -100,26 +100,12 @@ function TipDisplay(props: { talerTipUri: string }) {
   );
 }
 
-async function main() {
-  try {
+export function createTipPage() {
     const url = new URL(document.location.href);
     const talerTipUri = url.searchParams.get("talerTipUri");
     if (typeof talerTipUri !== "string") {
       throw Error("talerTipUri must be a string");
     }
 
-    ReactDOM.render(
-      ,
-      document.getElementById("container")!,
-    );
-  } catch (e) {
-    // TODO: provide more context information, maybe factor it out into a
-    // TODO:generic error reporting function or component.
-    document.body.innerText = i18n.str`Fatal error: "${e.message}".`;
-    console.error(`got error "${e.message}"`, e);
-  }
+    return ;
 }
-
-document.addEventListener("DOMContentLoaded", () => {
-  main();
-});
diff --git a/src/webex/pages/welcome.tsx b/src/webex/pages/welcome.tsx
index 83f4f01d5..8510ad383 100644
--- a/src/webex/pages/welcome.tsx
+++ b/src/webex/pages/welcome.tsx
@@ -22,7 +22,7 @@
 
 import React, { useState, useEffect } from "react";
 import { getDiagnostics } from "../wxApi";
-import { registerMountPage, PageLink } from "../renderHtml";
+import { PageLink } from "../renderHtml";
 import { WalletDiagnostics } from "../../types/walletTypes";
 
 function Diagnostics() {
@@ -110,4 +110,6 @@ function Welcome() {
   );
 }
 
-registerMountPage(() => );
+export function createWelcomePage() {
+  return ;
+}
\ No newline at end of file
diff --git a/src/webex/pages/withdraw.tsx b/src/webex/pages/withdraw.tsx
index 7a2665314..e071dc8ba 100644
--- a/src/webex/pages/withdraw.tsx
+++ b/src/webex/pages/withdraw.tsx
@@ -217,6 +217,11 @@ async function main() {
   }
 }
 
-document.addEventListener("DOMContentLoaded", () => {
-  main();
-});
+export function createWithdrawPage() {
+  const url = new URL(document.location.href);
+    const talerWithdrawUri = url.searchParams.get("talerWithdrawUri");
+    if (!talerWithdrawUri) {
+      throw Error("withdraw URI required");
+    }
+    return ;
+}
\ No newline at end of file
diff --git a/src/webex/renderHtml.tsx b/src/webex/renderHtml.tsx
index 08e7de607..082dd84dd 100644
--- a/src/webex/renderHtml.tsx
+++ b/src/webex/renderHtml.tsx
@@ -27,7 +27,6 @@ import { AmountJson } from "../util/amounts";
 import * as Amounts from "../util/amounts";
 import { DenominationRecord } from "../types/dbTypes";
 import { ExchangeWithdrawDetails } from "../types/walletTypes";
-import * as moment from "moment";
 import * as i18n from "./i18n";
 import React from "react";
 import ReactDOM from "react-dom";
@@ -331,29 +330,6 @@ export function ProgressButton(
   );
 }
 
-export function registerMountPage(mainFn: () => React.ReactElement) {
-  async function main() {
-    try {
-      const mainElement = mainFn();
-      const container = document.getElementById("container");
-      if (!container) {
-        throw Error("container not found, can't mount page contents");
-      }
-      ReactDOM.render(mainElement, container);
-    } catch (e) {
-      document.body.innerText = `Fatal error: "${e.message}".  Please report this bug at https://bugs.gnunet.org/.`;
-      console.error("got error", e);
-    }
-  }
-
-  if (document.readyState === "loading") {
-    document.addEventListener("DOMContentLoaded", main);
-    return;
-  } else {
-    main();
-  }
-}
-
 export function PageLink(props: React.PropsWithChildren<{ pageName: string }>) {
   const url = chrome.extension.getURL(`/src/webex/pages/${props.pageName}`);
   return (
diff --git a/tsconfig.json b/tsconfig.json
index f87e16b11..a6fa12f9b 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,11 +1,11 @@
 {
   "compileOnSave": true,
   "compilerOptions": {
-    "target": "es6",
+    "target": "ES6",
     "jsx": "react",
     "reactNamespace": "React",
-    "experimentalDecorators": true,
     "module": "commonjs",
+    "moduleResolution": "node",
     "sourceMap": true,
     "lib": [
       "es6",
@@ -21,103 +21,8 @@
     "allowJs": true,
     "checkJs": true,
     "incremental": true,
-    "esModuleInterop": true
+    "esModuleInterop": true,
+    "importHelpers": true
   },
-  "files": [
-    "src/android/index.ts",
-    "src/crypto/primitives/kdf.ts",
-    "src/crypto/primitives/nacl-fast.ts",
-    "src/crypto/primitives/sha256.ts",
-    "src/crypto/talerCrypto-test.ts",
-    "src/crypto/talerCrypto.ts",
-    "src/crypto/workers/browserWorkerEntry.ts",
-    "src/crypto/workers/cryptoApi.ts",
-    "src/crypto/workers/cryptoImplementation.ts",
-    "src/crypto/workers/cryptoWorker.ts",
-    "src/crypto/workers/nodeThreadWorker.ts",
-    "src/crypto/workers/synchronousWorker.ts",
-    "src/db.ts",
-    "src/headless/NodeHttpLib.ts",
-    "src/headless/bank.ts",
-    "src/headless/clk.ts",
-    "src/headless/helpers.ts",
-    "src/headless/integrationtest.ts",
-    "src/headless/merchant.ts",
-    "src/headless/taler-wallet-cli.ts",
-    "src/i18n/strings.ts",
-    "src/index.ts",
-    "src/operations/balance.ts",
-    "src/operations/errors.ts",
-    "src/operations/exchanges.ts",
-    "src/operations/history.ts",
-    "src/operations/pay.ts",
-    "src/operations/pending.ts",
-    "src/operations/recoup.ts",
-    "src/operations/refresh.ts",
-    "src/operations/refund.ts",
-    "src/operations/reserves.ts",
-    "src/operations/state.ts",
-    "src/operations/tip.ts",
-    "src/operations/versions.ts",
-    "src/operations/withdraw.ts",
-    "src/types/ReserveStatus.ts",
-    "src/types/ReserveTransaction.ts",
-    "src/types/dbTypes.ts",
-    "src/types/history.ts",
-    "src/types/notifications.ts",
-    "src/types/pending.ts",
-    "src/types/schemacore.ts",
-    "src/types/talerTypes.ts",
-    "src/types/types-test.ts",
-    "src/types/walletTypes.ts",
-    "src/util/RequestThrottler.ts",
-    "src/util/amounts.ts",
-    "src/util/assertUnreachable.ts",
-    "src/util/asyncMemo.ts",
-    "src/util/codec-test.ts",
-    "src/util/codec.ts",
-    "src/util/helpers-test.ts",
-    "src/util/helpers.ts",
-    "src/util/http.ts",
-    "src/util/libtoolVersion-test.ts",
-    "src/util/libtoolVersion.ts",
-    "src/util/logging.ts",
-    "src/util/payto-test.ts",
-    "src/util/payto.ts",
-    "src/util/promiseUtils.ts",
-    "src/util/query.ts",
-    "src/util/reserveHistoryUtil-test.ts",
-    "src/util/reserveHistoryUtil.ts",
-    "src/util/talerconfig.ts",
-    "src/util/taleruri-test.ts",
-    "src/util/taleruri.ts",
-    "src/util/time.ts",
-    "src/util/timer.ts",
-    "src/util/wire.ts",
-    "src/wallet-test.ts",
-    "src/wallet.ts",
-    "src/webex/background.ts",
-    "src/webex/chromeBadge.ts",
-    "src/webex/compat.ts",
-    "src/webex/i18n.tsx",
-    "src/webex/messages.ts",
-    "src/webex/notify.ts",
-    "src/webex/pages/add-auditor.tsx",
-    "src/webex/pages/auditors.tsx",
-    "src/webex/pages/benchmark.tsx",
-    "src/webex/pages/pay.tsx",
-    "src/webex/pages/payback.tsx",
-    "src/webex/pages/popup.tsx",
-    "src/webex/pages/redirect.js",
-    "src/webex/pages/refund.tsx",
-    "src/webex/pages/reset-required.tsx",
-    "src/webex/pages/return-coins.tsx",
-    "src/webex/pages/show-db.ts",
-    "src/webex/pages/tip.tsx",
-    "src/webex/pages/welcome.tsx",
-    "src/webex/pages/withdraw.tsx",
-    "src/webex/renderHtml.tsx",
-    "src/webex/wxApi.ts",
-    "src/webex/wxBackend.ts"
-  ]
-}
\ No newline at end of file
+  "include": ["src/**/*"]
+}
diff --git a/tslint.json b/tslint.json
deleted file mode 100644
index a56b56a94..000000000
--- a/tslint.json
+++ /dev/null
@@ -1,63 +0,0 @@
-{
-  "defaultSeverity": "error",
-  "extends": [
-    "tslint:recommended"
-    ],
-    "jsRules": {},
-    "rules": {
-      "arrow-parens": false,
-      "max-line-length": {
-        "options": [120]
-      },
-      "space-before-function-paren": [true, {"named": "never", "anonymous": "always"}],
-      "no-console": [false],
-      "no-consecutive-blank-lines": [true, 2],
-      "forin": false,
-      "member-access": false,
-      "variable-name": false,
-      "interface-name": false,
-      "max-classes-per-file": false,
-      "ordered-imports": [true,
-        {
-          "import-sources-order": "lowercase-last",
-          "named-imports-order": "lowercase-last"
-        }
-      ],
-      "no-namespace": false,
-      "member-ordering": false,
-      "array-type": [true, "array-simple"],
-      "class-name": false,
-      "no-bitwise": false,
-      "file-header": [true, "GNU General Public License"],
-      "completed-docs": [true, {
-        "methods": {
-          "privacies": ["public"],
-          "locations": "all"
-        },
-        "properties": {
-          "privacies": ["public"],
-          "locations": ["all"]
-        },
-        "functions": {
-          "visibilities": ["exported"]
-        },
-        "interfaces": {
-          "visibilities": ["exported"]
-        },
-        "types": {
-          "visibilities": ["exported"]
-        },
-        "enums": {
-          "visibilities": ["exported"]
-        },
-        "classes": {
-          "visibilities": ["exported"]
-        },
-        "namespaces": {
-          "visibilities": ["exported"]
-        }
-      }],
-      "no-unused-variable": true
-    },
-    "rulesDirectory": []
-}
diff --git a/manifest.json b/webextension/manifest.json
similarity index 81%
rename from manifest.json
rename to webextension/manifest.json
index 8926e2579..6a0101c4b 100644
--- a/manifest.json
+++ b/webextension/manifest.json
@@ -36,25 +36,21 @@
       "32": "img/icon.png"
     },
     "default_title": "Taler",
-    "default_popup": "src/webex/pages/popup.html"
+    "default_popup": "pages/popup.html"
   },
 
   "content_scripts": [
     {
       "matches": ["*://*/*"],
       "js": [
-        "dist/contentScript-bundle.js"
+        "js/contentScript.js"
       ],
       "run_at": "document_start"
     }
   ],
 
-  "web_accessible_resources": [
-    "src/webex/pages/redirect.html"
-  ],
-
   "background": {
-    "page": "src/webex/background.html",
+    "page": "background.html",
     "persistent": true
   }
 }
diff --git a/src/webex/pages/add-auditor.html b/webextension/static/add-auditor.html
similarity index 84%
rename from src/webex/pages/add-auditor.html
rename to webextension/static/add-auditor.html
index 7f30b5fb6..ab1638b8e 100644
--- a/src/webex/pages/add-auditor.html
+++ b/webextension/static/add-auditor.html
@@ -9,8 +9,7 @@
 
     
 
-    
-    
+