From 6cdef92e56b523c26b5f83fbef0d8ec2ac180cfe Mon Sep 17 00:00:00 2001 From: maoxiaoke Date: Tue, 29 Dec 2020 10:27:16 +0800 Subject: [PATCH 01/11] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20dynamic=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- packages/icestark-module/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0b4d37f5..38836393 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ice/stark", - "version": "2.0.2", + "version": "2.0.3", "description": "Icestark is a JavaScript library for multiple projects, Ice workbench solution.", "scripts": { "install:deps": "rm -rf node_modules && rm -rf ./packages/*/node_modules && yarn install && lerna exec -- npm install", diff --git a/packages/icestark-module/package.json b/packages/icestark-module/package.json index e2e796e9..b292dc1b 100644 --- a/packages/icestark-module/package.json +++ b/packages/icestark-module/package.json @@ -1,6 +1,6 @@ { "name": "@ice/stark-module", - "version": "1.2.0", + "version": "1.3.0", "description": "toolkit for load standard micro-module", "main": "lib/index.js", "scripts": { From 3905de3f839d4452268762ca621d54965116ced1 Mon Sep 17 00:00:00 2001 From: maoxiaoke Date: Wed, 6 Jan 2021 22:18:42 +0800 Subject: [PATCH 02/11] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20fetcher?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/AppRouter.tsx | 7 +++++-- src/apps.ts | 30 +++++++++++++++++++++--------- src/start.ts | 10 ++++++++++ src/util/handleAssets.ts | 19 ++++++++----------- 4 files changed, 44 insertions(+), 22 deletions(-) diff --git a/src/AppRouter.tsx b/src/AppRouter.tsx index 540c4f11..84e9e000 100644 --- a/src/AppRouter.tsx +++ b/src/AppRouter.tsx @@ -5,7 +5,7 @@ import appHistory from './appHistory'; import renderComponent from './util/renderComponent'; import { ICESTSRK_ERROR, ICESTSRK_NOT_FOUND } from './util/constant'; import { setCache } from './util/cache'; -import start, { unload } from './start'; +import start, { unload, Fetch, defaultFetch } from './start'; import { matchActivePath, PathData } from './util/matchPath'; import { AppConfig } from './apps'; @@ -28,6 +28,7 @@ export interface AppRouterProps { element?: HTMLElement | HTMLLinkElement | HTMLStyleElement | HTMLScriptElement, ) => boolean; basename?: string; + fetch?: Fetch; } interface AppRouterState { @@ -65,6 +66,7 @@ export default class AppRouter extends React.Component {}, onAppLeave: () => {}, basename: '', + fetch: defaultFetch, }; constructor(props: AppRouterProps) { @@ -76,7 +78,7 @@ export default class AppRouter extends React.Component app.name); } @@ -77,7 +81,7 @@ export function getAppStatus(appName: string) { } export function registerMicroApp(appConfig: AppConfig, appLifecyle?: AppLifecylceOptions) { - // check appConfig.name + // check appConfig.name if (getAppNames().includes(appConfig.name)) { throw Error(`name ${appConfig.name} already been regsitered`); } @@ -131,8 +135,10 @@ export function updateAppConfig(appName: string, config) { // load app js assets export async function loadAppModule(appConfig: AppConfig) { + const { onLoadingApp, onFinishLoading, fetch } = getAppConfig(appConfig.name)?.configuration ?? globalConfiguration; + let lifecycle: ModuleLifeCycle = {}; - globalConfiguration.onLoadingApp(appConfig); + onLoadingApp(appConfig); const appSandbox = createSandbox(appConfig.sandbox); const { url, container, entry, entryContent, name } = appConfig; const appAssets = url ? getUrlAssets(url) : await getEntryAssets({ @@ -141,13 +147,14 @@ export async function loadAppModule(appConfig: AppConfig) { href: location.href, entryContent, assetsCacheKey: name, + fetch, }); updateAppConfig(appConfig.name, { appAssets, appSandbox }); if (appConfig.umd) { await loadAndAppendCssAssets(appAssets); lifecycle = await loadUmdModule(appAssets.jsList, appSandbox); } else { - await appendAssets(appAssets, appSandbox); + await appendAssets(appAssets, appSandbox, fetch); lifecycle = { mount: getCache(AppLifeCycleEnum.AppEnter), unmount: getCache(AppLifeCycleEnum.AppLeave), @@ -155,7 +162,7 @@ export async function loadAppModule(appConfig: AppConfig) { setCache(AppLifeCycleEnum.AppEnter, null); setCache(AppLifeCycleEnum.AppLeave, null); } - globalConfiguration.onFinishLoading(appConfig); + onFinishLoading(appConfig); return combineLifecyle(lifecycle, appConfig); } @@ -198,7 +205,7 @@ export function getAppConfigForLoad (app: string | AppConfig, options?: AppLifec return getAppConfig(name); }; -export async function createMicroApp(app: string | AppConfig, appLifecyle?: AppLifecylceOptions) { +export async function createMicroApp(app: string | AppConfig, appLifecyle?: AppLifecylceOptions, configuration?: StartConfiguration) { const appConfig = getAppConfigForLoad(app, appLifecyle); const appName = appConfig && appConfig.name; // compatible with use inIcestark @@ -207,6 +214,10 @@ export async function createMicroApp(app: string | AppConfig, appLifecyle?: AppL setCache('root', container); } if (appConfig && appName) { + // add configuration to every micro app + const userConfiguration = { ...globalConfiguration, ...configuration }; + updateAppConfig(appName, { configuration: userConfiguration }); + // check status of app if (appConfig.status === NOT_LOADED || appConfig.status === LOAD_ERROR ) { if (appConfig.title) document.title = appConfig.title; @@ -219,7 +230,7 @@ export async function createMicroApp(app: string | AppConfig, appLifecyle?: AppL updateAppConfig(appName, { ...lifeCycle, status: NOT_MOUNTED }); } } catch (err){ - globalConfiguration.onError(err); + userConfiguration.onError(err); updateAppConfig(appName, { status: LOAD_ERROR }); } if (lifeCycle.mount) { @@ -257,7 +268,8 @@ export async function unmountMicroApp(appName: string) { const appConfig = getAppConfig(appName); if (appConfig && (appConfig.status === MOUNTED || appConfig.status === LOADING_ASSETS || appConfig.status === NOT_MOUNTED)) { // remove assets if app is not cached - emptyAssets(globalConfiguration.shouldAssetsRemove, !appConfig.cached && appConfig.name); + const { shouldAssetsRemove } = getAppConfig(appName)?.configuration ?? globalConfiguration; + emptyAssets(shouldAssetsRemove, !appConfig.cached && appConfig.name); updateAppConfig(appName, { status: UNMOUNTED }); if (!appConfig.cached && appConfig.appSandbox) { appConfig.appSandbox.clear(); diff --git a/src/start.ts b/src/start.ts index f776436f..2d649d91 100644 --- a/src/start.ts +++ b/src/start.ts @@ -12,6 +12,14 @@ import { AppConfig, getMicroApps, createMicroApp, unmountMicroApp, clearMicroApp import { emptyAssets, recordAssets } from './util/handleAssets'; import { LOADING_ASSETS, MOUNTED } from './util/constant'; +if (!window.fetch) { + throw new Error('[icestark] window.fetch not found, you need polyfill it'); +} + +export const defaultFetch = window.fetch.bind(window); + +export type Fetch = typeof window.fetch | ((url: string) => Promise); + export interface StartConfiguration { shouldAssetsRemove?: ( assetUrl?: string, @@ -31,6 +39,7 @@ export interface StartConfiguration { onError?: (err: Error) => void; onActiveApps?: (appConfigs: AppConfig[]) => void; reroute?: (url: string, type: RouteType | 'init' | 'popstate'| 'hashchange') => void; + fetch?: Fetch; } const globalConfiguration: StartConfiguration = { @@ -43,6 +52,7 @@ const globalConfiguration: StartConfiguration = { onError: () => {}, onActiveApps: () => {}, reroute, + fetch: defaultFetch, }; interface OriginalStateFunction { diff --git a/src/util/handleAssets.ts b/src/util/handleAssets.ts index 8ccb943b..c6f9b6ce 100644 --- a/src/util/handleAssets.ts +++ b/src/util/handleAssets.ts @@ -2,8 +2,8 @@ import * as urlParse from 'url-parse'; import Sandbox, { SandboxProps, SandboxContructor } from '@ice/sandbox'; import { PREFIX, DYNAMIC, STATIC, IS_CSS_REGEX } from './constant'; import { warn, error } from './message'; +import { Fetch, defaultFetch } from '../start'; -const winFetch = window.fetch; const COMMENT_REGEX = //g; const SCRIPT_REGEX = /]*>([\s\S]*?)<\/script>/gi; const SCRIPT_SRC_REGEX = /]*src=['"]?([^'"]*)['"]?\b[^>]*>/gi; @@ -42,10 +42,6 @@ export interface ParsedConfig { pathname: string; } -export interface Fetch { - (input: RequestInfo, init?: RequestInit): Promise; -} - // Lifecycle Props export interface ILifecycleProps { container: HTMLElement; @@ -154,7 +150,8 @@ export function getUrlAssets(url: string | string[]) { } const cachedScriptsContent: object = {}; -export function fetchScripts(jsList: Asset[], fetch: Fetch = winFetch) { + +export function fetchScripts(jsList: Asset[], fetch = defaultFetch ) { return Promise.all(jsList.map((asset) => { const { type, content } = asset; if (type === AssetTypeEnum.INLINE) { @@ -165,9 +162,9 @@ export function fetchScripts(jsList: Asset[], fetch: Fetch = winFetch) { } })); } -export async function appendAssets(assets: Assets, sandbox?: Sandbox) { +export async function appendAssets(assets: Assets, sandbox?: Sandbox, fetch = defaultFetch) { await loadAndAppendCssAssets(assets); - await loadAndAppendJsAssets(assets, sandbox); + await loadAndAppendJsAssets(assets, sandbox, fetch); } export function parseUrl(entry: string): ParsedConfig { @@ -279,7 +276,7 @@ export async function getEntryAssets({ entryContent, assetsCacheKey, href, - fetch = winFetch, + fetch = defaultFetch, }: { root: HTMLElement | ShadowRoot; entry?: string; @@ -423,14 +420,14 @@ export async function loadAndAppendCssAssets(assets: Assets) { * @param {Sandbox} [sandbox] * @returns */ -export async function loadAndAppendJsAssets(assets: Assets, sandbox?: Sandbox) { +export async function loadAndAppendJsAssets(assets: Assets, sandbox?: Sandbox, fetch = defaultFetch) { const jsRoot: HTMLElement = document.getElementsByTagName('head')[0]; const { jsList } = assets; // handle scripts if (sandbox && !sandbox.sandboxDisabled) { - const jsContents = await fetchScripts(jsList); + const jsContents = await fetchScripts(jsList, fetch); // excute code by order jsContents.forEach(script => { sandbox.execScriptInSandbox(script); From 4cb790608dbab5d62598f5f293881b03220abaf2 Mon Sep 17 00:00:00 2001 From: maoxiaoke Date: Fri, 8 Jan 2021 16:27:12 +0800 Subject: [PATCH 03/11] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20refact=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apps.ts | 9 ++++++--- tests/handleAssets.spec.tsx | 4 +--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/apps.ts b/src/apps.ts index 7bbbce3f..1d2e3c9d 100644 --- a/src/apps.ts +++ b/src/apps.ts @@ -135,7 +135,7 @@ export function updateAppConfig(appName: string, config) { // load app js assets export async function loadAppModule(appConfig: AppConfig) { - const { onLoadingApp, onFinishLoading, fetch } = getAppConfig(appConfig.name)?.configuration ?? globalConfiguration; + const { onLoadingApp, onFinishLoading, fetch } = getAppConfig(appConfig.name)?.configuration || globalConfiguration; let lifecycle: ModuleLifeCycle = {}; onLoadingApp(appConfig); @@ -215,7 +215,10 @@ export async function createMicroApp(app: string | AppConfig, appLifecyle?: AppL } if (appConfig && appName) { // add configuration to every micro app - const userConfiguration = { ...globalConfiguration, ...configuration }; + const userConfiguration = globalConfiguration; + Object.keys(configuration || {}).forEach(key => { + userConfiguration[key] = configuration[key]; + }); updateAppConfig(appName, { configuration: userConfiguration }); // check status of app @@ -268,7 +271,7 @@ export async function unmountMicroApp(appName: string) { const appConfig = getAppConfig(appName); if (appConfig && (appConfig.status === MOUNTED || appConfig.status === LOADING_ASSETS || appConfig.status === NOT_MOUNTED)) { // remove assets if app is not cached - const { shouldAssetsRemove } = getAppConfig(appName)?.configuration ?? globalConfiguration; + const { shouldAssetsRemove } = getAppConfig(appName)?.configuration || globalConfiguration; emptyAssets(shouldAssetsRemove, !appConfig.cached && appConfig.name); updateAppConfig(appName, { status: UNMOUNTED }); if (!appConfig.cached && appConfig.appSandbox) { diff --git a/tests/handleAssets.spec.tsx b/tests/handleAssets.spec.tsx index 54b6e42a..9de4719c 100644 --- a/tests/handleAssets.spec.tsx +++ b/tests/handleAssets.spec.tsx @@ -223,9 +223,7 @@ describe('getEntryAssets', () => { entry: htmlUrl, assetsCacheKey: '/test', fetch: (url) => ( - new Promise(resolve => { - resolve(fetchMockFn(url)); - }) + Promise.resolve(fetchMockFn(url) as Response) ), }) .then(() => { From 44700f1e9102873940a5503b4741d20a2c3973d1 Mon Sep 17 00:00:00 2001 From: maoxiaoke Date: Fri, 8 Jan 2021 16:28:12 +0800 Subject: [PATCH 04/11] Feat/hash router (#220) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 export AppLink from icestark-app --- packages/icestark-app/package.json | 2 +- packages/icestark-app/src/AppLink.tsx | 37 +++++++++++++++++++++ packages/icestark-app/src/appHistory.ts | 18 +++++++--- packages/icestark-app/src/index.ts | 1 + packages/icestark-app/src/util/formatUrl.ts | 10 ++++++ packages/icestark-app/tests/index.spec.tsx | 11 ++++++ 6 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 packages/icestark-app/src/AppLink.tsx create mode 100644 packages/icestark-app/src/util/formatUrl.ts diff --git a/packages/icestark-app/package.json b/packages/icestark-app/package.json index aae74969..aa6c0afb 100644 --- a/packages/icestark-app/package.json +++ b/packages/icestark-app/package.json @@ -1,6 +1,6 @@ { "name": "@ice/stark-app", - "version": "1.2.0", + "version": "1.2.1", "description": "icestark-app is a JavaScript library for icestark, used by sub-application.", "scripts": { "build": "rm -rf lib && tsc", diff --git a/packages/icestark-app/src/AppLink.tsx b/packages/icestark-app/src/AppLink.tsx new file mode 100644 index 00000000..959d0cb4 --- /dev/null +++ b/packages/icestark-app/src/AppLink.tsx @@ -0,0 +1,37 @@ +/* eslint-disable react/jsx-filename-extension */ +import * as React from 'react'; +import formatUrl from './util/formatUrl'; + +export type AppLinkProps = { + to: string; + hashType?: boolean; + replace?: boolean; + message?: string; + children?: React.ReactNode; +} & React.AnchorHTMLAttributes; + +const AppLink = (props: AppLinkProps) => { + const { to, hashType, replace, message, children, ...rest } = props; + const linkTo = formatUrl(to, hashType); + return ( + { + e.preventDefault(); + // eslint-disable-next-line no-alert + if (message && window.confirm(message) === false) { + return false; + } + + const changeState = window.history[replace ? 'replaceState' : 'pushState']; + + changeState({}, null, linkTo); + }} + > + {children} + + ); +}; + +export default AppLink; diff --git a/packages/icestark-app/src/appHistory.ts b/packages/icestark-app/src/appHistory.ts index eb1666eb..e95b977c 100644 --- a/packages/icestark-app/src/appHistory.ts +++ b/packages/icestark-app/src/appHistory.ts @@ -1,9 +1,19 @@ +import formatUrl from './util/formatUrl'; + const appHistory = { - push: (url: string) => { - window.history.pushState({}, null, url); + push: (url: string, hashType?: boolean) => { + window.history.pushState( + {}, + null, + formatUrl(url, hashType) + ); }, - replace: (url: string) => { - window.history.replaceState({}, null, url); + replace: (url: string, hashType?: boolean) => { + window.history.replaceState( + {}, + null, + formatUrl(url, hashType) + ); }, }; diff --git a/packages/icestark-app/src/index.ts b/packages/icestark-app/src/index.ts index d16b48a1..5687db3c 100644 --- a/packages/icestark-app/src/index.ts +++ b/packages/icestark-app/src/index.ts @@ -5,3 +5,4 @@ export { default as registerAppEnter } from './registerAppEnter'; export { default as registerAppLeave } from './registerAppLeave'; export { default as appHistory } from './appHistory'; export { default as isInIcestark } from './isInIcestark'; +export { default as AppLink } from './AppLink'; diff --git a/packages/icestark-app/src/util/formatUrl.ts b/packages/icestark-app/src/util/formatUrl.ts new file mode 100644 index 00000000..17894552 --- /dev/null +++ b/packages/icestark-app/src/util/formatUrl.ts @@ -0,0 +1,10 @@ +/** + * format url + * @param url + * @param hashType + */ +const formatUrl = (url: string, hashType?: boolean) => { + return (hashType && url.indexOf('#') === -1) ? `#${url}` : url; +}; + +export default formatUrl; diff --git a/packages/icestark-app/tests/index.spec.tsx b/packages/icestark-app/tests/index.spec.tsx index 1e04a960..62f2be80 100644 --- a/packages/icestark-app/tests/index.spec.tsx +++ b/packages/icestark-app/tests/index.spec.tsx @@ -10,6 +10,7 @@ import { isInIcestark, } from '../src/index'; import { setCache, getCache } from '../src/cache'; +import formatUrl from '../src/util/formatUrl'; const namespace = 'ICESTARK'; @@ -132,3 +133,13 @@ describe('isInIcestark', () => { expect(isInIcestark()).toBe(true); }); }); + +describe('formatUrl', () => { + test('formatUrl', () => { + expect(formatUrl('/seller')).toBe('/seller'); + + expect(formatUrl('#/seller')).toBe('#/seller'); + + expect(formatUrl('/seller', true)).toBe('#/seller'); + }) +}) From 1ac70fe81226bcb7eabd820a49c447a77136072a Mon Sep 17 00:00:00 2001 From: maoxiaoke Date: Mon, 11 Jan 2021 14:23:19 +0800 Subject: [PATCH 05/11] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20new=20html=20parse?= =?UTF-8?q?r=20(#218)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 new html parser --- src/util/handleAssets.ts | 121 ++++++++++++++++++++---------------- tests/handleAssets.spec.tsx | 57 ++++++++++++----- 2 files changed, 107 insertions(+), 71 deletions(-) diff --git a/src/util/handleAssets.ts b/src/util/handleAssets.ts index 8ccb943b..864503bf 100644 --- a/src/util/handleAssets.ts +++ b/src/util/handleAssets.ts @@ -5,12 +5,9 @@ import { warn, error } from './message'; const winFetch = window.fetch; const COMMENT_REGEX = //g; -const SCRIPT_REGEX = /]*>([\s\S]*?)<\/script>/gi; -const SCRIPT_SRC_REGEX = /]*src=['"]?([^'"]*)['"]?\b[^>]*>/gi; -const STYLE_REGEX = /]*>([^<]*)<\/style>/gi; -const LINK_HREF_REGEX = /]*href=['"]?([^'"]*)['"]?\b[^>]*>/gi; -const CSS_REGEX = new RegExp([STYLE_REGEX, LINK_HREF_REGEX].map((reg) => reg.source).join('|'), 'gi'); -const STYLE_SHEET_REGEX = /rel=['"]stylesheet['"]/gi; + +const EMPTY_STRING = ''; +const STYLESHEET_LINK_TYPE = 'stylesheet'; export enum AssetTypeEnum { INLINE = 'inline', @@ -28,7 +25,7 @@ export interface Asset { } export interface ProcessedContent { - html: string; + html: HTMLElement; assets: Assets; } @@ -209,61 +206,75 @@ export function getUrl(entry: string, relativePath: string): string { * If script/link processed by @ice/stark, add comment for it */ export function getComment(tag: string, from: string, type: AssetCommentEnum): string { - return ``; + return `${tag} ${from} ${type} by @ice/stark`; +} + +/** + * check if link is absolute url + * @param url + */ +export function isAbsoluteUrl(url: string): boolean { + return (/^(https?:)?\/\/.+/).test(url); +} + + +export function replaceNodeWithComment(node: HTMLElement, comment: string): void { + if (node?.parentNode) { + const commentNode = document.createComment(comment); + node.parentNode.appendChild(commentNode); + node.parentNode.removeChild(node); + } } /** * html -> { html: processedHtml, assets: processedAssets } */ export function processHtml(html: string, entry?: string): ProcessedContent { - if (!html) return { html: '', assets: { cssList:[], jsList: []} }; - - const processedJSAssets = []; - const processedCSSAssets = []; - const processedHtml = html - .replace(COMMENT_REGEX, '') - .replace(SCRIPT_REGEX, (...args) => { - const [matchStr, matchContent] = args; - if (!matchStr.match(SCRIPT_SRC_REGEX)) { - processedJSAssets.push({ + if (!html) return { html: document.createElement('div'), assets: { cssList:[], jsList: []} }; + + const domContent = (new DOMParser()).parseFromString(html.replace(COMMENT_REGEX, ''), 'text/html'); + + // process js assets + const scripts = Array.from(domContent.getElementsByTagName('script')); + const processedJSAssets = scripts.map(script => { + const inlineScript = script.src === EMPTY_STRING; + + const externalSrc = !inlineScript && (isAbsoluteUrl(script.src) ? script.src : getUrl(entry, script.src)); + const commentType = inlineScript ? AssetCommentEnum.PROCESSED : AssetCommentEnum.REPLACED; + replaceNodeWithComment(script, getComment('script', inlineScript ? 'inline' : script.src, commentType)); + + return { + type: inlineScript ? AssetTypeEnum.INLINE : AssetTypeEnum.EXTERNAL, + content: inlineScript ? script.text : externalSrc, + }; + }); + + // process css assets + const inlineStyleSheets = Array.from(domContent.getElementsByTagName('style')); + const externalStyleSheets = Array.from(domContent.getElementsByTagName('link')) + .filter(link => !link.rel || link.rel.includes(STYLESHEET_LINK_TYPE)); + + const processedCSSAssets = [ + ...inlineStyleSheets + .map(sheet => { + replaceNodeWithComment(sheet, getComment('style', 'inline', AssetCommentEnum.REPLACED)); + return { type: AssetTypeEnum.INLINE, - content: matchContent, - }); - - return getComment('script', 'inline', AssetCommentEnum.REPLACED); - } else { - return matchStr.replace(SCRIPT_SRC_REGEX, (_, argSrc2) => { - const url = argSrc2.indexOf('//') >= 0 ? argSrc2 : getUrl(entry, argSrc2); - processedJSAssets.push({ - type: AssetTypeEnum.EXTERNAL, - content: url, - }); - - return getComment('script', argSrc2, AssetCommentEnum.REPLACED); - }); - } - }) - .replace(CSS_REGEX, (...args) => { - const [matchStr, matchStyle, matchLink] = args; - // not stylesheet, return as it is - if (matchStr.match(STYLE_SHEET_REGEX)) { - const url = matchLink.indexOf('//') >= 0 ? matchLink : getUrl(entry, matchLink); - processedCSSAssets.push({ + content: sheet.innerText, + }; + }), + ...externalStyleSheets + .map((sheet) => { + replaceNodeWithComment(sheet, getComment('link', sheet.href, AssetCommentEnum.PROCESSED)); + return { type: AssetTypeEnum.EXTERNAL, - content: url, - }); - return `${getComment('link', matchLink, AssetCommentEnum.PROCESSED)}`; - } else if (matchStyle){ - processedCSSAssets.push({ - type: AssetTypeEnum.INLINE, - content: matchStyle, - }); - return getComment('style', 'inline', AssetCommentEnum.REPLACED); - } - return matchStr; - }); + content: isAbsoluteUrl(sheet.href) ? sheet.href : getUrl(entry, sheet.href), + }; + }), + ]; + return { - html: processedHtml, + html: domContent.getElementsByTagName('html')[0], assets: { jsList: processedJSAssets, cssList: processedCSSAssets, @@ -307,7 +318,9 @@ export async function getEntryAssets({ cachedProcessedContent[assetsCacheKey] = cachedContent; } - root.innerHTML = cachedContent.html; + const { html } = cachedContent; + root.appendChild(html); + return cachedContent.assets; } diff --git a/tests/handleAssets.spec.tsx b/tests/handleAssets.spec.tsx index 54b6e42a..b8cfecf7 100644 --- a/tests/handleAssets.spec.tsx +++ b/tests/handleAssets.spec.tsx @@ -14,6 +14,7 @@ import { processHtml, appendExternalScript, getUrlAssets, + isAbsoluteUrl, } from '../src/util/handleAssets'; import { setCache } from '../src/util/cache'; @@ -89,6 +90,9 @@ const tempHTML = ' ' + ' ' + ' ' + + ' ' + '
' + ' ' + ''; @@ -96,7 +100,7 @@ const tempHTML = describe('getComment', () => { test('getComment', () => { expect(getComment('script', 'inline', AssetCommentEnum.REPLACED)).toBe( - '', + 'script inline replaced by @ice/stark', ); expect( @@ -105,35 +109,39 @@ describe('getComment', () => { 'https://g.alicdn.com/platform/common/global.css', AssetCommentEnum.REPLACED, ), - ).toBe(''); + ).toBe('link https://g.alicdn.com/platform/common/global.css replaced by @ice/stark'); expect(getComment('link', '/test.css', AssetCommentEnum.PROCESSED)).toBe( - '', + 'link /test.css processed by @ice/stark', ); }); }); describe('processHtml', () => { test('processHtml', () => { - expect(processHtml(undefined).html).toBe(''); + expect(processHtml(undefined).html.innerHTML).toBe(''); const { html, assets: {jsList, cssList} } = processHtml(tempHTML); + const div = document.createElement('div'); + div.appendChild(html); + const content = div.innerHTML; - expect(html).not.toContain('