🐛 avoid illegal invocation error while using some dom api in nest sandbox (#1768)
This commit is contained in:
parent
ab64a7047b
commit
df24f02e92
|
|
@ -383,3 +383,30 @@ it('native window function calling should always be bound with window', () => {
|
||||||
const { proxy } = new ProxySandbox('mustBeBoundWithWindowReference');
|
const { proxy } = new ProxySandbox('mustBeBoundWithWindowReference');
|
||||||
expect(proxy.nativeWindowFunction()).toBe('success');
|
expect(proxy.nativeWindowFunction()).toBe('success');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('should work with nest sandbox', () => {
|
||||||
|
it('specified dom api should bound with native window', () => {
|
||||||
|
const { proxy: sandboxProxy } = new ProxySandbox('sandbox');
|
||||||
|
const { proxy: nestProxy } = new ProxySandbox('dom-api', sandboxProxy as typeof window);
|
||||||
|
|
||||||
|
function mockDomAPI(this: any) {
|
||||||
|
if (this !== window) {
|
||||||
|
throw new TypeError('Illegal invocation!');
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
nestProxy.mockDomAPIInBlackList = mockDomAPI;
|
||||||
|
// must use a new function to avoid cache
|
||||||
|
nestProxy.mockDomAPINotInBlackList = function fnCp(this: any) {
|
||||||
|
return mockDomAPI.call(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(nestProxy.mockDomAPIInBlackList()).toBeTruthy();
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
nestProxy.mockDomAPINotInBlackList();
|
||||||
|
}).toThrowError(/Illegal invocation!/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Freer } from '../../../interfaces';
|
import type { Freer } from '../../../interfaces';
|
||||||
|
import { nativeGlobal } from '../../../utils';
|
||||||
import { getCurrentRunningApp } from '../../common';
|
import { getCurrentRunningApp } from '../../common';
|
||||||
import type { ContainerConfig } from './common';
|
import type { ContainerConfig } from './common';
|
||||||
import {
|
import {
|
||||||
|
|
@ -21,8 +22,6 @@ declare global {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get native global window with a sandbox disgusted way, thus we could share it between qiankun instances🤪
|
// Get native global window with a sandbox disgusted way, thus we could share it between qiankun instances🤪
|
||||||
// eslint-disable-next-line no-new-func
|
|
||||||
const nativeGlobal: Window = new Function('return this')();
|
|
||||||
Object.defineProperty(nativeGlobal, '__proxyAttachContainerConfigMap__', { enumerable: false, writable: true });
|
Object.defineProperty(nativeGlobal, '__proxyAttachContainerConfigMap__', { enumerable: false, writable: true });
|
||||||
|
|
||||||
// Share proxyAttachContainerConfigMap between multiple qiankun instance, thus they could access the same record
|
// Share proxyAttachContainerConfigMap between multiple qiankun instance, thus they could access the same record
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,13 @@
|
||||||
*/
|
*/
|
||||||
import type { SandBox } from '../interfaces';
|
import type { SandBox } from '../interfaces';
|
||||||
import { SandBoxType } from '../interfaces';
|
import { SandBoxType } from '../interfaces';
|
||||||
import { nextTask } from '../utils';
|
import { nativeGlobal, nextTask } from '../utils';
|
||||||
import { getTargetValue, setCurrentRunningApp } from './common';
|
import { getTargetValue, setCurrentRunningApp } from './common';
|
||||||
|
|
||||||
|
type SymbolTarget = 'target' | 'globalContext';
|
||||||
|
|
||||||
|
type FakeWindow = Window & Record<PropertyKey, any>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fastest(at most time) unique array method
|
* fastest(at most time) unique array method
|
||||||
* @see https://jsperf.com/array-filter-unique/30
|
* @see https://jsperf.com/array-filter-unique/30
|
||||||
|
|
@ -57,9 +61,10 @@ const unscopables = {
|
||||||
Float32Array: true,
|
Float32Array: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
type SymbolTarget = 'target' | 'globalContext';
|
const useNativeWindowForBindingsProps = new Map<PropertyKey, boolean>([
|
||||||
|
['fetch', true],
|
||||||
type FakeWindow = Window & Record<PropertyKey, any>;
|
['mockDomAPIInBlackList', process.env.NODE_ENV === 'test'],
|
||||||
|
]);
|
||||||
|
|
||||||
function createFakeWindow(globalContext: Window) {
|
function createFakeWindow(globalContext: Window) {
|
||||||
// map always has the fastest performance in has check scenario
|
// map always has the fastest performance in has check scenario
|
||||||
|
|
@ -269,13 +274,19 @@ export default class ProxySandbox implements SandBox {
|
||||||
return eval;
|
return eval;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-nested-ternary
|
|
||||||
const value = propertiesWithGetter.has(p)
|
const value = propertiesWithGetter.has(p)
|
||||||
? (globalContext as any)[p]
|
? (globalContext as any)[p]
|
||||||
: p in target
|
: p in target
|
||||||
? (target as any)[p]
|
? (target as any)[p]
|
||||||
: (globalContext as any)[p];
|
: (globalContext as any)[p];
|
||||||
return getTargetValue(globalContext, value);
|
/* Some dom api must be bound to native window, otherwise it would cause exception like 'TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation'
|
||||||
|
See this code:
|
||||||
|
const proxy = new Proxy(window, {});
|
||||||
|
const proxyFetch = fetch.bind(proxy);
|
||||||
|
proxyFetch('https://qiankun.com');
|
||||||
|
*/
|
||||||
|
const boundTarget = useNativeWindowForBindingsProps.get(p) ? nativeGlobal : globalContext;
|
||||||
|
return getTargetValue(boundTarget, value);
|
||||||
},
|
},
|
||||||
|
|
||||||
// trap in operator
|
// trap in operator
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,8 @@ export function getWrapperId(id: string) {
|
||||||
return `__qiankun_microapp_wrapper_for_${snakeCase(id)}__`;
|
return `__qiankun_microapp_wrapper_for_${snakeCase(id)}__`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const nativeGlobal = new Function('return this')();
|
||||||
|
|
||||||
/** 校验子应用导出的 生命周期 对象是否正确 */
|
/** 校验子应用导出的 生命周期 对象是否正确 */
|
||||||
export function validateExportLifecycle(exports: any) {
|
export function validateExportLifecycle(exports: any) {
|
||||||
const { bootstrap, mount, unmount } = exports ?? {};
|
const { bootstrap, mount, unmount } = exports ?? {};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user