Skip to content

Commit 087a721

Browse files
authored
feat: support runtime.url to load multi type resource (#767)
* feat: support runtime.url to load multi type resource * chore: optimize code * fix: check value before compare * fix: css * fix: refactor fetchScripts for improved runtime asset handling
1 parent 6270d39 commit 087a721

File tree

5 files changed

+103
-31
lines changed

5 files changed

+103
-31
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
See [https://github.com/ice-lab/icestark/releases](https://github.com/ice-lab/icestark/releases) for what has changed in each version of icestark.
44

5+
# 2.8.3
6+
7+
- [feat] support `runtime.url` as an array for loading multiple types of resources.
8+
- [fix] refactor fetchScripts for improved runtime asset handling.
9+
510
# 2.8.2
611

712
- [fix] fix the issue that the UMD library is overwritten by undefined value.

packages/icestark/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ice/stark",
3-
"version": "2.8.2",
3+
"version": "2.8.3",
44
"description": "Icestark is a JavaScript library for multiple projects, Ice workbench solution.",
55
"scripts": {
66
"build": "rm -rf lib && tsc",

packages/icestark/src/AppRoute.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export default class AppRoute extends React.Component<AppRouteProps, AppRouteSta
8686
};
8787
const { loadScriptMode, runtime } = props;
8888

89-
if (loadScriptMode !== 'fetch' && runtime) {
89+
if (loadScriptMode && loadScriptMode !== 'fetch' && runtime) {
9090
console.error('[icestark] runtime option can only be used when loadScriptMode is set to "fetch"');
9191
}
9292
}

packages/icestark/src/apps.ts

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Sandbox, { SandboxConstructor, SandboxProps } from '@ice/sandbox';
22
import isEmpty from 'lodash.isempty';
3-
import { NOT_LOADED, NOT_MOUNTED, LOADING_ASSETS, UNMOUNTED, LOAD_ERROR, MOUNTED } from './util/constant';
3+
import { NOT_LOADED, NOT_MOUNTED, LOADING_ASSETS, UNMOUNTED, LOAD_ERROR, MOUNTED, IS_CSS_REGEX } from './util/constant';
44
import findActivePathCurry, { ActivePath, PathOption, formatPath } from './util/checkActive';
55
import {
66
createSandbox,
@@ -44,7 +44,7 @@ export interface ModuleLifeCycle {
4444
}
4545

4646
export interface RuntimeConfig {
47-
url: string;
47+
url: string | string[];
4848
library: string;
4949
version: string;
5050
}
@@ -214,21 +214,57 @@ export async function loadAppModule(appConfig: AppConfig) {
214214
// Not to handle script element temporarily.
215215
break;
216216
case 'fetch': {
217-
await loadAndAppendCssAssets(appAssets.cssList, {
218-
cacheCss,
219-
fetch,
220-
});
217+
const runtimeCssList = [];
218+
const runtimeJsList = [];
221219
// Filter and map runtime libraries that haven't been registered in window
222220
const runtimeLibs = runtime?.filter((config) => {
223221
return config.version && config.library && !window[`${config.library}@${config.version}`];
224-
}).map((config) => ({
225-
content: config.url,
226-
type: AssetTypeEnum.RUNTIME,
227-
library: config.library,
228-
version: config.version,
229-
})) || [];
222+
}).map((config) => {
223+
// Handle both string and array URL formats
224+
const urls = Array.isArray(config.url) ? config.url : [config.url];
225+
// Separate CSS and JS URLs
226+
const jsUrls = [];
227+
urls.forEach((configUrl) => {
228+
if (IS_CSS_REGEX.test(configUrl)) {
229+
runtimeCssList.push({
230+
content: configUrl,
231+
type: AssetTypeEnum.EXTERNAL,
232+
});
233+
} else {
234+
jsUrls.push(configUrl);
235+
}
236+
});
237+
if (jsUrls.length === 0) {
238+
return null;
239+
}
240+
// Use the last JS URL as the main runtime JS
241+
const mainJs = jsUrls[jsUrls.length - 1];
242+
const restJs = jsUrls.slice(0, -1);
243+
restJs.forEach((configUrl) => {
244+
runtimeJsList.push({
245+
content: configUrl,
246+
type: AssetTypeEnum.EXTERNAL,
247+
});
248+
});
249+
250+
return {
251+
content: mainJs,
252+
type: AssetTypeEnum.RUNTIME,
253+
library: config.library,
254+
version: config.version,
255+
};
256+
}).filter(Boolean) || [];
257+
258+
await loadAndAppendCssAssets([
259+
...runtimeCssList,
260+
...appAssets.cssList,
261+
], {
262+
cacheCss,
263+
fetch,
264+
});
230265

231266
lifecycle = await loadScriptByFetch([
267+
...runtimeJsList,
232268
...runtimeLibs,
233269
...appAssets.jsList,
234270
], appSandbox, fetch);

packages/icestark/src/util/handleAssets.ts

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -310,37 +310,68 @@ export function getUrlAssets(urls: string | string[]) {
310310
return { jsList, cssList };
311311
}
312312

313-
export function fetchScripts(jsList: Asset[], fetch: Fetch = defaultFetch): Promise<string[]> {
314-
return Promise.all(jsList.map((asset) => {
315-
const { type, content, library, version } = asset;
313+
export async function fetchScripts(jsList: Asset[], fetch: Fetch = defaultFetch): Promise<string[]> {
314+
let jsBeforeRuntime = '';
315+
let jsAfterRuntime = '';
316+
317+
jsList.forEach((asset) => {
318+
if (asset.type === AssetTypeEnum.RUNTIME) {
319+
const { library, version } = asset;
320+
const globalLib = `window['${library}']`;
321+
const backupLib = `window['__${library}__']`;
322+
const versionedLib = `window['${library}@${version}']`;
323+
jsBeforeRuntime = `${jsBeforeRuntime}if (${globalLib}) {${backupLib} = ${globalLib};}\n`;
324+
jsAfterRuntime = `${jsAfterRuntime}${versionedLib} = ${globalLib}; if (${backupLib}) {${globalLib} = ${backupLib};${backupLib} = undefined;}\n`;
325+
}
326+
});
327+
328+
const result = await Promise.all(jsList.map(async (asset) => {
329+
const { type, content } = asset;
316330
if (type === AssetTypeEnum.INLINE) {
317-
return content;
331+
return {
332+
type,
333+
content,
334+
};
318335
} else {
319336
const cacheKey = `${content}${type === AssetTypeEnum.RUNTIME ? '?runtime' : ''}`;
320337
// content will script url when type is AssetTypeEnum.EXTERNAL
321338
// eslint-disable-next-line no-return-assign
322-
return cachedScriptsContent[cacheKey]
339+
return {
340+
type,
341+
content: cachedScriptsContent[cacheKey]
323342
/**
324343
* If code is being evaluated as a string with `eval` or via `new Function`,then the source origin
325344
* will be the page's origin. As a result, `//# sourceURL` appends to the generated code.
326345
* See https://sourcemaps.info/spec.html
327346
*/
328-
|| (cachedScriptsContent[cacheKey] = fetch(content)
347+
|| (cachedScriptsContent[cacheKey] = await fetch(content)
329348
.then((res) => res.text())
330-
.then((text) => {
331-
if (type === AssetTypeEnum.RUNTIME && version && library) {
332-
const globalLib = `window['${library}']`;
333-
const backupLib = `window['__${library}__']`;
334-
const versionedLib = `window['${library}@${version}']`;
335-
return `;if (${globalLib}) {${backupLib} = ${globalLib};}
336-
${text}; ${versionedLib} = ${globalLib}; if (${backupLib}) {${globalLib} = ${backupLib};${backupLib} = undefined;}`;
337-
}
338-
return text;
339-
})
340349
.then((res) => `${res} \n //# sourceURL=${content}`)
341-
);
350+
),
351+
};
342352
}
343353
}));
354+
const scriptTexts = [];
355+
let hasInsertedBeforeRuntime = false;
356+
357+
for (let i = 0; i < result.length; i++) {
358+
const { type, content } = result[i];
359+
360+
// Insert jsBeforeRuntime before the first runtime script
361+
if (type === AssetTypeEnum.RUNTIME && !hasInsertedBeforeRuntime) {
362+
scriptTexts.push(jsBeforeRuntime);
363+
hasInsertedBeforeRuntime = true;
364+
}
365+
// Add the script content
366+
scriptTexts.push(content);
367+
368+
// Insert jsAfterRuntime after the runtime script
369+
if (type === AssetTypeEnum.RUNTIME && result[i + 1]?.type !== AssetTypeEnum.RUNTIME) {
370+
scriptTexts.push(jsAfterRuntime);
371+
}
372+
}
373+
374+
return scriptTexts;
344375
}
345376

346377
// for prefetch

0 commit comments

Comments
 (0)