Install scripts, build app with rollup & configurable mirror app id

This commit is contained in:
Benjamin Dobell
2018-09-20 02:42:50 +10:00
parent c4ed13fb0b
commit 131935507a
24 changed files with 15381 additions and 663 deletions

3
.gitignore vendored
View File

@@ -3,3 +3,6 @@ dist/
app/node_modules/
ext/node_modules/
test/ChromeProfile/
.idea/
*.iml
.DS_Store

View File

@@ -30,14 +30,9 @@ Only tested on Linux. mDNS library issue to be fixed. `mdns` only works on Windo
git clone https://github.com/hensm/caster.git
npm install
npm run build
npm run install-manifest
````
Installer scripts aren't written yet, so registering the native messaging manifest with Firefox and specifiying the path must be done manually:
[MDN: Native Manifests](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Native_manifests)
`path` key within `app/caster_bridge.json` must be set to absolute path of `app/src/launcher.sh` or `app/src/launcher.bat`. Then, the manifest must be either moved to the correct location or the path added to the registry (Windows):
[MDN: Native Manifests # Manifest location](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Native_manifests#Manifest_location)
### Testing
Testing requires geckodriver (or chromedriver for Chrome parity testing). See [selenium-webdriver](https://www.npmjs.com/package/selenium-webdriver#installation) installation instructions (ignore `npm install`).

18
app/.babelrc Normal file
View File

@@ -0,0 +1,18 @@
{
"presets": [
[
"@babel/preset-env"
, {
"targets": {
"node": "current"
}
}
]
],
"plugins": [
"@babel/plugin-syntax-dynamic-import"
, "@babel/plugin-syntax-import-meta"
, "@babel/plugin-proposal-class-properties"
, "@babel/plugin-proposal-json-strings"
]
}

View File

@@ -1,3 +0,0 @@
#!/bin/sh
# TODO

3370
app/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,38 @@
{
"dependencies": {
"castv2": "^0.1.9",
"mdns-js": "^1.0.1"
}
"name": "fx_cast_bridge",
"license": "MIT",
"version": "0.1.0-alpha1",
"scripts": {
"build": "node scripts/build.js",
"install-manifest": "node scripts/install-manifest.js",
"start-debug": "node scripts/start-debug.js",
"start": "node dist/app.js"
},
"dependencies": {
"@babel/runtime": "^7.0.0",
"castv2": "^0.1.9",
"mdns-js": "^1.0.1",
"mkdirp": "^0.5.1"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/plugin-proposal-class-properties": "^7.0.0",
"@babel/plugin-proposal-json-strings": "^7.0.0",
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
"@babel/plugin-syntax-import-meta": "^7.0.0",
"@babel/plugin-transform-runtime": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/register": "^7.0.0",
"magic-string": "^0.25.0",
"minimist": "^1.2.0",
"rollup": "^0.66.0",
"rollup-plugin-babel": "^4.0.3",
"rollup-plugin-commonjs": "^9.1.3",
"rollup-plugin-json": "^3.1.0",
"rollup-plugin-node-builtins": "^2.1.2",
"rollup-plugin-node-resolve": "^3.4.0"
},
"optionalDependencies": {
"regedit": "^3.0.0"
}
}

View File

@@ -0,0 +1,34 @@
import MagicString from 'magic-string';
const MEMCPY_REQUIRE_STRING = 'require("memcpy")';
const REPLACEMENT_STRING = 'null';
// We're doing this because memcpy is an optional dependency of ByteBufferNB,
// but Rollup doesn't understand optional dependencies and tries to use it.
// We can't simply use memcpy, because it has native code for older versions of
// Node and no longer builds!
export default (variant) => {
let mainId = null;
return {
name: 'no-memcpy-plugin'
, transform: (code, id) => {
if (id.endsWith('ByteBufferNB.js')) {
const start = code.indexOf(MEMCPY_REQUIRE_STRING);
if (start >= 0) {
const magicString = new MagicString(code);
const end = start + MEMCPY_REQUIRE_STRING.length;
magicString.overwrite(start, end, REPLACEMENT_STRING);
return {
code: magicString.toString()
, map: magicString.generateMap()
};
}
}
return null;
}
}
}

View File

@@ -0,0 +1,56 @@
import { resolve } from 'path'
import babel from 'rollup-plugin-babel'
import builtins from 'rollup-plugin-node-builtins'
import commonjs from 'rollup-plugin-commonjs'
import json from 'rollup-plugin-json'
import nodeResolve from 'rollup-plugin-node-resolve'
import noMemcpy from './no-memcpy-plugin'
export default options => {
// TODO: Enabling this option presently doesn't work because castv2 proto
// files aren't bundled, can be fixed with a couple of plugins
const external = options.dependencies ? () => false : (id, parentId) => {
if (!parentId) {
return true;
}
return !!id.match(/^[^./].+$/)
|| resolve(parentId, '..', id).includes('/node_modules/');
};
return {
external
, input: './src/js/main.js'
, plugins: [
noMemcpy()
, babel({
exclude: [
'node_modules/**'
]
, runtimeHelpers: true
})
, json()
, builtins()
, nodeResolve({
module: true
, jsnext: true
, main: true
, browser: false
})
, commonjs({
// Protobuf detects itself running in Node by attempting to
// require fs, so we better allow it...
ignore: ['fs']
})
]
, output: [
{
file: resolve(__dirname, '../../dist/app/app.js')
, format: 'cjs'
, sourcemap: options.sourcemap || false
}
]
};
}

48
app/scripts/build.js Normal file
View File

@@ -0,0 +1,48 @@
const fs = require('fs');
const os = require('os');
const path = require('path');
require('@babel/register');
const argv = require('minimist')(process.argv.slice(2));
const rollup = require('rollup');
const config = require('../rollup/rollup.config').default;
const MANIFEST_NAME = 'fx_cast_bridge.json';
async function build() {
const {path: executablePath, platform, ...configOptions} = argv;
const {output: bundleOutputs, ...bundleOptions} = config(configOptions);
const bundle = await rollup.rollup(bundleOptions);
for (const output of bundleOutputs) {
await bundle.write(output);
fs.chmodSync(output.file, '755');
}
const targetPlatform = platform || os.type();
const launcherExt = targetPlatform.toLowerCase().startsWith('win')
? 'bat'
: 'sh';
const launcherName = `launcher.${launcherExt}`;
const launcherPath = path.join(__dirname, '../../dist', launcherName);
fs.copyFileSync(path.join(`src`, launcherName), launcherPath);
const manifest = {
...(JSON.parse(fs.readFileSync(`src/${MANIFEST_NAME}`, 'utf8')))
, path: (executablePath || path.resolve(launcherPath))
};
fs.writeFileSync(
path.join('../dist/app', MANIFEST_NAME)
, JSON.stringify(manifest, null, 4)
);
}
build().catch(e => {
console.log("Build failed", e);
process.exit(1);
});

View File

@@ -0,0 +1,69 @@
const fs = require('fs');
const os = require('os');
const path = require('path');
const argv = require('minimist')(process.argv.slice(2));
const mkdirpSync = require('mkdirp').sync;
const MANIFEST_NAME = 'fx_cast_bridge.json';
const MANIFEST_PATH = path.resolve(__dirname, '../../dist/app', MANIFEST_NAME);
const WIN_REGISTRY_KEY = 'fx_cast_bridge';
let destination = argv.destination;
switch (os.type()) {
case 'Darwin': {
if (!destination) {
const root = argv.root || process.env.HOME;
destination = path.resolve(
path.join(
root
, 'Library/Application Support/Mozilla/NativeMessagingHosts'
)
);
}
break;
}
case 'Linux': {
if (!destination) {
const root = argv.root || `${process.env.HOME}/.`;
destination = root.endsWith('/.')
? `${root}mozilla/native-messaging-hosts/`
: path.join(root, 'mozilla/native-messaging-hosts/');
}
break;
}
case 'Windows_NT': {
const regedit = require('regedit');
const destinationManifestPath = path.join(destination, MANIFEST_NAME)
|| MANIFEST_PATH;
regedit.putValue({
'HKEY_CURRENT_USER\\SOFTWARE\\Mozilla\\NativeMessagingHosts': {
[WIN_REGISTRY_KEY]: {
value: destinationManifestPath
, type: 'REG_DEFAULT'
}
}
});
break;
}
default:
console.error('Sorry, this installer does not yet support your OS');
process.exit(1);
}
if (destination) {
mkdirpSync(destination);
fs.copyFileSync(
MANIFEST_PATH
, path.join(destination, MANIFEST_NAME)
);
}

View File

@@ -0,0 +1,21 @@
require('@babel/register')({
presets: [
[
"@babel/preset-env"
, {
targets: {
node: "current"
}
}
]
]
, plugins: [
"@babel/plugin-transform-runtime"
, "@babel/plugin-syntax-dynamic-import"
, "@babel/plugin-syntax-import-meta"
, "@babel/plugin-proposal-class-properties"
, "@babel/plugin-proposal-json-strings"
]
});
require('../src/js/main');

View File

@@ -1,14 +1,13 @@
"use strict";
import { Client } from 'castv2'
import { createBrowser, tcp } from 'mdns-js'
const { Client } = require("castv2");
const mdns = require("mdns-js");
const http = require("http");
const fs = require("fs");
import http from 'http'
import fs from 'fs'
const transforms = require("./transforms");
import * as transforms from './transforms'
const browser = mdns.createBrowser(mdns.tcp("googlecast"));
const browser = createBrowser(tcp("googlecast"));
// Local media server
let httpServer;

View File

@@ -100,7 +100,7 @@ const encode = new Transform({
});
module.exports = {
export {
response
, decode
, encode

View File

@@ -1,4 +1,4 @@
#!/bin/sh
AVAHI_COMPAT_NOWARN=1 node main.js
#!/usr/bin/env bash
BASEDIR=$(dirname $0)
PATH=/usr/local/bin:$PATH
AVAHI_COMPAT_NOWARN=1 node $BASEDIR/app.js

View File

@@ -1,3 +0,0 @@
#!/bin/sh
# TODO

20
ext/build.js Normal file
View File

@@ -0,0 +1,20 @@
const spawn = require('child_process').spawn;
const argv = require('minimist')(process.argv.slice(2));
const appId = argv.appId || "19A6F4AE";
const child = spawn(
`webpack --env.appId=${appId} `
+ '&& web-ext build '
+ '--overwrite-dest '
+ '--source-dir '
+ '../dist/ext/unpacked '
+ '--artifacts-dir '
+ '../dist/ext '
+ '&& mv ../dist/ext/*.zip ../dist/ext/ext.xpi'
, {
shell: true
}
);
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);

10776
ext/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"scripts": {
"build": "webpack && web-ext build --overwrite-dest --source-dir ../dist/unpacked --artifacts-dir ../dist && mv ../dist/*.zip ../dist/ext.xpi",
"build": "node build.js",
"watch": "webpack -d --watch"
},
"devDependencies": {
@@ -12,6 +12,7 @@
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
"copy-webpack-plugin": "^4.3.1",
"minimist": "^1.2.0",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"web-ext": "^2.7.0",

View File

@@ -148,7 +148,7 @@ browser.menus.onClicked.addListener(async (info, tab) => {
mirrorCastFrameId = frameId;
await browser.tabs.executeScript(tab.id, {
code: `const selectedMedia = "${info.pageUrl ? "tab" : "screen"}";`
code: `let selectedMedia = "${info.pageUrl ? "tab" : "screen"}";`
, frameId
});
@@ -192,8 +192,30 @@ const bridgeMap = new Map();
* forwarding.
*/
function initBridge (tabId, frameId) {
const existingPort = bridgeMap.get(tabId);
if (existingPort) {
existingPort.disconnect();
bridgeMap.delete(tabId);
}
const port = browser.runtime.connectNative("fx_cast_bridge");
bridgeMap.set(tabId, port);
if (port.error) {
console.error("Failed connect to fx_cast_bridge:", port.error.message);
} else {
bridgeMap.set(tabId, port);
}
port.onDisconnect.addListener(p => {
if (p.error) {
console.error("fx_cast_bridge disconnected:", p.error.message);
} else {
console.log("fx_cast_bridge disconnected");
}
bridgeMap.delete(tabId);
});
port.onMessage.addListener(message => {
// Forward shim: messages

View File

@@ -3,7 +3,9 @@
let chrome;
let logMessage;
const FX_CAST_RECEIVER_APP_ID = "19A6F4AE";
const FX_CAST_RECEIVER_APP_ID = typeof MIRROR_CAST_APP_ID !== "undefined"
? MIRROR_CAST_APP_ID
: "19A6F4AE";
const FX_CAST_NAMESPACE = "urn:x-cast:fx_cast";
let session;

View File

@@ -6,9 +6,9 @@ const webpack_copy = require("copy-webpack-plugin");
const include_path = path.resolve(__dirname, "src");
const output_path = path.resolve(__dirname, "../dist/unpacked");
const output_path = path.resolve(__dirname, "../dist/ext/unpacked");
module.exports = {
module.exports = (env) => ({
entry: {
"main" : `${include_path}/main.js`
, "popup/bundle" : `${include_path}/popup/index.js`
@@ -28,6 +28,7 @@ module.exports = {
//, new webpack.optimize.CommonsChunkPlugin("lib/init.bundle")
new webpack.DefinePlugin({
"process.env.NODE_ENV": `"production"`
, "MIRROR_CAST_APP_ID": JSON.stringify(env.appId.toString() || "19A6F4AE")
})
// Ext copy assets
@@ -37,7 +38,7 @@ module.exports = {
, ignore: [ "*.js" ]
}])
]
, devtool: "source-map"
, devtool: "eval-source-map"
, module: {
loaders: [
{
@@ -55,4 +56,4 @@ module.exports = {
}
]
}
};
});

258
package-lock.json generated Normal file
View File

@@ -0,0 +1,258 @@
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true
},
"core-js": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz",
"integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=",
"dev": true
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true
},
"es6-promise": {
"version": "3.0.2",
"resolved": "http://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz",
"integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=",
"dev": true
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
"glob": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
"integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=",
"dev": true
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"dev": true
},
"jasmine": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.2.0.tgz",
"integrity": "sha512-qv6TZ32r+slrQz8fbx2EhGbD9zlJo3NwPrpLK1nE8inILtZO9Fap52pyHk7mNTh4tG50a+1+tOiWVT3jO5I0Sg==",
"dev": true,
"requires": {
"glob": "^7.0.6",
"jasmine-core": "~3.2.0"
}
},
"jasmine-core": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.2.1.tgz",
"integrity": "sha512-pa9tbBWgU0EE4SWgc85T4sa886ufuQdsgruQANhECYjwqgV4z7Vw/499aCaP8ZH79JDS4vhm8doDG9HO4+e4sA==",
"dev": true
},
"jszip": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.5.tgz",
"integrity": "sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ==",
"dev": true,
"requires": {
"core-js": "~2.3.0",
"es6-promise": "~3.0.2",
"lie": "~3.1.0",
"pako": "~1.0.2",
"readable-stream": "~2.0.6"
}
},
"lie": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
"integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=",
"dev": true,
"requires": {
"immediate": "~3.0.5"
}
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"requires": {
"wrappy": "1"
}
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"dev": true
},
"pako": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz",
"integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==",
"dev": true
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
"process-nextick-args": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
"integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
"dev": true
},
"readable-stream": {
"version": "2.0.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
"integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
"dev": true,
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.1",
"isarray": "~1.0.0",
"process-nextick-args": "~1.0.6",
"string_decoder": "~0.10.x",
"util-deprecate": "~1.0.1"
}
},
"rimraf": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
"integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
"dev": true,
"requires": {
"glob": "^7.0.5"
}
},
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
"dev": true
},
"selenium-webdriver": {
"version": "4.0.0-alpha.1",
"resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.0.0-alpha.1.tgz",
"integrity": "sha512-z88rdjHAv3jmTZ7KSGUkTvo4rGzcDGMq0oXWHNIDK96Gs31JKVdu9+FMtT4KBrVoibg8dUicJDok6GnqqttO5Q==",
"dev": true,
"requires": {
"jszip": "^3.1.3",
"rimraf": "^2.5.4",
"tmp": "0.0.30",
"xml2js": "^0.4.17"
}
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
"dev": true
},
"tmp": {
"version": "0.0.30",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz",
"integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=",
"dev": true,
"requires": {
"os-tmpdir": "~1.0.1"
}
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"dev": true
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
},
"xml2js": {
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
"integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
"dev": true,
"requires": {
"sax": ">=0.6.0",
"xmlbuilder": "~9.0.1"
}
},
"xmlbuilder": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
"integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=",
"dev": true
}
}
}

View File

@@ -3,8 +3,9 @@
"postinstall": "npm run postinstall:app && npm run postinstall:ext",
"postinstall:app": "npm install ./app --prefix ./app",
"postinstall:ext": "npm install ./ext --prefix ./ext",
"build": "npm run build --prefix ./ext",
"test": "jasmine --config=jasmine.json"
"build": "npm run build --prefix ./app && npm run build --prefix ./ext --",
"test": "jasmine --config=jasmine.json",
"install-manifest": "npm run install-manifest --prefix ./app"
},
"devDependencies": {
"jasmine": "^3.1.0",