Skip to content

Commit 2dca0ce

Browse files
authored
fix(zone.js): correctly patch es6 classes (#15)
1 parent 44d0d88 commit 2dca0ce

File tree

5 files changed

+113
-4
lines changed

5 files changed

+113
-4
lines changed

packages/zone-js/dist/core.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint-disable */
2-
import { patchNativeScriptEventTarget } from './utils';
2+
import { patchClass, patchNativeScriptEventTarget } from './utils';
33

44
function isPropertyWritable(propertyDesc: any) {
55
if (!propertyDesc) {
@@ -49,3 +49,7 @@ Zone.__load_patch('nativescript_patchMethod', (global, Zone, api) => {
4949
Zone.__load_patch('nativescript_event_target_api', (g, z, api: any) => {
5050
api.patchNativeScriptEventTarget = patchNativeScriptEventTarget;
5151
});
52+
53+
Zone.__load_patch('nativescript_patch_class_api', (g, z, api) => {
54+
api.patchClass = (className: string) => patchClass(className, api);
55+
});

packages/zone-js/dist/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import './core';
2+
import './nativescript-globals';
23
import './events';
34
import './xhr';
45
import './connectivity';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Zone.__load_patch('nativescript_MutationObserver', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
2+
// api.patchClass('MutationObserver');
3+
// api.patchClass('WebKitMutationObserver');
4+
// });
5+
6+
// Zone.__load_patch('nativescript_IntersectionObserver', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
7+
// api.patchClass('IntersectionObserver');
8+
// });
9+
10+
Zone.__load_patch('nativescript_FileReader', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
11+
api.patchClass('FileReader');
12+
});
+5-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
global['__Zone_disable_legacy'] = true;
2-
global['__Zone_disable_EventTarget'] = true;
3-
global['__Zone_disable_XHR'] = true;
1+
export const disabledPatches = ['legacy', 'EventTarget', 'XHR', 'MutationObserver', 'IntersectionObserver', 'FileReader'];
2+
3+
for (const patch of disabledPatches) {
4+
global[`__Zone_disable_${patch}`] = true;
5+
}

packages/zone-js/dist/utils.ts

+90
Original file line numberDiff line numberDiff line change
@@ -331,3 +331,93 @@ export function patchNativeScriptEventTarget(global: any, api: _ZonePrivate, api
331331

332332
return results;
333333
}
334+
335+
const _global = global;
336+
337+
const zoneSymbol = Zone.__symbol__;
338+
339+
const originalInstanceKey = zoneSymbol('originalInstance');
340+
341+
function getAllPropertyNames(obj: unknown) {
342+
const props = new Set<string>();
343+
344+
do {
345+
Object.getOwnPropertyNames(obj).forEach((prop) => {
346+
props.add(prop);
347+
});
348+
} while ((obj = Object.getPrototypeOf(obj)) && obj !== Object.prototype);
349+
350+
return Array.from(props);
351+
}
352+
353+
// wrap some native API on `window`
354+
export function patchClass(className: string, api: _ZonePrivate) {
355+
const OriginalClass = _global[className];
356+
if (!OriginalClass) return;
357+
// keep original class in global
358+
_global[zoneSymbol(className)] = OriginalClass;
359+
360+
_global[className] = function () {
361+
const a = api.bindArguments(<any>arguments, className);
362+
switch (a.length) {
363+
case 0:
364+
this[originalInstanceKey] = new OriginalClass();
365+
break;
366+
case 1:
367+
this[originalInstanceKey] = new OriginalClass(a[0]);
368+
break;
369+
case 2:
370+
this[originalInstanceKey] = new OriginalClass(a[0], a[1]);
371+
break;
372+
case 3:
373+
this[originalInstanceKey] = new OriginalClass(a[0], a[1], a[2]);
374+
break;
375+
case 4:
376+
this[originalInstanceKey] = new OriginalClass(a[0], a[1], a[2], a[3]);
377+
break;
378+
default:
379+
throw new Error('Arg list too long.');
380+
}
381+
};
382+
383+
// attach original delegate to patched function
384+
api.attachOriginToPatched(_global[className], OriginalClass);
385+
386+
const instance = new OriginalClass(function () {});
387+
388+
let prop;
389+
for (prop of Object.getOwnPropertyNames(instance)) {
390+
// https://bugs.webkit.org/show_bug.cgi?id=44721
391+
if (className === 'XMLHttpRequest' && prop === 'responseBlob') continue;
392+
(function (prop) {
393+
if (typeof instance[prop] === 'function') {
394+
_global[className].prototype[prop] = function () {
395+
return this[originalInstanceKey][prop].apply(this[originalInstanceKey], arguments);
396+
};
397+
} else {
398+
api.ObjectDefineProperty(_global[className].prototype, prop, {
399+
set: function (fn) {
400+
if (typeof fn === 'function') {
401+
this[originalInstanceKey][prop] = api.wrapWithCurrentZone(fn, className + '.' + prop);
402+
// keep callback in wrapped function so we can
403+
// use it in Function.prototype.toString to return
404+
// the native one.
405+
api.attachOriginToPatched(this[originalInstanceKey][prop], fn);
406+
} else {
407+
this[originalInstanceKey][prop] = fn;
408+
}
409+
},
410+
get: function () {
411+
return this[originalInstanceKey][prop];
412+
},
413+
});
414+
}
415+
})(prop);
416+
}
417+
418+
for (prop in Object.getOwnPropertyNames(OriginalClass)) {
419+
if (prop !== 'prototype' && OriginalClass.hasOwnProperty(prop)) {
420+
_global[className][prop] = OriginalClass[prop];
421+
}
422+
}
423+
}

0 commit comments

Comments
 (0)