Skip to content

Commit ef36fbc

Browse files
committed
add loader
1 parent b352c46 commit ef36fbc

File tree

7 files changed

+339
-5
lines changed

7 files changed

+339
-5
lines changed

packages/@ember/debug/ember-inspector-support/adapters/basic.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export default class BasicAdapter extends BaseObject {
5959
6060
@param {Function} callback
6161
*/
62-
onMessageReceived(callback: () => void) {
62+
onMessageReceived(callback: (msg: any) => void) {
6363
this._messageCallbacks.push(callback);
6464
}
6565

packages/@ember/debug/ember-inspector-support/adapters/bookmarklet.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import BasicAdapter from './basic';
22

3-
export default class extends BasicAdapter {
3+
export default class BookmarkletAdapter extends BasicAdapter {
44
init() {
55
super.init();
66
this._listen();
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
import WebExtension from './web-extension';
2-
export default class extends WebExtension {}
2+
export default class ChromeAdapter extends WebExtension {}

packages/@ember/debug/ember-inspector-support/adapters/firefox.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint no-empty:0 */
22
import WebExtension from './web-extension';
33

4-
export default class extends WebExtension {
4+
export default class FirefoxAdapter extends WebExtension {
55
debug(...args: unknown[]) {
66
// WORKAROUND: temporarily workaround issues with firebug console object:
77
// - https://github.com/tildeio/ember-extension/issues/94
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { default as BasicAdapter } from './basic';
2+
import { default as BookmarkletAdapter } from './bookmarklet';
3+
import { default as ChromeAdapter } from './chrome';
4+
import { default as WebsocketAdapter } from './websocket';
5+
import { default as WebExtensionAdapter } from './web-extension';
6+
7+
export default {
8+
BasicAdapter,
9+
BookmarkletAdapter,
10+
ChromeAdapter,
11+
WebExtensionAdapter,
12+
WebsocketAdapter,
13+
};

packages/@ember/debug/ember-inspector-support/adapters/websocket.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import BasicAdapter from './basic';
22
import { onReady } from '@ember/debug/ember-inspector-support/utils/on-ready';
33
import { run } from '@ember/runloop';
44

5-
export default class extends BasicAdapter {
5+
export default class WebsocketAdapter extends BasicAdapter {
66
sendMessage(options = {}) {
77
this.socket.emit('emberInspectorMessage', options);
88
}
Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
import { VERSION } from '@ember/version';
2+
import Adapters from './adapters';
3+
import MainModule from './main';
4+
import { guidFor } from '@ember/object/internals';
5+
import { A } from '@ember/array';
6+
import Namespace from '@ember/application/namespace';
7+
import Application from '@ember/application';
8+
import type ApplicationInstance from '@ember/application/instance';
9+
10+
export function setupEmberInspectorSupport() {
11+
window.addEventListener('ember-inspector-loaded' as any, (event: CustomEvent) => {
12+
const adapter = event.detail.adapter;
13+
const EMBER_VERSIONS_SUPPORTED = event.detail.EMBER_VERSIONS_SUPPORTED;
14+
loadEmberDebug(adapter, EMBER_VERSIONS_SUPPORTED);
15+
});
16+
17+
const e = new Event('ember-inspector-support-setup');
18+
window.dispatchEvent(e);
19+
}
20+
21+
function loadEmberDebug(
22+
adapter: keyof typeof Adapters,
23+
EMBER_VERSIONS_SUPPORTED: [string, string]
24+
) {
25+
const w = window as any;
26+
// global to prevent injection
27+
if (w.NO_EMBER_DEBUG) {
28+
return;
29+
}
30+
31+
if (!versionTest(VERSION, EMBER_VERSIONS_SUPPORTED)) {
32+
// Wrong inspector version. Redirect to the correct version.
33+
sendVersionMiss();
34+
return;
35+
}
36+
37+
// prevent from injecting twice
38+
if (!w.EmberInspector) {
39+
w.EmberInspector = MainModule;
40+
w.EmberInspector.Adapter = Adapters[adapter];
41+
42+
onApplicationStart(function appStarted(instance: ApplicationInstance) {
43+
let app = instance.application;
44+
if (!('__inspector__booted' in app)) {
45+
// Watch for app reset/destroy
46+
app.reopen({
47+
reset: function (this: Application) {
48+
(this as any).__inspector__booted = false;
49+
this._super.apply(this, arguments as any);
50+
},
51+
});
52+
}
53+
54+
if (instance && !('__inspector__booted' in instance)) {
55+
instance.reopen({
56+
// Clean up on instance destruction
57+
willDestroy() {
58+
if (w.EmberInspector.owner === instance) {
59+
w.EmberInspector.destroyContainer();
60+
w.EmberInspector.clear();
61+
}
62+
return (this as any)._super.apply(this, arguments);
63+
},
64+
});
65+
66+
if (!w.EmberInspector._application) {
67+
setTimeout(() => bootEmberInspector(instance), 0);
68+
}
69+
}
70+
});
71+
}
72+
73+
function bootEmberInspector(appInstance: ApplicationInstance) {
74+
(appInstance.application as any).__inspector__booted = true;
75+
(appInstance as any).__inspector__booted = true;
76+
77+
// Boot the inspector (or re-boot if already booted, for example in tests)
78+
w.EmberInspector._application = appInstance.application;
79+
w.EmberInspector.owner = appInstance;
80+
w.EmberInspector.start(true);
81+
}
82+
83+
// There's probably a better way
84+
// to determine when the application starts
85+
// but this definitely works
86+
function onApplicationStart(callback: Function) {
87+
const adapterInstance = new Adapters[adapter]();
88+
89+
adapterInstance.onMessageReceived(function (message) {
90+
if (message.type === 'app-picker-loaded') {
91+
sendApps(adapterInstance, getApplications());
92+
}
93+
94+
if (message.type === 'app-selected') {
95+
let current = w.EmberInspector._application;
96+
let selected = getApplications().find((app: any) => guidFor(app) === message.applicationId);
97+
98+
if (selected && current !== selected && selected.__deprecatedInstance__) {
99+
bootEmberInspector(selected.__deprecatedInstance__);
100+
}
101+
}
102+
});
103+
104+
let apps = getApplications();
105+
106+
sendApps(adapterInstance, apps);
107+
108+
function loadInstance(app: Application) {
109+
const applicationInstances = app._applicationInstances && [...app._applicationInstances];
110+
let instance = app.__deprecatedInstance__ || applicationInstances[0];
111+
if (instance) {
112+
// App started
113+
setupInstanceInitializer(app, callback);
114+
callback(instance);
115+
return true;
116+
}
117+
return;
118+
}
119+
120+
let app: Application;
121+
for (let i = 0, l = apps.length; i < l; i++) {
122+
app = apps[i];
123+
// We check for the existance of an application instance because
124+
// in Ember > 3 tests don't destroy the app when they're done but the app has no booted instances.
125+
if (app._readinessDeferrals === 0) {
126+
if (loadInstance(app)) {
127+
break;
128+
}
129+
}
130+
131+
// app already run initializers, but no instance, use _bootPromise and didBecomeReady
132+
if (app._bootPromise) {
133+
app._bootPromise.then((app) => {
134+
loadInstance(app);
135+
});
136+
}
137+
138+
app.reopen({
139+
didBecomeReady(this: Application) {
140+
this._super.apply(this, arguments as any);
141+
setTimeout(() => loadInstance(app), 0);
142+
},
143+
});
144+
}
145+
Application.initializer({
146+
name: 'ember-inspector-booted',
147+
initialize: function (app) {
148+
setupInstanceInitializer(app, callback);
149+
},
150+
});
151+
}
152+
153+
function setupInstanceInitializer(app: Application, callback: Function) {
154+
if (!(app as any).__inspector__setup) {
155+
(app as any).__inspector__setup = true;
156+
157+
// We include the app's guid in the initializer name because in Ember versions < 3
158+
// registering an instance initializer with the same name, even if on a different app,
159+
// triggers an error because instance initializers seem to be global instead of per app.
160+
app.instanceInitializer({
161+
name: 'ember-inspector-app-instance-booted-' + guidFor(app),
162+
initialize: function (instance) {
163+
callback(instance);
164+
},
165+
});
166+
}
167+
}
168+
169+
/**
170+
* Get all the Ember.Application instances from Ember.Namespace.NAMESPACES
171+
* and add our own applicationId and applicationName to them
172+
* @return {*}
173+
*/
174+
function getApplications() {
175+
let namespaces = A(Namespace.NAMESPACES);
176+
177+
let apps = namespaces.filter(function (namespace) {
178+
return namespace instanceof Application;
179+
});
180+
181+
return apps.map(function (app: any) {
182+
// Add applicationId and applicationName to the app
183+
let applicationId = guidFor(app);
184+
let applicationName = app.name || app.modulePrefix || `(unknown app - ${applicationId})`;
185+
186+
Object.assign(app, {
187+
applicationId,
188+
applicationName,
189+
});
190+
191+
return app;
192+
});
193+
}
194+
195+
let channel = new MessageChannel();
196+
let port = channel.port1;
197+
window.postMessage('debugger-client', '*', [channel.port2]);
198+
199+
let registeredMiss = false;
200+
201+
/**
202+
* This function is called if the app's Ember version
203+
* is not supported by this version of the inspector.
204+
*
205+
* It sends a message to the inspector app to redirect
206+
* to an inspector version that supports this Ember version.
207+
*/
208+
function sendVersionMiss() {
209+
if (registeredMiss) {
210+
return;
211+
}
212+
213+
registeredMiss = true;
214+
215+
port.addEventListener('message', (message) => {
216+
if (message.type === 'check-version') {
217+
sendVersionMismatch();
218+
}
219+
});
220+
221+
sendVersionMismatch();
222+
223+
port.start();
224+
225+
function sendVersionMismatch() {
226+
port.postMessage({
227+
name: 'version-mismatch',
228+
version: VERSION,
229+
from: 'inspectedWindow',
230+
});
231+
}
232+
}
233+
234+
function sendApps(adapter: any, apps: any[]) {
235+
const serializedApps = apps.map((app) => {
236+
return {
237+
applicationName: app.applicationName,
238+
applicationId: app.applicationId,
239+
};
240+
});
241+
242+
adapter.sendMessage({
243+
type: 'apps-loaded',
244+
apps: serializedApps,
245+
from: 'inspectedWindow',
246+
});
247+
}
248+
249+
/**
250+
* Checks if a version is between two different versions.
251+
* version should be >= left side, < right side
252+
*
253+
* @param {String} version1
254+
* @param {String} version2
255+
* @return {Boolean}
256+
*/
257+
function versionTest(version: string, between: [string, string]) {
258+
let fromVersion = between[0];
259+
let toVersion = between[1];
260+
261+
if (compareVersion(version, fromVersion) === -1) {
262+
return false;
263+
}
264+
return !toVersion || compareVersion(version, toVersion) === -1;
265+
}
266+
267+
/**
268+
* Compares two Ember versions.
269+
*
270+
* Returns:
271+
* `-1` if version1 < version
272+
* 0 if version1 == version2
273+
* 1 if version1 > version2
274+
*
275+
* @param {String} version1
276+
* @param {String} version2
277+
* @return {Boolean} result of the comparison
278+
*/
279+
function compareVersion(version1: string, version2: string) {
280+
let compared, i;
281+
let version1Split = cleanupVersion(version1).split('.');
282+
let version2Split = cleanupVersion(version2).split('.');
283+
for (i = 0; i < 3; i++) {
284+
compared = compare(Number(version1Split[i]), Number(version2Split[i]));
285+
if (compared !== 0) {
286+
return compared;
287+
}
288+
}
289+
return 0;
290+
}
291+
292+
/**
293+
* Remove -alpha, -beta, etc from versions
294+
*
295+
* @param {String} version
296+
* @return {String} The cleaned up version
297+
*/
298+
function cleanupVersion(version: string) {
299+
return version.replace(/-.*/g, '');
300+
}
301+
302+
/**
303+
* @method compare
304+
* @param {Number} val
305+
* @param {Number} number
306+
* @return {Number}
307+
* 0: same
308+
* -1: <
309+
* 1: >
310+
*/
311+
function compare(val: number, number: number) {
312+
if (val === number) {
313+
return 0;
314+
} else if (val < number) {
315+
return -1;
316+
} else if (val > number) {
317+
return 1;
318+
}
319+
return;
320+
}
321+
}

0 commit comments

Comments
 (0)