Skip to content

Commit

Permalink
Overhaul and refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel Brain committed Feb 5, 2019
1 parent 88c82f1 commit 675ddec
Show file tree
Hide file tree
Showing 47 changed files with 1,089 additions and 1,323 deletions.
3 changes: 0 additions & 3 deletions .babelrc

This file was deleted.

1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
1 change: 1 addition & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
.*/node_modules/flow-runtime
.*/node_modules/npm
.*/node_modules/**/test
.*/node_modules/jsonlint
.*/**/dist/module
[include]
[libs]
Expand Down
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
language: node_js
node_js:
- "6"
- "8"
script: npm test
32 changes: 0 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,14 +161,6 @@ postRobot.send(someWindow, 'getUser', { id: 1337 }, { domain: 'http://zombo.com'
});
```

## Send a message to the direct parent

```javascript
postRobot.sendToParent('getUser').then(function(event) {
console.log(event.data);
});
```

## Async / Await

```javascript
Expand Down Expand Up @@ -216,30 +208,6 @@ postRobot.send(someWindow, 'getUser', { id: 1337 }, { domain: 'http://zombo.com'
});
```

You can even set up a listener and sender instance in advance:

```javascript
var listener = postRobot.listener({ window: childWindow, domain: 'http://zombo.com' });

listener.on('getUser', function(event) {
return {
id: event.data.id,
name: 'Frodo'
};
});
```

```javascript
var client = postRobot.client({ window: someWindow, domain: 'http://zombo.com' });

client.send('getUser', { id: 1337 }).then(function(event) {
console.log(event.source, event.origin, 'Got user:', event.data.name);

}).catch(function(err) {
console.error(err);
});
```

## Functions

Post robot lets you send across functions in your data payload, fairly seamlessly.
Expand Down
6 changes: 6 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* @flow */

// eslint-disable-next-line import/no-commonjs
module.exports = {
extends: 'grumbler-scripts/config/.babelrc-node'
};
1 change: 1 addition & 0 deletions globals.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

module.exports = {
'__POST_ROBOT__': {
__AUTO_SETUP__: true,
__IE_POPUP_SUPPORT__: true,
__GLOBAL_MESSAGE_SUPPORT__: true
}
Expand Down
3 changes: 2 additions & 1 deletion karma.conf.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
/* @flow */
/* eslint import/no-default-export: off */

import { getKarmaConfig } from 'grumbler-scripts/config/karma.conf';

import { WEBPACK_CONFIG_TEST } from './webpack.config';

export default function configKarma(karma : Object) {

let karmaConfig = getKarmaConfig(karma, {
const karmaConfig = getKarmaConfig(karma, {
basePath: __dirname,
webpack: WEBPACK_CONFIG_TEST
});
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"flow:build": "flow gen-flow-files ./src/index.js --out-dir ./dist/module",
"karma": "cross-env NODE_ENV=test babel-node --plugins=transform-es2015-modules-commonjs ./node_modules/.bin/karma start",
"babel": "babel src/ --out-dir dist/module",
"webpack": "babel-node --plugins=transform-es2015-modules-commonjs ./node_modules/.bin/webpack --progress",
"webpack": "babel-node --config-file ./node_modules/grumbler-scripts/config/.babelrc-node --plugins=transform-es2015-modules-commonjs ./node_modules/.bin/webpack --display-optimization-bailout --progress",
"test": "npm run lint && npm run flow-typed && npm run flow && npm run karma",
"build": "npm run test && npm run babel && npm run webpack",
"release": "./publish.sh",
Expand Down Expand Up @@ -48,8 +48,8 @@
],
"readmeFilename": "README.md",
"devDependencies": {
"flow-bin": "^0.76",
"grumbler-scripts": "^2.0.18",
"flow-bin": "^0.81",
"grumbler-scripts": "^3",
"mocha": "^4"
},
"dependencies": {
Expand Down
112 changes: 52 additions & 60 deletions src/bridge/bridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,14 @@ import { getParent, isWindowClosed, type CrossDomainWindowType } from 'cross-dom
import { noop, uniqueID } from 'belter/src';

import { MESSAGE_NAME, WILDCARD } from '../conf';
import { global, globalStore } from '../global';

/*
HERE BE DRAGONS
Warning: this file may look weird. Why save the tunnel window in an Object
by ID, then look it up later, rather than just using the reference from the closure scope?
The reason is, that ends up meaning the garbage collector can never get its hands
on a closed window, since our closure has continued access to it -- and post-robot
has no good way to know whether to clean up the function with the closure scope.
If you're editing this file, be sure to run significant memory / GC tests afterwards.
*/

let tunnelWindows = globalStore('tunnelWindows');
import { getGlobal, globalStore } from '../global';
import type { SendType, ResponseMessageEvent } from '../types';

function cleanTunnelWindows() {
for (let key of tunnelWindows.keys()) {
let tunnelWindow = tunnelWindows[key];
const tunnelWindows = globalStore('tunnelWindows');

for (const key of tunnelWindows.keys()) {
const tunnelWindow = tunnelWindows[key];

try {
noop(tunnelWindow.source);
Expand All @@ -39,58 +27,62 @@ function cleanTunnelWindows() {
}
}

type TunnelWindowDataType = {
type TunnelWindowDataType = {|
name : string,
source : CrossDomainWindowType,
canary : () => void,
sendMessage : (message : string) => void
};
|};

function addTunnelWindow({ name, source, canary, sendMessage } : TunnelWindowDataType) : string {
cleanTunnelWindows();
let id = uniqueID();
const id = uniqueID();
const tunnelWindows = globalStore('tunnelWindows');
tunnelWindows.set(id, { name, source, canary, sendMessage });
return id;
}

global.openTunnelToParent = function openTunnelToParent({ name, source, canary, sendMessage } : TunnelWindowDataType) : ZalgoPromise<{ source : CrossDomainWindowType, origin : string, data : Object }> {

let parentWindow = getParent(window);

if (!parentWindow) {
throw new Error(`No parent window found to open tunnel to`);
}

let id = addTunnelWindow({ name, source, canary, sendMessage });

return global.send(parentWindow, MESSAGE_NAME.OPEN_TUNNEL, {

name,
export function setupOpenTunnelToParent({ send } : { send : SendType }) {
getGlobal(window).openTunnelToParent = function openTunnelToParent({ name, source, canary, sendMessage } : TunnelWindowDataType) : ZalgoPromise<ResponseMessageEvent> {

sendMessage() {

let tunnelWindow = tunnelWindows.get(id);

try {
// IE gets antsy if you try to even reference a closed window
noop(tunnelWindow && tunnelWindow.source);
} catch (err) {
tunnelWindows.del(id);
return;
}

if (!tunnelWindow || !tunnelWindow.source || isWindowClosed(tunnelWindow.source)) {
return;
}

try {
tunnelWindow.canary();
} catch (err) {
return;
}

tunnelWindow.sendMessage.apply(this, arguments);
const tunnelWindows = globalStore('tunnelWindows');
const parentWindow = getParent(window);

if (!parentWindow) {
throw new Error(`No parent window found to open tunnel to`);
}

}, { domain: WILDCARD });
};

const id = addTunnelWindow({ name, source, canary, sendMessage });

return send(parentWindow, MESSAGE_NAME.OPEN_TUNNEL, {

name,

sendMessage() {

const tunnelWindow = tunnelWindows.get(id);

try {
// IE gets antsy if you try to even reference a closed window
noop(tunnelWindow && tunnelWindow.source);
} catch (err) {
tunnelWindows.del(id);
return;
}

if (!tunnelWindow || !tunnelWindow.source || isWindowClosed(tunnelWindow.source)) {
return;
}

try {
tunnelWindow.canary();
} catch (err) {
return;
}

tunnelWindow.sendMessage.apply(this, arguments);
}

}, { domain: WILDCARD });
};
}
49 changes: 21 additions & 28 deletions src/bridge/child.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,35 @@

import { ZalgoPromise } from 'zalgo-promise/src';
import { isSameDomain, getOpener, getDomain, getFrameByName, type CrossDomainWindowType } from 'cross-domain-utils/src';
import { weakMapMemoize, noop } from 'belter/src';
import { noop } from 'belter/src';

import { WINDOW_PROP } from '../conf';
import { global } from '../global';
import { getGlobal, windowStore } from '../global';
import type { OnType, SendType, ReceiveMessageType } from '../types';

import { needsBridge, registerRemoteWindow, rejectRemoteSendMessage, registerRemoteSendMessage, getBridgeName } from './common';

let awaitRemoteBridgeForWindow = weakMapMemoize((win : CrossDomainWindowType) : ZalgoPromise<?CrossDomainWindowType> => {
return ZalgoPromise.try(() => {
try {
let frame = getFrameByName(win, getBridgeName(getDomain()));
function awaitRemoteBridgeForWindow (win : CrossDomainWindowType) : ZalgoPromise<?CrossDomainWindowType> {
return windowStore('remoteBridgeAwaiters').getOrSet(win, () => {
return ZalgoPromise.try(() => {
const frame = getFrameByName(win, getBridgeName(getDomain()));

if (!frame) {
return;
throw new Error(`Bridge not found for domain: ${ getDomain() }`);
}

// $FlowFixMe
if (isSameDomain(frame) && frame[WINDOW_PROP.POSTROBOT]) {
if (isSameDomain(frame) && getGlobal(frame)) {
return frame;
}

return new ZalgoPromise(resolve => {
return new ZalgoPromise((resolve, reject) => {

let interval;
let timeout;
let timeout; // eslint-disable-line prefer-const

interval = setInterval(() => {
interval = setInterval(() => { // eslint-disable-line prefer-const
// $FlowFixMe
if (frame && isSameDomain(frame) && frame[WINDOW_PROP.POSTROBOT]) {
if (frame && isSameDomain(frame) && getGlobal(frame)) {
clearInterval(interval);
clearTimeout(timeout);
return resolve(frame);
Expand All @@ -39,19 +39,15 @@ let awaitRemoteBridgeForWindow = weakMapMemoize((win : CrossDomainWindowType) :

timeout = setTimeout(() => {
clearInterval(interval);
return resolve();
return reject(new Error(`Bridge not found for domain: ${ getDomain() }`));
}, 2000);
});

} catch (err) {
// pass
}
});
});
});
}

export function openTunnelToOpener() : ZalgoPromise<void> {
export function openTunnelToOpener({ on, send, receiveMessage } : { on : OnType, send : SendType, receiveMessage : ReceiveMessageType }) : ZalgoPromise<void> {
return ZalgoPromise.try(() => {

const opener = getOpener(window);

if (!opener) {
Expand All @@ -66,15 +62,12 @@ export function openTunnelToOpener() : ZalgoPromise<void> {

return awaitRemoteBridgeForWindow(opener).then(bridge => {

if (!bridge) {
return rejectRemoteSendMessage(opener, new Error(`Can not register with opener: no bridge found in opener`));
}

if (!window.name) {
return rejectRemoteSendMessage(opener, new Error(`Can not register with opener: window does not have a name`));
}

return bridge[WINDOW_PROP.POSTROBOT].openTunnelToParent({
// $FlowFixMe
return getGlobal(bridge).openTunnelToParent({

name: window.name,

Expand All @@ -97,11 +90,11 @@ export function openTunnelToOpener() : ZalgoPromise<void> {
}

try {
global.receiveMessage({
receiveMessage({
data: message,
origin: this.origin,
source: this.source
});
}, { on, send });
} catch (err) {
ZalgoPromise.reject(err);
}
Expand Down
Loading

0 comments on commit 675ddec

Please sign in to comment.