Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 将子应用样式中相对地址默认转换成绝对地址 #35

Merged
merged 1 commit into from
Aug 7, 2022
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
2 changes: 1 addition & 1 deletion examples/main-react/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ h3 {
.content {
flex: 1;
height: 100vh;
overflow: scroll;
overflow: hidden scroll;
width: 1px;
}

Expand Down
2 changes: 1 addition & 1 deletion examples/main-vue/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ h3 {
.content {
flex: 1;
height: 100vh;
overflow: scroll;
overflow: hidden scroll;
width: 1px;
}

Expand Down
16 changes: 12 additions & 4 deletions examples/react16/src/Font.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { IconFont } from "tdesign-icons-react";

export default class Font extends React.Component {
componentDidMount() {
console.log("react16 font mounted")
console.log("react16 font mounted");
}
render() {
return (
Expand All @@ -24,9 +24,17 @@ export default class Font extends React.Component {
</p>
<h3>IconFont 图标示例</h3>
<p>TDesign icon</p>
<IconFont name="loading" size="2em" />
<IconFont name="close" size="2em" />
<IconFont name="check-circle-filled" size="2em" />
<p>
<IconFont name="loading" size="2em" />
<IconFont name="close" size="2em" />
<IconFont name="check-circle-filled" size="2em" />
</p>
<h3>相对地址</h3>
<p>框架会将子应用的 css 文件中的相对地址换成绝对地址</p>
<p>比如 TDesign icon 的 css 文件地址为: </p>
<p> https://tdesign.gtimg.com/icon/0.1.1/fonts/index.css</p>
<p>index.css 文件中 @font-face 中 url('./t.woff') 最终转换为:</p>
<p> https://tdesign.gtimg.com/icon/0.1.1/fonts/t.woff</p>
</div>
</div>
);
Expand Down
1 change: 1 addition & 0 deletions examples/react16/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ nav .active {
text-align: left;
max-width: 740px;
margin: 0 auto;
overflow: hidden;
}

p, div.p {
Expand Down
4 changes: 2 additions & 2 deletions packages/wujie-core/__test__/integration/font.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe("main react startApp", () => {
// 等待字体加载
await page.waitForResponse((response) => response.url().includes("https://tdesign.gtimg.com/icon/0.1.1/fonts"));
// 等待字体装载
await new Promise((resolve) => setTimeout(resolve, 100));
await new Promise((resolve) => setTimeout(resolve, 1000));
// 检查字体是否生效
expect(await page.evaluate(() => document.fonts.check("12px t", "E07F"))).toBe(true);
});
Expand All @@ -50,7 +50,7 @@ describe("main vue startApp", () => {
// 等待字体加载
await page.waitForResponse((response) => response.url().includes("https://tdesign.gtimg.com/icon/0.1.1/fonts"));
// 等待字体装载
await new Promise((resolve) => setTimeout(resolve, 100));
await new Promise((resolve) => setTimeout(resolve, 1000));
// 检查字体是否生效
expect(await page.evaluate(() => document.fonts.check("12px t", "E07F"))).toBe(true);
});
Expand Down
41 changes: 18 additions & 23 deletions packages/wujie-core/src/effect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
isFunction,
isHijackingTag,
requestIdleCallback,
compose,
error,
warn,
nextTick,
Expand All @@ -21,6 +20,7 @@ import {
import { insertScriptToIframe } from "./iframe";
import Wujie from "./sandbox";
import { getPatchStyleElements } from "./shadow";
import { getCssLoader } from "./plugin";
import { WUJIE_DATA_ID, WUJIE_DATA_FLAG, WUJIE_TIPS_REPEAT_RENDER } from "./constant";
import { ScriptObject } from "./template";

Expand Down Expand Up @@ -56,13 +56,9 @@ function manualInvokeElementEvent(element: HTMLLinkElement | HTMLScriptElement,
/**
* 样式元素的css变量处理
*/
function handleStylesheetElementPatch(stylesheetElement: HTMLStyleElement, sandbox: Wujie, baseUrl?: string) {
function handleStylesheetElementPatch(stylesheetElement: HTMLStyleElement, sandbox: Wujie) {
if (!stylesheetElement.innerHTML || sandbox.degrade) return;
const curUrl = getCurUrl(sandbox.proxyLocation as Location);
const [hostStyleSheetElement, fontStyleSheetElement] = getPatchStyleElements(
[stylesheetElement.sheet],
baseUrl ? baseUrl : curUrl
);
const [hostStyleSheetElement, fontStyleSheetElement] = getPatchStyleElements([stylesheetElement.sheet]);
if (hostStyleSheetElement) {
sandbox.shadowRoot.head.appendChild(hostStyleSheetElement);
}
Expand All @@ -76,8 +72,9 @@ function handleStylesheetElementPatch(stylesheetElement: HTMLStyleElement, sandb
*/
function patchStylesheetElement(
stylesheetElement: HTMLStyleElement,
cssLoader: (code: string) => string,
sandbox: Wujie
cssLoader: (code: string, url: string, base: string) => string,
sandbox: Wujie,
curUrl: string
) {
const innerHTMLDesc = Object.getOwnPropertyDescriptor(Element.prototype, "innerHTML");
const innerTextDesc = Object.getOwnPropertyDescriptor(HTMLElement.prototype, "innerText");
Expand All @@ -88,7 +85,7 @@ function patchStylesheetElement(
return innerHTMLDesc.get.call(stylesheetElement);
},
set: function (code: string) {
innerHTMLDesc.set.call(stylesheetElement, cssLoader(code));
innerHTMLDesc.set.call(stylesheetElement, cssLoader(code, "", curUrl));
nextTick(() => handleStylesheetElementPatch(this, sandbox));
},
},
Expand All @@ -97,7 +94,7 @@ function patchStylesheetElement(
return innerTextDesc.get.call(stylesheetElement);
},
set: function (code: string) {
innerTextDesc.set.call(stylesheetElement, cssLoader(code));
innerTextDesc.set.call(stylesheetElement, cssLoader(code, "", curUrl));
nextTick(() => handleStylesheetElementPatch(this, sandbox));
},
},
Expand All @@ -106,7 +103,7 @@ function patchStylesheetElement(
return textContentDesc.get.call(stylesheetElement);
},
set: function (code: string) {
textContentDesc.set.call(stylesheetElement, cssLoader(code));
textContentDesc.set.call(stylesheetElement, cssLoader(code, "", curUrl));
nextTick(() => handleStylesheetElementPatch(this, sandbox));
},
},
Expand All @@ -116,7 +113,7 @@ function patchStylesheetElement(
if (node.nodeType === Node.TEXT_NODE) {
return rawAppendChild.call(
stylesheetElement,
stylesheetElement.ownerDocument.createTextNode(cssLoader(node.textContent))
stylesheetElement.ownerDocument.createTextNode(cssLoader(node.textContent, "", curUrl))
);
} else return rawAppendChild(node);
},
Expand All @@ -141,8 +138,9 @@ function rewriteAppendOrInsertChild(opts: {
return rawDOMAppendOrInsertBefore.call(this, element, refChild) as T;
}

const { styleSheetElements, replace, fetch, plugins, iframe, lifecycles } = sandbox;
const { styleSheetElements, replace, fetch, plugins, iframe, lifecycles, proxyLocation } = sandbox;
const iframeDocument = iframe.contentDocument;
const curUrl = getCurUrl(proxyLocation);

// TODO 过滤可以开放
if (element.tagName) {
Expand All @@ -164,14 +162,12 @@ function rewriteAppendOrInsertChild(opts: {
// 记录js插入样式,子应用重新激活时恢复
const stylesheetElement = iframeDocument.createElement("style");
// 处理css-loader插件
stylesheetElement.innerHTML = compose(plugins.map((plugin) => plugin.cssLoader))(
replace ? replace(content) : content,
src
);
const cssLoader = getCssLoader({ plugins, replace });
stylesheetElement.innerHTML = cssLoader(content, src, curUrl);
styleSheetElements.push(stylesheetElement);
rawDOMAppendOrInsertBefore.call(this, stylesheetElement, refChild);
// 处理样式补丁
handleStylesheetElementPatch(stylesheetElement, sandbox, href);
handleStylesheetElementPatch(stylesheetElement, sandbox);
manualInvokeElementEvent(element, "load");
element = null;
},
Expand All @@ -190,10 +186,9 @@ function rewriteAppendOrInsertChild(opts: {
const stylesheetElement: HTMLLinkElement | HTMLStyleElement = newChild as any;
styleSheetElements.push(stylesheetElement);
const content = stylesheetElement.innerHTML;
const cssLoader = (content) =>
compose(plugins.map((plugin) => plugin.cssLoader))(replace ? replace(content) : content);
content && (stylesheetElement.innerHTML = cssLoader(content));
patchStylesheetElement(stylesheetElement, cssLoader, sandbox);
const cssLoader = getCssLoader({ plugins, replace });
content && (stylesheetElement.innerHTML = cssLoader(content, "", curUrl));
patchStylesheetElement(stylesheetElement, cssLoader, sandbox, curUrl);
const res = rawDOMAppendOrInsertBefore.call(this, element, refChild);
// 处理样式补丁
handleStylesheetElementPatch(stylesheetElement, sandbox);
Expand Down
13 changes: 11 additions & 2 deletions packages/wujie-core/src/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@ import processTpl, {
ScriptBaseObject,
StyleObject,
} from "./template";
import { defaultGetPublicPath, getInlineCode, requestIdleCallback, error, compose, getExcludes } from "./utils";
import {
defaultGetPublicPath,
getInlineCode,
requestIdleCallback,
error,
compose,
getExcludes,
getCurUrl,
} from "./utils";
import { WUJIE_TIPS_NO_FETCH, WUJIE_TIPS_SCRIPT_ERROR_REQUESTED, WUJIE_TIPS_CSS_ERROR_REQUESTED } from "./constant";
import Wujie from "./sandbox";
import { plugin, loadErrorHandler } from "./index";
Expand Down Expand Up @@ -51,11 +59,12 @@ export async function processCssLoader(
template: string,
getExternalStyleSheets: () => StyleResultList
): Promise<string> {
const curUrl = getCurUrl(sandbox.proxyLocation);
/** css-loader */
const composeCssLoader = compose(sandbox.plugins.map((plugin) => plugin.cssLoader));
const processedCssList: StyleResultList = getExternalStyleSheets().map(({ src, contentPromise }) => ({
src,
contentPromise: contentPromise.then((content) => composeCssLoader(content, src)),
contentPromise: contentPromise.then((content) => composeCssLoader(content, src, curUrl)),
}));
const embedHTML = await getEmbedHTML(template, processedCssList);
return sandbox.replace ? sandbox.replace(embedHTML) : embedHTML;
Expand Down
8 changes: 5 additions & 3 deletions packages/wujie-core/src/iframe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import {
isMatchSyncQueryById,
warn,
error,
compose,
execHooks,
getCurUrl,
} from "./utils";
import {
documentProxyProperties,
Expand All @@ -23,6 +23,7 @@ import {
windowProxyProperties,
windowRegWhiteList,
} from "./common";
import { getJsLoader } from "./plugin";
import { WUJIE_TIPS_EMPTY_CALLBACK, WUJIE_TIPS_SCRIPT_ERROR_REQUESTED, WUJIE_DATA_FLAG } from "./constant";
import { ScriptObjectLoader } from "./index";

Expand Down Expand Up @@ -611,8 +612,9 @@ export function insertScriptToIframe(scriptResult: ScriptObject | ScriptObjectLo
const { src, module, content, crossorigin, crossoriginType, callback } = scriptResult as ScriptObjectLoader;
const scriptElement = iframeWindow.document.createElement("script");
const nextScriptElement = iframeWindow.document.createElement("script");
const { replace, plugins } = iframeWindow.__WUJIE;
let code = compose(plugins.map((plugin) => plugin.jsLoader))(replace ? replace(content) : content, src);
const { replace, plugins, proxyLocation } = iframeWindow.__WUJIE;
const jsLoader = getJsLoader({ plugins, replace });
let code = jsLoader(content, src, getCurUrl(proxyLocation));

// 内联脚本
if (content) {
Expand Down
4 changes: 2 additions & 2 deletions packages/wujie-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ export interface plugin {
/** 处理js加载前的loader */
jsBeforeLoaders?: Array<ScriptObjectLoader>;
/** 处理js的loader */
jsLoader?: (code: string, url: string) => string;
jsLoader?: (code: string, url: string, base: string) => string;
/** 处理js加载后的loader */
jsAfterLoaders?: Array<ScriptObjectLoader>;
/** css排除列表 */
cssExcludes?: Array<string | RegExp>;
/** 处理css加载前的loader */
cssBeforeLoaders?: Array<StyleObject>;
/** 处理css的loader */
cssLoader?: (code: string, url: string) => string;
cssLoader?: (code: string, url: string, base: string) => string;
/** 处理css加载后的loader */
cssAfterLoaders?: Array<StyleObject>;
/** 子应用 window addEventListener 钩子回调 */
Expand Down
81 changes: 81 additions & 0 deletions packages/wujie-core/src/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { plugin } from "./index";
import { compose, getAbsolutePath } from "./utils";

interface loaderOption {
plugins: Array<plugin>;
replace: (code: string) => string;
}

/**
* 获取柯里化 cssLoader
*/
export function getCssLoader({ plugins, replace }: loaderOption) {
return (code: string, src: string = "", base: string): string =>
compose(plugins.map((plugin) => plugin.cssLoader))(replace ? replace(code) : code, src, base);
}

/**
* 获取柯里化 jsLoader
*/
export function getJsLoader({ plugins, replace }: loaderOption) {
return (code: string, src: string = "", base: string): string =>
compose(plugins.map((plugin) => plugin.jsLoader))(replace ? replace(code) : code, src, base);
}

/**
* 获取有效的 cssBeforeLoaders
*/
export function getCssBeforeLoaders(plugins: Array<plugin>) {
return plugins
.map((plugin) => plugin.cssBeforeLoaders)
.reduce((preLoaders, curLoaders) => preLoaders.concat(curLoaders), [])
.filter((cssLoader) => typeof cssLoader === "object")
.reverse();
}

/**
* 获取有效的 cssAfterLoaders
*/
export function getCssAfterLoaders(plugins: Array<plugin>) {
return plugins
.map((plugin) => plugin.cssAfterLoaders)
.reduce((preLoaders, curLoaders) => preLoaders.concat(curLoaders), [])
.filter((afterLoader) => typeof afterLoader === "object");
}

/**
* 获取有效的 jsBeforeLoaders
*/
export function getJsBeforeLoaders(plugins: Array<plugin>) {
return plugins
.map((plugin) => plugin.jsBeforeLoaders)
.reduce((preLoaders, curLoaders) => preLoaders.concat(curLoaders), [])
.filter((preLoader) => typeof preLoader === "object");
}

/**
* 获取有效的 jsAfterLoaders
*/
export function getJsAfterLoaders(plugins: Array<plugin>) {
return plugins
.map((plugin) => plugin.jsAfterLoaders)
.reduce((preLoaders, curLoaders) => preLoaders.concat(curLoaders), [])
.filter((afterLoader) => typeof afterLoader === "object");
}

/**
* 转换子应用css内的相对地址成绝对地址
*/
function cssRelativePathResolve(code: string, src: string, base: string) {
const baseUrl = src ? getAbsolutePath(src, base) : base;
const urlReg = /(url\()([^)]*)(\))/g;
return code.replace(urlReg, (_m, pre, url, post) => {
const urlString = url.replace(/["']/g, "");
const absoluteUrl = getAbsolutePath(urlString, baseUrl);
return pre + "'" + absoluteUrl + "'" + post;
});
}

export default {
cssLoader: cssRelativePathResolve,
};
Loading