Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions packages/icestark-module/src/eventHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* CustomEvent Polyfill for IE.
* See https://gist.github.com/gt3/787767e8cbf0451716a189cdcb2a0d08.
*/
(function () {
if (typeof (window as any).CustomEvent === 'function') return false;

function CustomEvent(event, params) {
params = params || { bubbles: false, cancelable: false, detail: null };
const evt = document.createEvent('CustomEvent');
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
return evt;
}

(window as any).CustomEvent = CustomEvent;
})();


export function dispatchEvent(mark: string, params: Record<'detail', object>) {
return window.dispatchEvent(new CustomEvent(mark, params));
}

export function resolveEvent(mark: string) {
return new Promise((resolve) => window.addEventListener(mark, resolve));
}
5 changes: 4 additions & 1 deletion packages/icestark-module/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
clearModules,
} from './modules';
import MicroModule from './MicroModule';
import { preloadModules } from './preload';

export {
StarkModule,
Expand All @@ -18,4 +19,6 @@ export {
getModules,
mountModule,
unmoutModule,
};
preloadModules,
};

50 changes: 39 additions & 11 deletions packages/icestark-module/src/modules.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Sandbox, { SandboxProps, SandboxConstructor } from '@ice/sandbox';
import ModuleLoader from './loader';
import { Runtime, parseRuntime, RuntimeInstance } from './runtimeHelper';
import { resolveEvent, dispatchEvent } from './eventHelper';

export interface StarkModule {
name: string;
Expand Down Expand Up @@ -180,6 +181,8 @@ export const getModules = function () {
return globalModules || [];
};

export const getImportedModules = () => importModules;

/**
* get import modules
*/
Expand All @@ -199,31 +202,56 @@ export const getImportedModule = function (name: string) {
return importModules[name];
};

/**
* load module source
*/
export const loadModule = async (targetModule: StarkModule, sandbox?: ISandbox) => {
export const execModule = async (targetModule: StarkModule, sandbox?: ISandbox) => {
const { name, url, runtime } = targetModule;
if (importModules[name]?.state === 'LOADING') {
await resolveEvent(name);
}

let moduleSandbox = null;
if (importModules[name]?.state === 'LOADED') {
return importModules[name];
}

if (!importModules[name]) {
let deps = null;
if (runtime) {
deps = await parseRuntime(runtime);
}
importModules[name] = {
state: 'LOADING',
};

let moduleSandbox = null;
let deps = null;
if (runtime) {
deps = await parseRuntime(runtime);
}

try {
const { jsList, cssList } = parseUrlAssets(url);
moduleSandbox = createSandbox(sandbox, deps);
const moduleInfo = await moduleLoader.execModule({ name, url: jsList }, moduleSandbox, deps);

dispatchEvent(name, { detail: { state: 'LOADED' } });

importModules[name] = {
...importModules[name],
moduleInfo,
moduleSandbox,
moduleCSS: cssList,
state: 'LOADED',
};
} catch (e) {
dispatchEvent(name, { detail: { state: 'LOAD_ERROR' } });
// eslint-disable-next-line require-atomic-updates
importModules[name] = {};
}

const { moduleInfo, moduleCSS } = importModules[name];
return importModules[name];
};

/**
* load module source
*/
export const loadModule = async (targetModule: StarkModule, sandbox?: ISandbox) => {
const { name } = targetModule;

const { moduleInfo, moduleCSS } = await execModule(targetModule, sandbox) as any;

if (!moduleInfo) {
const errMsg = 'load or exec module faild';
Expand Down
68 changes: 68 additions & 0 deletions packages/icestark-module/src/preload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { StarkModule, execModule, getModules, ISandbox, getImportedModules } from './modules';

/**
* https://github.com/microsoft/TypeScript/issues/21309#issuecomment-376338415
*/
type RequestIdleCallbackHandle = any;
interface RequestIdleCallbackOptions {
timeout: number;
}
interface RequestIdleCallbackDeadline {
readonly didTimeout: boolean;
timeRemaining: (() => number);
}

declare global {
interface Window {
// @ts-ignore
requestIdleCallback: ((
callback: ((deadline: RequestIdleCallbackDeadline) => void),
opts?: RequestIdleCallbackOptions,
) => RequestIdleCallbackHandle);
// @ts-ignore
cancelIdleCallback: ((handle: RequestIdleCallbackHandle) => void);
}
}

/**
* polyfill/shim for the `requestIdleCallback` and `cancelIdleCallback`.
* https://github.com/pladaria/requestidlecallback-polyfill/blob/master/index.js
*/
// @ts-ignore
window.requestIdleCallback =
window.requestIdleCallback ||
function (cb) {
const start = Date.now();
return setTimeout(() => {
cb({
didTimeout: false,
timeRemaining() {
return Math.max(0, 50 - (Date.now() - start));
},
});
}, 1);
};

window.cancelIdleCallback =
window.cancelIdleCallback ||
function (id) {
clearTimeout(id);
};

export function preloadModules(modules: StarkModule[] | string[], sandbox?: ISandbox): void {
if (!modules?.length) {
return;
}
modules.forEach((module) => {
const moduleInfo: StarkModule = typeof module === 'string' ? getModules().filter((m) => m.name === module)[0] : module;
if (!moduleInfo) {
console.error('Can\'t find module in modules config');
return;
}
const notImported = !Object.keys(getImportedModules()).includes(moduleInfo.name);
if (notImported) {
window.requestIdleCallback(() => execModule(moduleInfo, sandbox));
}
});
}

24 changes: 4 additions & 20 deletions packages/icestark-module/src/runtimeHelper.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,7 @@
import Sandbox from '@ice/sandbox';
import { any2AnyArray } from './utils';
import { parseUrlAssets, appendCSS } from './modules';

/**
* CustomEvent Polyfill for IE.
* See https://gist.github.com/gt3/787767e8cbf0451716a189cdcb2a0d08.
*/
(function () {
if (typeof (window as any).CustomEvent === 'function') return false;

function CustomEvent(event, params) {
params = params || { bubbles: false, cancelable: false, detail: null };
const evt = document.createEvent('CustomEvent');
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
return evt;
}

(window as any).CustomEvent = CustomEvent;
})();
import { dispatchEvent, resolveEvent } from './eventHelper';

export interface RuntimeInstance {
id: string;
Expand Down Expand Up @@ -71,7 +55,7 @@ export async function cacheDeps(runtime: CombineRuntime, deps: object, fetch = w

if (runtimeCache[mark]?.state === 'LOADING') {
// await util resource loaded or error
await new Promise((resolve) => window.addEventListener(mark, resolve));
await resolveEvent(mark);
}

if (runtimeCache[mark]?.state === 'LOADED') {
Expand All @@ -95,12 +79,12 @@ export async function cacheDeps(runtime: CombineRuntime, deps: object, fetch = w
).then((codes) => execute(codes, deps));

updateRuntimeState(mark, 'LOADED');
window.dispatchEvent(new CustomEvent(mark, { detail: { state: 'LOADED' } }));
dispatchEvent(mark, { detail: { state: 'LOADED' } });

return runtimeCache[mark].deps;
} catch (e) {
updateRuntimeState(mark, 'LOAD_ERROR');
window.dispatchEvent(new CustomEvent(mark, { detail: { state: 'LOAD_ERROR' } }));
dispatchEvent(mark, { detail: { state: 'LOAD_ERROR' } });
console.error(`[icestark module] ${id} fetch or excute js assets error`, e);
return Promise.reject(e);
}
Expand Down