Skip to content

Commit

Permalink
✨ experimentalStyleIsolation support dynamic link style appending (um…
Browse files Browse the repository at this point in the history
…ijs#1005)

* ✨ experimentalStyleIsolation support dynamic link style appending

* 🎨 rename function name
  • Loading branch information
kuitos committed Oct 19, 2020
1 parent fb614e1 commit 8b632de
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 13 deletions.
6 changes: 4 additions & 2 deletions src/sandbox/patchers/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const arrayify = <T>(list: CSSRuleList | any[]) => {
return [].slice.call(list, 0) as T[];
};

const rawDocumentBodyAppend = document.body.appendChild;

export class ScopedCSS {
private static ModifiedTag = 'Symbol(style-modified-qiankun)';

Expand All @@ -31,7 +33,7 @@ export class ScopedCSS {

constructor() {
const styleNode = document.createElement('style');
document.body.appendChild(styleNode);
rawDocumentBodyAppend.call(document.body, styleNode);

this.swapNode = styleNode;
this.sheet = styleNode.sheet!;
Expand Down Expand Up @@ -181,7 +183,7 @@ export const process = (
appWrapper: HTMLElement,
stylesheetElement: HTMLStyleElement | HTMLLinkElement,
appName: string,
) => {
): void => {
// lazy singleton pattern
if (!processor) {
processor = new ScopedCSS();
Expand Down
68 changes: 57 additions & 11 deletions src/sandbox/patchers/dynamicAppend/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,29 @@ export function isStyledComponentsLike(element: HTMLStyleElement) {
);
}

function convertLinkAsStyle(
element: HTMLLinkElement,
postProcess: (styleElement: HTMLStyleElement) => void,
fetchFn = fetch,
): HTMLStyleElement {
const styleElement = document.createElement('style');
const { href } = element;
// add source link element href
styleElement.dataset.qiankunHref = href;

fetchFn(href)
.then((res: any) => res.text())
.then((styleContext: string) => {
styleElement.appendChild(document.createTextNode(styleContext));
postProcess(styleElement);
});

return styleElement;
}

const styledComponentCSSRulesMap = new WeakMap<HTMLStyleElement, CSSRuleList>();
const dynamicScriptAttachedCommentMap = new WeakMap<HTMLScriptElement, Comment>();
const dynamicLinkAttachedInlineStyleMap = new WeakMap<HTMLLinkElement, HTMLStyleElement>();

export function recordStyledComponentsCSSRules(styleElements: HTMLStyleElement[]): void {
styleElements.forEach((styleElement) => {
Expand Down Expand Up @@ -116,7 +137,7 @@ function getOverwrittenAppendChildOrInsertBefore(opts: {
switch (element.tagName) {
case LINK_TAG_NAME:
case STYLE_TAG_NAME: {
const stylesheetElement: HTMLLinkElement | HTMLStyleElement = newChild as any;
let stylesheetElement: HTMLLinkElement | HTMLStyleElement = newChild as any;
const { href } = stylesheetElement as HTMLLinkElement;
if (excludeAssetFilter && href && excludeAssetFilter(href)) {
return rawDOMAppendOrInsertBefore.call(this, element, refChild) as T;
Expand All @@ -125,7 +146,17 @@ function getOverwrittenAppendChildOrInsertBefore(opts: {
const mountDOM = appWrapperGetter();

if (scopedCSS) {
css.process(mountDOM, stylesheetElement, appName);
if (element.tagName === LINK_TAG_NAME) {
const { fetch } = frameworkConfiguration;
stylesheetElement = convertLinkAsStyle(
element,
(styleElement) => css.process(mountDOM, styleElement, appName),
fetch,
);
dynamicLinkAttachedInlineStyleMap.set(element, stylesheetElement);
} else {
css.process(mountDOM, stylesheetElement, appName);
}
}

// eslint-disable-next-line no-shadow
Expand Down Expand Up @@ -212,18 +243,33 @@ function getNewRemoveChild(
appWrapperGetterGetter: (element: HTMLElement) => ContainerConfig['appWrapperGetter'],
) {
return function removeChild<T extends Node>(this: HTMLHeadElement | HTMLBodyElement, child: T) {
const { tagName } = child as any;
if (!isHijackingTag(tagName)) return headOrBodyRemoveChild.call(this, child) as T;

try {
const { tagName } = child as any;
if (isHijackingTag(tagName)) {
const appWrapperGetter = appWrapperGetterGetter(child as any);

// container may had been removed while app unmounting if the removeChild action was async
const container = appWrapperGetter();
const attachedElement = dynamicScriptAttachedCommentMap.get(child as any) || child;
if (container.contains(attachedElement)) {
return rawRemoveChild.call(container, attachedElement) as T;
let attachedElement: Node;
switch (tagName) {
case LINK_TAG_NAME: {
attachedElement = (dynamicLinkAttachedInlineStyleMap.get(child as any) as Node) || child;
break;
}

case SCRIPT_TAG_NAME: {
attachedElement = (dynamicScriptAttachedCommentMap.get(child as any) as Node) || child;
break;
}

default: {
attachedElement = child;
}
}

// container may had been removed while app unmounting if the removeChild action was async
const appWrapperGetter = appWrapperGetterGetter(child as any);
const container = appWrapperGetter();
if (container.contains(attachedElement)) {
return rawRemoveChild.call(container, attachedElement) as T;
}
} catch (e) {
console.warn(e);
}
Expand Down

0 comments on commit 8b632de

Please sign in to comment.