forked from codesandbox/codesandbox-client
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.ts
More file actions
147 lines (120 loc) · 3.54 KB
/
index.ts
File metadata and controls
147 lines (120 loc) · 3.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// import * as debug from 'debug';
// import host from './host';
const bundlers: Map<Window, string> = new Map();
function checkIsStandalone() {
if (typeof window === 'undefined') {
return true;
}
if (window.location && window.location.href.indexOf('?standalone') > -1) {
return true;
}
return !window.opener && window.parent === window;
}
// Whether the tab has a connection with the editor
export const isStandalone = checkIsStandalone();
let initializeResolved: () => void;
/**
* Resolves when the handshake between the frame and the editor has succeeded
*/
export const intializedPromise = new Promise(resolve => {
initializeResolved = resolve;
});
// Field used by a "child" frame to determine its parent origin
let parentOrigin: string | null = null;
const parentOriginListener = (e: MessageEvent) => {
if (e.data.type === 'register-frame') {
parentOrigin = e.data.origin;
if (initializeResolved) {
initializeResolved();
}
self.removeEventListener('message', parentOriginListener);
}
};
if (typeof window !== 'undefined') {
self.addEventListener('message', parentOriginListener);
}
export function resetState() {
parentOrigin = null;
bundlers.clear();
}
/**
* Send a message to the editor, this is most probably an action you generated
*
* @export
* @param {*} message
* @returns
*/
export function dispatch(message: any) {
if (!message) return;
const newMessage = { ...message, codesandbox: true };
notifyListeners(newMessage);
notifyFrames(newMessage);
if (isStandalone) return;
if (parentOrigin === null && message.type !== 'initialized') return;
if (window.opener) {
window.opener.postMessage(newMessage, parentOrigin === null ? '*' : parentOrigin);
} else {
window.parent.postMessage(newMessage, parentOrigin === null ? '*' : parentOrigin);
}
}
export type Callback = (
message: object,
source?: MessageEvent['source'] | null | undefined
) => void;
const listeners: { [id: string]: Callback } = {};
let listenerId = 0;
/**
* Listen to everything that comes in from either the editor or the sandbox
* @param callback Call this function to 'unlisten'
*/
export function listen(callback: Callback): () => void {
const id = ++listenerId;
listeners[id] = callback;
return () => {
delete listeners[id];
};
}
export function notifyListeners(data: object, source?: MessageEvent['source']) {
// eslint-disable-next-line no-shadow
Object.keys(listeners).forEach(listenerId => {
if (listeners[listenerId]) {
listeners[listenerId](data, source);
}
});
}
function notifyFrames(message: object) {
const rawMessage = JSON.parse(JSON.stringify(message));
bundlers.forEach((origin, frame) => {
if (frame && frame.postMessage) {
frame.postMessage({ ...rawMessage, codesandbox: true }, origin);
}
});
}
function eventListener(e: MessageEvent) {
const { data } = e;
if (data && data.codesandbox && (parentOrigin === null || e.origin === parentOrigin)) {
notifyListeners(data, e.source);
}
}
/**
* Register an window as a output the `dispatch` function can send messages to.
*
* @param frame
*/
export function registerFrame(frame: Window, origin: string) {
bundlers.set(frame, origin);
frame.postMessage(
{
type: 'register-frame',
origin: document.location.origin,
},
origin
);
}
if (typeof window !== 'undefined') {
// We now start listening so we can let our listeners know
window.addEventListener('message', eventListener);
}
export function reattach() {
window.addEventListener('message', eventListener);
}