Browse Source

build: use typescript for internal Electron JS code (#16441)

Samuel Attard 6 years ago
parent
commit
26df9992cf

+ 9 - 0
.eslintrc.json

@@ -1,12 +1,21 @@
 {
   "extends": "standard",
+  "parser": "typescript-eslint-parser",
+  "plugins": ["typescript"],
   "env": {
     "browser": true
   },
   "rules": {
     "no-var": "error",
+    "no-unused-vars": 0,
+    "no-global-assign": 0,
+    "typescript/no-unused-vars": "error",
     "prefer-const": ["error", {
       "destructuring": "all"
     }]
+  },
+  "parserOptions": {
+    "ecmaVersion": 6,
+    "sourceType": "module"
   }
 }

+ 3 - 0
.gitignore

@@ -59,3 +59,6 @@ spec/.hash
 
 # Generated native addon files
 /spec/fixtures/native-addon/echo/build/
+
+# If someone runs tsc this is where stuff will end up
+ts-gen

+ 43 - 8
BUILD.gn

@@ -11,8 +11,10 @@ import("//tools/v8_context_snapshot/v8_context_snapshot.gni")
 import("//v8/snapshot_toolchain.gni")
 import("build/asar.gni")
 import("build/npm.gni")
+import("build/tsc.gni")
 import("buildflags/buildflags.gni")
 import("electron_paks.gni")
+import("filenames.auto.gni")
 import("filenames.gni")
 
 if (is_mac) {
@@ -53,6 +55,21 @@ config("branding") {
   ]
 }
 
+# We geneate the definitions twice here, once in //electron/electron.d.ts
+# and once in $target_gen_dir
+# The one in $target_gen_dir is used for the actual TSC build later one
+# and the one in //electron/electron.d.ts is used by your IDE (vscode)
+# for typescript prompting
+npm_action("build_electron_definitions") {
+  script = "gn-typescript-definitions"
+  args = [ rebase_path("$target_gen_dir/tsc/typings/electron.d.ts") ]
+  inputs = auto_filenames.api_docs + [ "package-lock.json" ]
+
+  outputs = [
+    "$target_gen_dir/tsc/typings/electron.d.ts",
+  ]
+}
+
 npm_action("atom_browserify_sandbox") {
   script = "browserify"
 
@@ -140,9 +157,12 @@ action("atom_js2c") {
 target_gen_electron_js = "$target_gen_dir/js/electron"
 target_gen_default_app_js = "$target_gen_dir/js/default_app"
 
-# TODO(MarshallOfSound)
-# This copy will be replaced by a call to tsc in the future
-copy("lib_js") {
+typescript_build("lib_js") {
+  deps = [
+    ":build_electron_definitions",
+  ]
+  type_root = rebase_path("$target_gen_dir/tsc/electron/typings")
+
   sources = filenames.js_sources
   if (enable_desktop_capturer) {
     sources += [
@@ -162,9 +182,9 @@ copy("lib_js") {
     ]
   }
 
-  outputs = [
-    "$target_gen_electron_js/{{source}}",
-  ]
+  output_gen_dir = target_gen_electron_js
+  output_dir_name = "lib"
+  tsconfig = "tsconfig.electron.json"
 }
 
 asar("electron_asar") {
@@ -179,8 +199,21 @@ asar("electron_asar") {
   ]
 }
 
-copy("default_app_js") {
-  sources = filenames.default_app_sources
+typescript_build("default_app_js") {
+  deps = [
+    ":build_electron_definitions",
+  ]
+  type_root = rebase_path("$target_gen_dir/tsc/electron/typings")
+
+  sources = filenames.default_app_ts_sources
+
+  output_gen_dir = target_gen_default_app_js
+  output_dir_name = "default_app"
+  tsconfig = "tsconfig.default_app.json"
+}
+
+copy("default_app_static") {
+  sources = filenames.default_app_static_sources
   outputs = [
     "$target_gen_default_app_js/{{source}}",
   ]
@@ -197,10 +230,12 @@ asar("default_app_asar") {
   deps = [
     ":default_app_js",
     ":default_app_octicon_deps",
+    ":default_app_static",
   ]
 
   root = "$target_gen_default_app_js/electron/default_app"
   sources = get_target_outputs(":default_app_js") +
+            get_target_outputs(":default_app_static") +
             get_target_outputs(":default_app_octicon_deps")
   outputs = [
     "$root_out_dir/resources/default_app.asar",

+ 50 - 0
build/tsc.gni

@@ -0,0 +1,50 @@
+import("npm.gni")
+
+template("typescript_build") {
+  assert(defined(invoker.tsconfig), "Need tsconfig name to run")
+  assert(defined(invoker.sources), "Need tsc sources to run")
+  assert(defined(invoker.output_dir_name),
+         "Need output_dir_name to run, should be 'lib' or other top level dir")
+  assert(defined(invoker.output_gen_dir),
+         "Need output_gen_dir to run, should be relative to the root gen dir")
+
+  npm_action(target_name) {
+    forward_variables_from(invoker,
+                           [
+                             "deps",
+                             "public_deps",
+                             "outputs",
+                           ])
+    script = "tsc"
+
+    sources = invoker.sources
+    inputs = [
+      invoker.tsconfig,
+      "//electron/tsconfig.json",
+      "//electron/package-lock.json",
+    ]
+
+    type_roots = "node_modules/@types,typings"
+    if (defined(invoker.type_root)) {
+      type_roots += "," + invoker.type_root
+    }
+
+    base_out_path = invoker.output_gen_dir + "/electron/"
+    args = [
+      "-p",
+      rebase_path(invoker.tsconfig),
+      "--outDir",
+      rebase_path("$base_out_path" + invoker.output_dir_name),
+      "--typeRoots",
+      type_roots,
+    ]
+
+    outputs = []
+
+    foreach(invoker_source, invoker.sources) {
+      # The output of TSC is all inputs but with JS instead of TS as the extension
+      outputs += [ "$base_out_path" + get_path_info(invoker_source, "dir") +
+                   "/" + get_path_info(invoker_source, "name") + ".js" ]
+    }
+  }
+}

+ 6 - 8
default_app/default_app.js → default_app/default_app.ts

@@ -1,19 +1,17 @@
-'use strict'
+import { app, BrowserWindow, BrowserWindowConstructorOptions } from 'electron'
+import * as path from 'path'
 
-const { app, BrowserWindow } = require('electron')
-const path = require('path')
-
-let mainWindow = null
+let mainWindow: BrowserWindow | null = null
 
 // Quit when all windows are closed.
 app.on('window-all-closed', () => {
   app.quit()
 })
 
-exports.load = async (appUrl) => {
+export const load = async (appUrl: string) => {
   await app.whenReady()
 
-  const options = {
+  const options: BrowserWindowConstructorOptions = {
     width: 900,
     height: 600,
     autoHideMenuBar: true,
@@ -33,7 +31,7 @@ exports.load = async (appUrl) => {
 
   mainWindow = new BrowserWindow(options)
 
-  mainWindow.on('ready-to-show', () => mainWindow.show())
+  mainWindow.on('ready-to-show', () => mainWindow!.show())
 
   mainWindow.loadURL(appUrl)
   mainWindow.focus()

+ 25 - 13
default_app/main.js → default_app/main.ts

@@ -1,20 +1,31 @@
-'use strict'
-
-const { app, dialog } = require('electron')
+import { app, dialog } from 'electron'
+
+import * as fs from 'fs'
+import * as path from 'path'
+import * as url from 'url'
+
+type DefaultAppOptions = {
+  file: null | string;
+  noHelp: boolean;
+  version: boolean;
+  webdriver: boolean;
+  interactive: boolean;
+  abi: boolean;
+  modules: string[];
+}
 
-const fs = require('fs')
 const Module = require('module')
-const path = require('path')
-const url = require('url')
 
 // Parse command line options.
 const argv = process.argv.slice(1)
 
-const option = {
+const option: DefaultAppOptions = {
   file: null,
   noHelp: Boolean(process.env.ELECTRON_NO_HELP),
-  version: null,
-  webdriver: null,
+  version: false,
+  webdriver: false,
+  interactive: false,
+  abi: false,
   modules: []
 }
 
@@ -62,7 +73,7 @@ if (option.modules.length > 0) {
   Module._preloadModules(option.modules)
 }
 
-function loadApplicationPackage (packagePath) {
+function loadApplicationPackage (packagePath: string) {
   // Add a flag indicating app is started from default app.
   Object.defineProperty(process, 'defaultApp', {
     configurable: false,
@@ -112,14 +123,15 @@ function loadApplicationPackage (packagePath) {
   }
 }
 
-function showErrorMessage (message) {
+function showErrorMessage (message: string) {
   app.focus()
   dialog.showErrorBox('Error launching app', message)
   process.exit(1)
 }
 
-function loadApplicationByUrl (appUrl) {
-  require('./default_app').load(appUrl)
+async function loadApplicationByUrl (appUrl: string) {
+  const { load } = await import('./default_app')
+  load(appUrl)
 }
 
 function startRepl () {

+ 19 - 19
default_app/renderer.js → default_app/renderer.ts

@@ -1,9 +1,7 @@
-'use strict'
-
-const { remote, shell } = require('electron')
-const fs = require('fs')
-const path = require('path')
-const URL = require('url')
+import { remote, shell } from 'electron'
+import * as fs from 'fs'
+import * as path from 'path'
+import * as URL from 'url'
 
 function initialize () {
   // Find the shortest path to the electron binary
@@ -13,13 +11,13 @@ function initialize () {
     ? absoluteElectronPath
     : relativeElectronPath
 
-  for (const link of document.querySelectorAll('a[href]')) {
+  for (const link of document.querySelectorAll<HTMLLinkElement>('a[href]')) {
     // safely add `?utm_source=default_app
-    const parsedUrl = URL.parse(link.getAttribute('href'), true)
+    const parsedUrl = URL.parse(link.getAttribute('href')!, true)
     parsedUrl.query = { ...parsedUrl.query, utm_source: 'default_app' }
     const url = URL.format(parsedUrl)
 
-    const openLinkExternally = (e) => {
+    const openLinkExternally = (e: Event) => {
       e.preventDefault()
       shell.openExternalSync(url)
     }
@@ -28,13 +26,13 @@ function initialize () {
     link.addEventListener('auxclick', openLinkExternally)
   }
 
-  document.querySelector('.electron-version').innerText = `Electron v${process.versions.electron}`
-  document.querySelector('.chrome-version').innerText = `Chromium v${process.versions.chrome}`
-  document.querySelector('.node-version').innerText = `Node v${process.versions.node}`
-  document.querySelector('.v8-version').innerText = `v8 v${process.versions.v8}`
-  document.querySelector('.command-example').innerText = `${electronPath} path-to-app`
+  document.querySelector<HTMLAnchorElement>('.electron-version')!.innerText = `Electron v${process.versions.electron}`
+  document.querySelector<HTMLAnchorElement>('.chrome-version')!.innerText = `Chromium v${process.versions.chrome}`
+  document.querySelector<HTMLAnchorElement>('.node-version')!.innerText = `Node v${process.versions.node}`
+  document.querySelector<HTMLAnchorElement>('.v8-version')!.innerText = `v8 v${process.versions.v8}`
+  document.querySelector<HTMLAnchorElement>('.command-example')!.innerText = `${electronPath} path-to-app`
 
-  function getOcticonSvg (name) {
+  function getOcticonSvg (name: string) {
     const octiconPath = path.resolve(__dirname, 'octicon', `${name}.svg`)
     if (fs.existsSync(octiconPath)) {
       const content = fs.readFileSync(octiconPath, 'utf8')
@@ -45,13 +43,15 @@ function initialize () {
     return null
   }
 
-  function loadSVG (element) {
+  function loadSVG (element: HTMLSpanElement) {
     for (const cssClass of element.classList) {
       if (cssClass.startsWith('octicon-')) {
         const icon = getOcticonSvg(cssClass.substr(8))
         if (icon) {
-          icon.classList = element.classList
-          element.parentNode.insertBefore(icon, element)
+          for (const elemClass of element.classList) {
+            icon.classList.add(elemClass)
+          }
+          element.before(icon)
           element.remove()
           break
         }
@@ -59,7 +59,7 @@ function initialize () {
     }
   }
 
-  for (const element of document.querySelectorAll('.octicon')) {
+  for (const element of document.querySelectorAll<HTMLSpanElement>('.octicon')) {
     loadSVG(element)
   }
 }

+ 107 - 0
filenames.auto.gni

@@ -0,0 +1,107 @@
+# THIS FILE IS AUTO-GENERATED, PLEASE DO NOT EDIT BY HAND
+auto_filenames = {
+  api_docs = [
+    "docs/api/accelerator.md",
+    "docs/api/app.md",
+    "docs/api/auto-updater.md",
+    "docs/api/breaking-changes.md",
+    "docs/api/browser-view.md",
+    "docs/api/browser-window-proxy.md",
+    "docs/api/browser-window.md",
+    "docs/api/chrome-command-line-switches.md",
+    "docs/api/client-request.md",
+    "docs/api/clipboard.md",
+    "docs/api/content-tracing.md",
+    "docs/api/cookies.md",
+    "docs/api/crash-reporter.md",
+    "docs/api/debugger.md",
+    "docs/api/desktop-capturer.md",
+    "docs/api/dialog.md",
+    "docs/api/download-item.md",
+    "docs/api/environment-variables.md",
+    "docs/api/file-object.md",
+    "docs/api/frameless-window.md",
+    "docs/api/global-shortcut.md",
+    "docs/api/in-app-purchase.md",
+    "docs/api/incoming-message.md",
+    "docs/api/ipc-main.md",
+    "docs/api/ipc-renderer.md",
+    "docs/api/locales.md",
+    "docs/api/menu-item.md",
+    "docs/api/menu.md",
+    "docs/api/native-image.md",
+    "docs/api/net-log.md",
+    "docs/api/net.md",
+    "docs/api/notification.md",
+    "docs/api/power-monitor.md",
+    "docs/api/power-save-blocker.md",
+    "docs/api/process.md",
+    "docs/api/promisification.md",
+    "docs/api/protocol.md",
+    "docs/api/remote.md",
+    "docs/api/sandbox-option.md",
+    "docs/api/screen.md",
+    "docs/api/session.md",
+    "docs/api/shell.md",
+    "docs/api/structures",
+    "docs/api/synopsis.md",
+    "docs/api/system-preferences.md",
+    "docs/api/touch-bar-button.md",
+    "docs/api/touch-bar-color-picker.md",
+    "docs/api/touch-bar-group.md",
+    "docs/api/touch-bar-label.md",
+    "docs/api/touch-bar-popover.md",
+    "docs/api/touch-bar-scrubber.md",
+    "docs/api/touch-bar-segmented-control.md",
+    "docs/api/touch-bar-slider.md",
+    "docs/api/touch-bar-spacer.md",
+    "docs/api/touch-bar.md",
+    "docs/api/tray.md",
+    "docs/api/web-contents.md",
+    "docs/api/web-frame.md",
+    "docs/api/web-request.md",
+    "docs/api/webview-tag.md",
+    "docs/api/window-open.md",
+    "docs/api/structures/bluetooth-device.md",
+    "docs/api/structures/certificate-principal.md",
+    "docs/api/structures/certificate.md",
+    "docs/api/structures/cookie.md",
+    "docs/api/structures/cpu-usage.md",
+    "docs/api/structures/crash-report.md",
+    "docs/api/structures/custom-scheme.md",
+    "docs/api/structures/desktop-capturer-source.md",
+    "docs/api/structures/display.md",
+    "docs/api/structures/file-filter.md",
+    "docs/api/structures/gpu-feature-status.md",
+    "docs/api/structures/io-counters.md",
+    "docs/api/structures/jump-list-category.md",
+    "docs/api/structures/jump-list-item.md",
+    "docs/api/structures/memory-info.md",
+    "docs/api/structures/memory-usage-details.md",
+    "docs/api/structures/mime-typed-buffer.md",
+    "docs/api/structures/notification-action.md",
+    "docs/api/structures/point.md",
+    "docs/api/structures/printer-info.md",
+    "docs/api/structures/process-metric.md",
+    "docs/api/structures/product.md",
+    "docs/api/structures/rectangle.md",
+    "docs/api/structures/referrer.md",
+    "docs/api/structures/remove-client-certificate.md",
+    "docs/api/structures/remove-password.md",
+    "docs/api/structures/scrubber-item.md",
+    "docs/api/structures/segmented-control-segment.md",
+    "docs/api/structures/shortcut-details.md",
+    "docs/api/structures/size.md",
+    "docs/api/structures/stream-protocol-response.md",
+    "docs/api/structures/task.md",
+    "docs/api/structures/thumbar-button.md",
+    "docs/api/structures/trace-categories-and-options.md",
+    "docs/api/structures/trace-config.md",
+    "docs/api/structures/transaction.md",
+    "docs/api/structures/upload-blob.md",
+    "docs/api/structures/upload-data.md",
+    "docs/api/structures/upload-file.md",
+    "docs/api/structures/upload-raw-data.md",
+    "docs/api/structures/web-source.md",
+  ]
+}

+ 8 - 5
filenames.gni

@@ -40,7 +40,7 @@ filenames = {
     "lib/browser/default-menu.js",
     "lib/browser/guest-view-manager.js",
     "lib/browser/guest-window-manager.js",
-    "lib/browser/init.js",
+    "lib/browser/init.ts",
     "lib/browser/ipc-main-internal-utils.js",
     "lib/browser/ipc-main-internal.js",
     "lib/browser/navigation-controller.js",
@@ -92,13 +92,16 @@ filenames = {
     "lib/worker/init.js",
   ]
 
-  default_app_sources = [
-    "default_app/default_app.js",
+  default_app_ts_sources = [
+    "default_app/default_app.ts",
+    "default_app/main.ts",
+    "default_app/renderer.ts",
+  ]
+
+  default_app_static_sources = [
     "default_app/icon.png",
     "default_app/index.html",
-    "default_app/main.js",
     "default_app/package.json",
-    "default_app/renderer.js",
     "default_app/styles.css",
   ]
 

+ 25 - 22
lib/browser/init.js → lib/browser/init.ts

@@ -1,11 +1,10 @@
-'use strict'
+import { Buffer } from 'buffer'
+import * as fs from 'fs'
+import * as path from 'path'
+import * as util from 'util'
+import * as v8 from 'v8'
 
-const { Buffer } = require('buffer')
-const fs = require('fs')
-const path = require('path')
-const util = require('util')
 const Module = require('module')
-const v8 = require('v8')
 
 // We modified the original process.argv to let node.js load the init.js,
 // we need to restore it here.
@@ -25,10 +24,10 @@ globalPaths.push(path.join(__dirname, 'api', 'exports'))
 if (process.platform === 'win32') {
   // Redirect node's console to use our own implementations, since node can not
   // handle console output when running as GUI program.
-  const consoleLog = function (...args) {
-    return process.log(util.format(...args) + '\n')
+  const consoleLog = (format: any, ...args: any[]) => {
+    return process.log(util.format(format, ...args) + '\n')
   }
-  const streamWrite = function (chunk, encoding, callback) {
+  const streamWrite: NodeJS.WritableStream['write'] = function (chunk: Buffer | string, encoding?: any, callback?: Function) {
     if (Buffer.isBuffer(chunk)) {
       chunk = chunk.toString(encoding)
     }
@@ -90,7 +89,7 @@ if (process.platform === 'win32') {
 }
 
 // Map process.exit to app.exit, which quits gracefully.
-process.exit = app.exit
+process.exit = app.exit as () => never
 
 // Load the RPC server.
 require('@electron/internal/browser/rpc-server')
@@ -103,13 +102,15 @@ require('@electron/internal/browser/guest-window-manager')
 let packagePath = null
 let packageJson = null
 const searchPaths = ['app', 'app.asar', 'default_app.asar']
-for (packagePath of searchPaths) {
-  try {
-    packagePath = path.join(process.resourcesPath, packagePath)
-    packageJson = require(path.join(packagePath, 'package.json'))
-    break
-  } catch (error) {
-    continue
+if (process.resourcesPath) {
+  for (packagePath of searchPaths) {
+    try {
+      packagePath = path.join(process.resourcesPath, packagePath)
+      packageJson = require(path.join(packagePath, 'package.json'))
+      break
+    } catch (error) {
+      continue
+    }
   }
 }
 
@@ -149,9 +150,6 @@ app.setPath('userData', path.join(app.getPath('appData'), app.getName()))
 app.setPath('userCache', path.join(app.getPath('cache'), app.getName()))
 app.setAppPath(packagePath)
 
-// Load the chrome devtools support.
-require('@electron/internal/browser/chrome-devtools')
-
 // Load the chrome extension support.
 require('@electron/internal/browser/chrome-extension')
 
@@ -199,5 +197,10 @@ const { setDefaultApplicationMenu } = require('@electron/internal/browser/defaul
 // Create default menu.
 app.once('ready', setDefaultApplicationMenu)
 
-// Finally load app's main.js and transfer control to C++.
-Module._load(path.join(packagePath, mainStartupScript), Module, true)
+if (packagePath) {
+  // Finally load app's main.js and transfer control to C++.
+  Module._load(path.join(packagePath, mainStartupScript), Module, true)
+} else {
+  console.error('Failed to locate a valid package to load (app, app.asar or default_app.asar)')
+  console.error('This normally means you\'ve damaged the Electron package somehow')
+}

+ 270 - 119
package-lock.json

@@ -34,9 +34,9 @@
           }
         },
         "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
           "dev": true,
           "requires": {
             "ansi-styles": "^3.2.1",
@@ -44,26 +44,11 @@
             "supports-color": "^5.3.0"
           }
         },
-        "has-flag": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
-          "dev": true
-        },
         "js-tokens": {
           "version": "4.0.0",
           "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
           "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
           "dev": true
-        },
-        "supports-color": {
-          "version": "5.5.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
         }
       }
     },
@@ -171,9 +156,9 @@
       }
     },
     "@types/node": {
-      "version": "7.10.2",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-7.10.2.tgz",
-      "integrity": "sha512-RO4ig5taKmcrU4Rex8ojG1gpwFkjddzug9iPQSDvbewHN9vDpcFewevkaOK+KT+w1LeZnxbgOyfXwV4pxsQ4GQ==",
+      "version": "10.12.21",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.21.tgz",
+      "integrity": "sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ==",
       "dev": true
     },
     "JSONStream": {
@@ -196,7 +181,7 @@
     "abbrev": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
-      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+      "integrity": "sha1-+PLIh60Qv2f2NPAFtph/7TF5qsg=",
       "dev": true
     },
     "accepts": {
@@ -591,6 +576,17 @@
         "mkdirp": "^0.5.0",
         "mksnapshot": "^0.3.4",
         "tmp": "0.0.28"
+      },
+      "dependencies": {
+        "tmp": {
+          "version": "0.0.28",
+          "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.28.tgz",
+          "integrity": "sha1-Fyc1t/YU6nrzlmT6hM8N5OUV0SA=",
+          "dev": true,
+          "requires": {
+            "os-tmpdir": "~1.0.1"
+          }
+        }
       }
     },
     "asn1": {
@@ -651,6 +647,12 @@
       "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
       "dev": true
     },
+    "astral-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
+      "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+      "dev": true
+    },
     "async": {
       "version": "1.5.2",
       "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
@@ -883,7 +885,7 @@
     "bn.js": {
       "version": "4.11.8",
       "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
-      "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
+      "integrity": "sha1-LN4J617jQfSEdGuwMJsyU7GxRC8=",
       "dev": true
     },
     "boolbase": {
@@ -1584,7 +1586,7 @@
     "cipher-base": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
-      "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+      "integrity": "sha1-h2Dk7MJy9MNjUy+SbYdKriwTl94=",
       "dev": true,
       "requires": {
         "inherits": "^2.0.1",
@@ -1594,7 +1596,7 @@
     "circular-json": {
       "version": "0.3.3",
       "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
-      "integrity": "sha1-gVyZ6oT2gJUp0vRXkb34JxE1LWY=",
+      "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
       "dev": true
     },
     "clang-format": {
@@ -2986,11 +2988,23 @@
         "typescript": "^2.2.1"
       },
       "dependencies": {
+        "@types/node": {
+          "version": "7.10.2",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-7.10.2.tgz",
+          "integrity": "sha512-RO4ig5taKmcrU4Rex8ojG1gpwFkjddzug9iPQSDvbewHN9vDpcFewevkaOK+KT+w1LeZnxbgOyfXwV4pxsQ4GQ==",
+          "dev": true
+        },
         "lodash": {
           "version": "4.17.11",
           "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
           "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
           "dev": true
+        },
+        "typescript": {
+          "version": "2.9.2",
+          "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz",
+          "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==",
+          "dev": true
         }
       }
     },
@@ -3114,21 +3128,21 @@
       }
     },
     "eslint": {
-      "version": "5.6.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.6.0.tgz",
-      "integrity": "sha512-/eVYs9VVVboX286mBK7bbKnO1yamUy2UCRjiY6MryhQL2PaaXCExsCQ2aO83OeYRhU2eCU/FMFP+tVMoOrzNrA==",
+      "version": "5.13.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.13.0.tgz",
+      "integrity": "sha512-nqD5WQMisciZC5EHZowejLKQjWGuFS5c70fxqSKlnDME+oz9zmE8KTlX+lHSg+/5wsC/kf9Q9eMkC8qS3oM2fg==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "^7.0.0",
         "ajv": "^6.5.3",
         "chalk": "^2.1.0",
         "cross-spawn": "^6.0.5",
-        "debug": "^3.1.0",
+        "debug": "^4.0.1",
         "doctrine": "^2.1.0",
         "eslint-scope": "^4.0.0",
         "eslint-utils": "^1.3.1",
         "eslint-visitor-keys": "^1.0.0",
-        "espree": "^4.0.0",
+        "espree": "^5.0.0",
         "esquery": "^1.0.1",
         "esutils": "^2.0.2",
         "file-entry-cache": "^2.0.0",
@@ -3136,9 +3150,9 @@
         "glob": "^7.1.2",
         "globals": "^11.7.0",
         "ignore": "^4.0.6",
+        "import-fresh": "^3.0.0",
         "imurmurhash": "^0.1.4",
         "inquirer": "^6.1.0",
-        "is-resolvable": "^1.1.0",
         "js-yaml": "^3.12.0",
         "json-stable-stringify-without-jsonify": "^1.0.1",
         "levn": "^0.3.0",
@@ -3148,21 +3162,31 @@
         "natural-compare": "^1.4.0",
         "optionator": "^0.8.2",
         "path-is-inside": "^1.0.2",
-        "pluralize": "^7.0.0",
         "progress": "^2.0.0",
-        "regexpp": "^2.0.0",
-        "require-uncached": "^1.0.3",
+        "regexpp": "^2.0.1",
         "semver": "^5.5.1",
         "strip-ansi": "^4.0.0",
         "strip-json-comments": "^2.0.1",
-        "table": "^4.0.3",
+        "table": "^5.0.2",
         "text-table": "^0.2.0"
       },
       "dependencies": {
+        "acorn": {
+          "version": "6.0.7",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.7.tgz",
+          "integrity": "sha512-HNJNgE60C9eOTgn974Tlp3dpLZdUr+SoxxDwPaY9J/kDNOLQTkaDgwBUXAF4SSsrAwD9RpdxuHK/EbuF+W9Ahw==",
+          "dev": true
+        },
+        "acorn-jsx": {
+          "version": "5.0.1",
+          "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz",
+          "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==",
+          "dev": true
+        },
         "ajv": {
-          "version": "6.5.3",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz",
-          "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==",
+          "version": "6.8.1",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.8.1.tgz",
+          "integrity": "sha512-eqxCp82P+JfqL683wwsL73XmFs1eG6qjw+RD3YHx+Jll1r0jNd4dh8QG9NYAeNGA/hnZjeEDgtTskgJULbxpWQ==",
           "dev": true,
           "requires": {
             "fast-deep-equal": "^2.0.1",
@@ -3187,9 +3211,9 @@
           }
         },
         "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
           "dev": true,
           "requires": {
             "ansi-styles": "^3.2.1",
@@ -3211,14 +3235,25 @@
           }
         },
         "debug": {
-          "version": "3.2.5",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz",
-          "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==",
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
           "dev": true,
           "requires": {
             "ms": "^2.1.1"
           }
         },
+        "espree": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.0.tgz",
+          "integrity": "sha512-1MpUfwsdS9MMoN7ZXqAr9e9UKdVHDcvrJpyx7mm1WuQlx/ygErEQBzgi5Nh5qBHIoYweprhtMkTCb9GhcAIcsA==",
+          "dev": true,
+          "requires": {
+            "acorn": "^6.0.2",
+            "acorn-jsx": "^5.0.0",
+            "eslint-visitor-keys": "^1.0.0"
+          }
+        },
         "esprima": {
           "version": "4.0.1",
           "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
@@ -3245,22 +3280,22 @@
             "path-is-absolute": "^1.0.0"
           }
         },
-        "has-flag": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
-          "dev": true
-        },
         "ignore": {
           "version": "4.0.6",
           "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
           "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
           "dev": true
         },
+        "is-fullwidth-code-point": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+          "dev": true
+        },
         "js-yaml": {
-          "version": "3.12.0",
-          "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
-          "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
+          "version": "3.12.1",
+          "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz",
+          "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==",
           "dev": true,
           "requires": {
             "argparse": "^1.0.7",
@@ -3279,12 +3314,33 @@
           "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
           "dev": true
         },
-        "semver": {
-          "version": "5.5.1",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz",
-          "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==",
+        "regexpp": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
+          "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
           "dev": true
         },
+        "slice-ansi": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
+          "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^3.2.0",
+            "astral-regex": "^1.0.0",
+            "is-fullwidth-code-point": "^2.0.0"
+          }
+        },
+        "string-width": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+          "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+          "dev": true,
+          "requires": {
+            "is-fullwidth-code-point": "^2.0.0",
+            "strip-ansi": "^4.0.0"
+          }
+        },
         "strip-ansi": {
           "version": "4.0.0",
           "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
@@ -3294,13 +3350,24 @@
             "ansi-regex": "^3.0.0"
           }
         },
-        "supports-color": {
-          "version": "5.5.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+        "table": {
+          "version": "5.2.2",
+          "resolved": "https://registry.npmjs.org/table/-/table-5.2.2.tgz",
+          "integrity": "sha512-f8mJmuu9beQEDkKHLzOv4VxVYlU68NpdzjbGPl69i4Hx0sTopJuNxuzJd17iV2h24dAfa93u794OnDA5jqXvfQ==",
           "dev": true,
           "requires": {
-            "has-flag": "^3.0.0"
+            "ajv": "^6.6.1",
+            "lodash": "^4.17.11",
+            "slice-ansi": "^2.0.0",
+            "string-width": "^2.1.1"
+          },
+          "dependencies": {
+            "lodash": {
+              "version": "4.17.11",
+              "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+              "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
+              "dev": true
+            }
           }
         }
       }
@@ -3516,6 +3583,15 @@
       "integrity": "sha512-OwxJkR6TQiYMmt1EsNRMe5qG3GsbjlcOhbGUBY4LtavF9DsLaTcoR+j2Tdjqi23oUwKNUqX7qcn5fPStafMdlA==",
       "dev": true
     },
+    "eslint-plugin-typescript": {
+      "version": "0.14.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-typescript/-/eslint-plugin-typescript-0.14.0.tgz",
+      "integrity": "sha512-2u1WnnDF2mkWWgU1lFQ2RjypUlmRoBEvQN02y9u+IL12mjWlkKFGEBnVsjs9Y8190bfPQCvWly1c2rYYUSOxWw==",
+      "dev": true,
+      "requires": {
+        "requireindex": "~1.1.0"
+      }
+    },
     "eslint-scope": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz",
@@ -3633,7 +3709,7 @@
     "evp_bytestokey": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
-      "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+      "integrity": "sha1-f8vbGY3HGVlDLv4ThCaE4FJaywI=",
       "dev": true,
       "requires": {
         "md5.js": "^1.3.4",
@@ -3725,15 +3801,6 @@
           "requires": {
             "safer-buffer": ">= 2.1.2 < 3"
           }
-        },
-        "tmp": {
-          "version": "0.0.33",
-          "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
-          "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
-          "dev": true,
-          "requires": {
-            "os-tmpdir": "~1.0.2"
-          }
         }
       }
     },
@@ -4184,7 +4251,7 @@
     "find-root": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
-      "integrity": "sha1-q8/Iunb3CMQql7PWhbfpRQv7nOQ=",
+      "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
       "dev": true
     },
     "find-up": {
@@ -5259,7 +5326,7 @@
     "gunzip-maybe": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/gunzip-maybe/-/gunzip-maybe-1.4.1.tgz",
-      "integrity": "sha512-qtutIKMthNJJgeHQS7kZ9FqDq59/Wn0G2HYCRNjpup7yKfVI6/eqwpmroyZGFoCYaG+sW6psNVb4zoLADHpp2g==",
+      "integrity": "sha1-Occu2J0bSbpwjhh3ZQBIiQKlICc=",
       "dev": true,
       "requires": {
         "browserify-zlib": "^0.1.4",
@@ -5564,6 +5631,16 @@
       "integrity": "sha512-pUh+xUQQhQzevjRHHFqqcTy0/dP/kS9I8HSrUydhihjuD09W6ldVWFtIrwhXdUJHis3i2rZNqEHpZH/cbinFbg==",
       "dev": true
     },
+    "import-fresh": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz",
+      "integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==",
+      "dev": true,
+      "requires": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      }
+    },
     "import-lazy": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
@@ -5614,26 +5691,32 @@
       }
     },
     "inquirer": {
-      "version": "6.2.0",
-      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz",
-      "integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==",
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz",
+      "integrity": "sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==",
       "dev": true,
       "requires": {
-        "ansi-escapes": "^3.0.0",
-        "chalk": "^2.0.0",
+        "ansi-escapes": "^3.2.0",
+        "chalk": "^2.4.2",
         "cli-cursor": "^2.1.0",
         "cli-width": "^2.0.0",
-        "external-editor": "^3.0.0",
+        "external-editor": "^3.0.3",
         "figures": "^2.0.0",
-        "lodash": "^4.17.10",
+        "lodash": "^4.17.11",
         "mute-stream": "0.0.7",
         "run-async": "^2.2.0",
-        "rxjs": "^6.1.0",
+        "rxjs": "^6.4.0",
         "string-width": "^2.1.0",
-        "strip-ansi": "^4.0.0",
+        "strip-ansi": "^5.0.0",
         "through": "^2.3.6"
       },
       "dependencies": {
+        "ansi-escapes": {
+          "version": "3.2.0",
+          "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
+          "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
+          "dev": true
+        },
         "ansi-regex": {
           "version": "3.0.0",
           "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
@@ -5650,9 +5733,9 @@
           }
         },
         "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
           "dev": true,
           "requires": {
             "ansi-styles": "^3.2.1",
@@ -5669,18 +5752,18 @@
             "restore-cursor": "^2.0.0"
           }
         },
-        "has-flag": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-          "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
-          "dev": true
-        },
         "is-fullwidth-code-point": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
           "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
           "dev": true
         },
+        "lodash": {
+          "version": "4.17.11",
+          "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+          "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
+          "dev": true
+        },
         "onetime": {
           "version": "2.0.1",
           "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
@@ -5708,24 +5791,34 @@
           "requires": {
             "is-fullwidth-code-point": "^2.0.0",
             "strip-ansi": "^4.0.0"
+          },
+          "dependencies": {
+            "strip-ansi": {
+              "version": "4.0.0",
+              "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+              "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+              "dev": true,
+              "requires": {
+                "ansi-regex": "^3.0.0"
+              }
+            }
           }
         },
         "strip-ansi": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^3.0.0"
-          }
-        },
-        "supports-color": {
-          "version": "5.5.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz",
+          "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==",
           "dev": true,
           "requires": {
-            "has-flag": "^3.0.0"
+            "ansi-regex": "^4.0.0"
+          },
+          "dependencies": {
+            "ansi-regex": {
+              "version": "4.0.0",
+              "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
+              "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+              "dev": true
+            }
           }
         }
       }
@@ -7348,6 +7441,12 @@
       "integrity": "sha1-EjBkIvYzJK7YSD0/ODMrX2cFR6A=",
       "dev": true
     },
+    "lodash.unescape": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz",
+      "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=",
+      "dev": true
+    },
     "lodash.uniq": {
       "version": "4.5.0",
       "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
@@ -7852,7 +7951,7 @@
     "miller-rabin": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
-      "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
+      "integrity": "sha1-8IA1HIZbDcViqEYpZtqlNUPHik0=",
       "dev": true,
       "requires": {
         "bn.js": "^4.0.0",
@@ -8545,6 +8644,23 @@
       "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=",
       "dev": true
     },
+    "parent-module": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.0.tgz",
+      "integrity": "sha512-8Mf5juOMmiE4FcmzYc4IaiS9L3+9paz2KOiXzkRviCP6aDmN49Hz6EMWz0lGNp9pX80GvvAuLADtyGfW/Em3TA==",
+      "dev": true,
+      "requires": {
+        "callsites": "^3.0.0"
+      },
+      "dependencies": {
+        "callsites": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz",
+          "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==",
+          "dev": true
+        }
+      }
+    },
     "parents": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz",
@@ -10176,6 +10292,12 @@
         }
       }
     },
+    "requireindex": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.1.0.tgz",
+      "integrity": "sha1-5UBLgVV+91225JxacgBIk/4D4WI=",
+      "dev": true
+    },
     "resolve": {
       "version": "1.7.1",
       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz",
@@ -10280,9 +10402,9 @@
       "dev": true
     },
     "rxjs": {
-      "version": "6.3.2",
-      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.2.tgz",
-      "integrity": "sha512-hV7criqbR0pe7EeL3O66UYVg92IR0XsA97+9y+BWTePK9SKmEI5Qd3Zj6uPnGkNzXsBywBQWTvujPl+1Kn9Zjw==",
+      "version": "6.4.0",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz",
+      "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==",
       "dev": true,
       "requires": {
         "tslib": "^1.9.0"
@@ -10312,7 +10434,7 @@
     "sax": {
       "version": "1.2.4",
       "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
-      "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+      "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=",
       "dev": true,
       "optional": true
     },
@@ -10824,7 +10946,7 @@
     "split": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
-      "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
+      "integrity": "sha1-YFvZvjA6pZ+zX5Ip++oN3snqB9k=",
       "dev": true,
       "requires": {
         "through": "2"
@@ -10842,7 +10964,7 @@
     "split2": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz",
-      "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==",
+      "integrity": "sha1-GGsldbz4PoW30YRldWI47k7kJJM=",
       "dev": true,
       "requires": {
         "through2": "^2.0.2"
@@ -12024,12 +12146,12 @@
       }
     },
     "tmp": {
-      "version": "0.0.28",
-      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.28.tgz",
-      "integrity": "sha1-Fyc1t/YU6nrzlmT6hM8N5OUV0SA=",
+      "version": "0.0.33",
+      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+      "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
       "dev": true,
       "requires": {
-        "os-tmpdir": "~1.0.1"
+        "os-tmpdir": "~1.0.2"
       }
     },
     "to-arraybuffer": {
@@ -12270,11 +12392,40 @@
       "dev": true
     },
     "typescript": {
-      "version": "2.9.2",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz",
-      "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==",
+      "version": "3.1.6",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.6.tgz",
+      "integrity": "sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==",
       "dev": true
     },
+    "typescript-eslint-parser": {
+      "version": "21.0.2",
+      "resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-21.0.2.tgz",
+      "integrity": "sha512-u+pj4RVJBr4eTzj0n5npoXD/oRthvfUCjSKndhNI714MG0mQq2DJw5WP7qmonRNIFgmZuvdDOH3BHm9iOjIAfg==",
+      "dev": true,
+      "requires": {
+        "eslint-scope": "^4.0.0",
+        "eslint-visitor-keys": "^1.0.0",
+        "typescript-estree": "5.3.0"
+      }
+    },
+    "typescript-estree": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/typescript-estree/-/typescript-estree-5.3.0.tgz",
+      "integrity": "sha512-Vu0KmYdSCkpae+J48wsFC1ti19Hq3Wi/lODUaE+uesc3gzqhWbZ5itWbsjylLVbjNW4K41RqDzSfnaYNbmEiMQ==",
+      "dev": true,
+      "requires": {
+        "lodash.unescape": "4.0.1",
+        "semver": "5.5.0"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
+          "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
+          "dev": true
+        }
+      }
+    },
     "uc.micro": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.5.tgz",

+ 13 - 3
package.json

@@ -5,6 +5,7 @@
   "description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
   "devDependencies": {
     "@octokit/rest": "^16.3.2",
+    "@types/node": "^10.12.21",
     "aliasify": "^2.1.0",
     "asar": "^0.14.6",
     "browserify": "^16.2.3",
@@ -15,9 +16,10 @@
     "dugite": "^1.45.0",
     "electron-docs-linter": "^2.4.0",
     "electron-typescript-definitions": "^6.0.0",
-    "eslint": "^5.6.0",
+    "eslint": "^5.13.0",
     "eslint-config-standard": "^12.0.0",
     "eslint-plugin-mocha": "^5.2.0",
+    "eslint-plugin-typescript": "^0.14.0",
     "folder-hash": "^2.1.1",
     "husky": "^0.14.3",
     "lint": "^1.1.2",
@@ -33,7 +35,9 @@
     "serve": "^6.5.8",
     "standard-markdown": "^5.0.0",
     "sumchecker": "^2.0.2",
-    "temp": "^0.8.3"
+    "temp": "^0.8.3",
+    "typescript": "~3.1.1",
+    "typescript-eslint-parser": "^21.0.0"
   },
   "private": true,
   "scripts": {
@@ -53,13 +57,15 @@
     "lint:js-in-markdown": "standard-markdown docs",
     "create-api-json": "electron-docs-linter docs --outfile=electron-api.json",
     "create-typescript-definitions": "npm run create-api-json && electron-typescript-definitions --in=electron-api.json --out=electron.d.ts",
+    "gn-typescript-definitions": "npm run create-typescript-definitions && cp electron.d.ts",
     "preinstall": "node -e 'process.exit(0)'",
     "precommit": "lint-staged",
     "prepack": "check-for-leaks",
     "prepush": "check-for-leaks",
     "repl": "node ./script/start.js --interactive",
     "start": "node ./script/start.js",
-    "test": "node ./script/spec-runner.js electron/spec"
+    "test": "node ./script/spec-runner.js electron/spec",
+    "tsc": "tsc"
   },
   "license": "MIT",
   "author": "Electron Community",
@@ -90,6 +96,10 @@
     "*.py": [
       "node script/lint.js --py --fix --only --",
       "git add"
+    ],
+    "docs/api/*.md": [
+      "node script/gen-filenames.js",
+      "git add"
     ]
   }
 }

+ 22 - 0
script/gen-filenames.js

@@ -0,0 +1,22 @@
+const fs = require('fs')
+const path = require('path')
+
+const gniPath = path.resolve(__dirname, '../filenames.auto.gni')
+
+const allDocs = fs.readdirSync(path.resolve(__dirname, '../docs/api'))
+  .map(doc => `docs/api/${doc}`)
+  .concat(
+    fs.readdirSync(path.resolve(__dirname, '../docs/api/structures'))
+      .map(doc => `docs/api/structures/${doc}`)
+  )
+
+fs.writeFileSync(
+  gniPath,
+  `# THIS FILE IS AUTO-GENERATED, PLEASE DO NOT EDIT BY HAND
+auto_filenames = {
+  api_docs = [
+${allDocs.map(doc => `    "${doc}",`).join('\n')}
+  ]
+}
+`
+)

+ 2 - 2
script/lint.js

@@ -75,10 +75,10 @@ const LINTERS = [ {
   key: 'javascript',
   roots: ['lib', 'spec', 'script', 'default_app'],
   ignoreRoots: ['spec/node_modules'],
-  test: filename => filename.endsWith('.js'),
+  test: filename => filename.endsWith('.js') || filename.endsWith('.ts'),
   run: (opts, filenames) => {
     const cmd = path.join(SOURCE_ROOT, 'node_modules', '.bin', 'eslint')
-    const args = [ '--cache', ...filenames ]
+    const args = [ '--cache', '--ext', '.js,.ts', ...filenames ]
     if (opts.fix) args.unshift('--fix')
     spawnAndCheckExitCode(cmd, args, { cwd: SOURCE_ROOT })
   }

+ 13 - 0
tsconfig.default_app.json

@@ -0,0 +1,13 @@
+{
+	"extends": "./tsconfig.json",
+	"compilerOptions": {
+		"rootDir": "default_app",
+	},
+	"exclude": [
+		"script",
+		"spec",
+		"tools",
+		"lib",
+		"npm"
+	]
+}

+ 14 - 0
tsconfig.electron.json

@@ -0,0 +1,14 @@
+{
+	"extends": "./tsconfig.json",
+	"compilerOptions": {
+		"rootDir": "lib",
+	},
+	"exclude": [
+		"script",
+		"spec",
+		"tools",
+		"default_app",
+		"npm",
+		"electron.d.ts"
+	]
+}

+ 23 - 0
tsconfig.json

@@ -0,0 +1,23 @@
+{
+	"compilerOptions": {
+		"module": "commonjs",
+		"target": "es2017",
+		"lib": [
+			"es2017",
+			"dom",
+			"dom.iterable"
+		],
+		"sourceMap": true,
+		"experimentalDecorators": false,
+		"strict": true,
+		"baseUrl": ".",
+		"allowJs": true,
+		"outDir": "ts-gen",
+		"paths": {
+			"@electron-internal/*": ["lib"]
+		}
+	},
+	"exclude": [
+		"electron.d.ts"
+	]
+}

+ 17 - 0
typings/internal-ambient.d.ts

@@ -0,0 +1,17 @@
+declare namespace NodeJS {
+  interface FeaturesBinding {
+    isDesktopCapturerEnabled(): boolean;
+    isOffscreenRenderingEnabled(): boolean;
+    isPDFViewerEnabled(): boolean;
+    isRunAsNodeEnabled(): boolean;
+    isFakeLocationProviderEnabled(): boolean;
+    isViewApiEnabled(): boolean;
+    isTtsEnabled(): boolean;
+    isPrintingEnabled(): boolean;
+  }
+  interface Process {
+    atomBinding(name: string): any;
+    atomBinding(name: 'features'): FeaturesBinding;
+    log: NodeJS.WriteStream['write'];
+  }
+}

+ 14 - 0
typings/internal-electron.d.ts

@@ -0,0 +1,14 @@
+/// <reference path="../electron.d.ts" />
+
+ /**
+ * This file augments the Electron TS namespace with the internal APIs
+ * that are not documented but are used by Electron internally
+ */
+
+declare namespace Electron {
+  interface App {
+    setVersion(version: string): void;
+    setDesktopName(name: string): void;
+    setAppPath(path: string | null): void;
+  }
+}