Skip to content

Commit

Permalink
feat: 添加jsIgnores和cssIgnores两个插件 (#95)
Browse files Browse the repository at this point in the history
  • Loading branch information
yiludege authored Aug 25, 2022
1 parent e957c67 commit 7631e7c
Show file tree
Hide file tree
Showing 15 changed files with 222 additions and 178 deletions.
41 changes: 41 additions & 0 deletions packages/wujie-core/__test__/unit/plugin.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { getEffectLoaders, isMatchUrl } from "../../src/plugin";

describe("utils test", () => {
describe("getEffectLoaders", () => {
test("plugins is undefined, should return empty array", () => {
expect(getEffectLoaders("jsExcludes", [])).toEqual([]);
expect(getEffectLoaders("cssExcludes", [])).toEqual([]);
});

test('sourceType is "jsExcludes", should return jsExcludes', () => {
const jsExcludes = ["https://www.foo/a.js", /b\.js/];
const plugins = [
{
jsExcludes,
},
];
expect(getEffectLoaders("jsExcludes", plugins)).toEqual(jsExcludes);
});

test('sourceType is "cssExcludes", should return cssExcludes', () => {
const cssExcludes = ["https://www.foo/a.css", /b\.css/];
const plugins = [
{
cssExcludes,
},
];
expect(getEffectLoaders("cssExcludes", plugins)).toEqual(cssExcludes);
});
});
describe("isMatchUrl", () => {
test("url is not exclude, should return false", () => {
expect(isMatchUrl("https://www.foo/a.js", ["https://www.foo/b.js"])).toBe(false);
});
test("url is exclude, should return true", () => {
expect(isMatchUrl("https://www.foo/a.js", ["https://www.foo/a.js"])).toBe(true);
});
test("url is match regexp, should return true", () => {
expect(isMatchUrl("https://www.foo/a.js", [/a\.js/])).toBe(true);
});
});
});
42 changes: 0 additions & 42 deletions packages/wujie-core/__test__/unit/utils.test.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/wujie-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"lib": "webpack build --config webpack.config.js",
"esm": "cross-env BABEL_ENV=esm babel ./src --out-dir ./esm --extensions .ts --source-maps && tsc --emitDeclarationOnly",
"prepack": "bash build.sh",
"start": "cross-env BABEL_ENV=esm babel ./src --out-dir ./esm --extensions .ts --source-maps inline --watch",
"start": "cross-env BABEL_ENV=esm babel ./src --out-dir ./esm --extensions .ts --source-maps inline --watch & tsc --emitDeclarationOnly",
"lint": "eslint --ext .ts ./src --fix && git add .",
"test": "npm run test:unit && npm run test:integration",
"test:unit": "jest -c __test__/unit/jest.config.js",
Expand Down
61 changes: 29 additions & 32 deletions packages/wujie-core/src/effect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,11 @@ import {
rawAddEventListener,
rawRemoveEventListener,
} from "./common";
import {
isFunction,
isHijackingTag,
requestIdleCallback,
error,
warn,
nextTick,
isExcludeUrl,
getExcludes,
getCurUrl,
} from "./utils";
import { isFunction, isHijackingTag, requestIdleCallback, error, warn, nextTick, getCurUrl } from "./utils";
import { insertScriptToIframe } from "./iframe";
import Wujie from "./sandbox";
import { getPatchStyleElements } from "./shadow";
import { getCssLoader } from "./plugin";
import { getCssLoader, getEffectLoaders, isMatchUrl } from "./plugin";
import { WUJIE_DATA_ID, WUJIE_DATA_FLAG, WUJIE_TIPS_REPEAT_RENDER } from "./constant";
import { ScriptObject } from "./template";

Expand Down Expand Up @@ -152,27 +142,33 @@ function rewriteAppendOrInsertChild(opts: {
case "LINK": {
const { href } = element as HTMLLinkElement;
// 排除css
if (
href &&
!plugins
.map((plugin) => plugin.cssExcludes)
.reduce((pre, next) => pre.concat(next), [])
.filter((item) => item)
.includes(href)
) {
getExternalStyleSheets([{ src: href }], fetch, lifecycles.loadError).forEach(({ src, contentPromise }) =>
if (href && !isMatchUrl(href, getEffectLoaders("cssExcludes", plugins))) {
getExternalStyleSheets(
[{ src: href, ignore: isMatchUrl(href, getEffectLoaders("cssIgnores", plugins)) }],
fetch,
lifecycles.loadError
).forEach(({ src, ignore, contentPromise }) =>
contentPromise.then(
(content) => {
// 记录js插入样式,子应用重新激活时恢复
const stylesheetElement = iframeDocument.createElement("style");
// 处理css-loader插件
const cssLoader = getCssLoader({ plugins, replace });
stylesheetElement.innerHTML = cssLoader(content, src, curUrl);
styleSheetElements.push(stylesheetElement);
rawDOMAppendOrInsertBefore.call(this, stylesheetElement, refChild);
// 处理样式补丁
handleStylesheetElementPatch(stylesheetElement, sandbox);
manualInvokeElementEvent(element, "load");
// 处理 ignore 样式
if (ignore && src) {
const stylesheetElement = iframeDocument.createElement("link");
stylesheetElement.setAttribute("type", "text/css");
stylesheetElement.setAttribute("ref", "stylesheet");
rawDOMAppendOrInsertBefore.call(this, stylesheetElement, refChild);
manualInvokeElementEvent(element, "load");
} else {
// 记录js插入样式,子应用重新激活时恢复
const stylesheetElement = iframeDocument.createElement("style");
// 处理css-loader插件
const cssLoader = getCssLoader({ plugins, replace });
stylesheetElement.innerHTML = cssLoader(content, src, curUrl);
styleSheetElements.push(stylesheetElement);
rawDOMAppendOrInsertBefore.call(this, stylesheetElement, refChild);
// 处理样式补丁
handleStylesheetElementPatch(stylesheetElement, sandbox);
manualInvokeElementEvent(element, "load");
}
element = null;
},
() => {
Expand Down Expand Up @@ -201,7 +197,7 @@ function rewriteAppendOrInsertChild(opts: {
case "SCRIPT": {
const { src, text, type, crossOrigin } = element as HTMLScriptElement;
// 排除js
if (!isExcludeUrl(src, getExcludes("jsExcludes", plugins))) {
if (!isMatchUrl(src, getEffectLoaders("jsExcludes", plugins))) {
const execScript = (scriptResult) => {
// 假如子应用被连续渲染两次,两次渲染会导致处理流程的交叉污染
if (sandbox.iframe === null) return warn(WUJIE_TIPS_REPEAT_RENDER);
Expand All @@ -214,6 +210,7 @@ function rewriteAppendOrInsertChild(opts: {
module: type === "module",
crossorigin: crossOrigin !== null,
crossoriginType: crossOrigin || "",
ignore: isMatchUrl(src, getEffectLoaders("jsIgnores", plugins)),
} as ScriptObject;
getExternalScripts([scriptOptions], fetch, lifecycles.loadError).forEach((scriptResult) =>
scriptResult.contentPromise.then(
Expand Down
56 changes: 31 additions & 25 deletions packages/wujie-core/src/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,14 @@ import processTpl, {
ScriptBaseObject,
StyleObject,
} from "./template";
import {
defaultGetPublicPath,
getInlineCode,
requestIdleCallback,
error,
compose,
getExcludes,
getCurUrl,
} from "./utils";
import { defaultGetPublicPath, getInlineCode, requestIdleCallback, error, compose, getCurUrl } from "./utils";
import { WUJIE_TIPS_NO_FETCH, WUJIE_TIPS_SCRIPT_ERROR_REQUESTED, WUJIE_TIPS_CSS_ERROR_REQUESTED } from "./constant";
import { getEffectLoaders, isMatchUrl } from "./plugin";
import Wujie from "./sandbox";
import { plugin, loadErrorHandler } from "./index";

export type ScriptResultList = (ScriptBaseObject & { contentPromise: Promise<string> })[];
export type StyleResultList = { src: string; contentPromise: Promise<string> }[];
export type StyleResultList = { src: string; contentPromise: Promise<string>; ignore?: boolean }[];

interface htmlParseResult {
template: string;
Expand Down Expand Up @@ -62,8 +55,9 @@ export async function processCssLoader(
const curUrl = getCurUrl(sandbox.proxyLocation);
/** css-loader */
const composeCssLoader = compose(sandbox.plugins.map((plugin) => plugin.cssLoader));
const processedCssList: StyleResultList = getExternalStyleSheets().map(({ src, contentPromise }) => ({
const processedCssList: StyleResultList = getExternalStyleSheets().map(({ src, ignore, contentPromise }) => ({
src,
ignore,
contentPromise: contentPromise.then((content) => composeCssLoader(content, src, curUrl)),
}));
const embedHTML = await getEmbedHTML(template, processedCssList);
Expand All @@ -78,12 +72,14 @@ async function getEmbedHTML(template, styleResultList: StyleResultList): Promise
let embedHTML = template;

return Promise.all(
styleResultList.map((scriptResult, index) =>
scriptResult.contentPromise.then((content) => {
if (scriptResult.src) {
styleResultList.map((styleResult, index) =>
styleResult.contentPromise.then((content) => {
if (styleResult.src) {
embedHTML = embedHTML.replace(
genLinkReplaceSymbol(scriptResult.src),
`<style>/* ${scriptResult.src} */${content}</style>`
genLinkReplaceSymbol(styleResult.src),
styleResult.ignore
? `<link href="${styleResult.src}" rel="stylesheet" type="text/css">`
: `<style>/* ${styleResult.src} */${content}</style>`
);
} else if (content) {
embedHTML = embedHTML.replace(
Expand Down Expand Up @@ -143,7 +139,7 @@ export function getExternalStyleSheets(
fetch: (input: RequestInfo, init?: RequestInit) => Promise<Response> = defaultFetch,
loadError: loadErrorHandler
): StyleResultList {
return styles.map(({ src, content }) => {
return styles.map(({ src, content, ignore }) => {
// 内联
if (content) {
return { src: "", contentPromise: Promise.resolve(content) };
Expand All @@ -152,7 +148,11 @@ export function getExternalStyleSheets(
return { src: "", contentPromise: Promise.resolve(getInlineCode(src)) };
} else {
// external styles
return { src, contentPromise: fetchAssets(src, styleCache, fetch, true, loadError) };
return {
src,
ignore,
contentPromise: ignore ? Promise.resolve("") : fetchAssets(src, styleCache, fetch, true, loadError),
};
}
});
}
Expand All @@ -165,15 +165,15 @@ export function getExternalScripts(
): ScriptResultList {
// module should be requested in iframe
return scripts.map((script) => {
const { src, async, defer, module } = script;
const { src, async, defer, module, ignore } = script;
let contentPromise = null;
// async
if ((async || defer) && src && !module) {
contentPromise = new Promise((resolve, reject) =>
requestIdleCallback(() => fetchAssets(src, scriptCache, fetch, false, loadError).then(resolve, reject))
);
// module
} else if (module && src) {
// module || ignore
} else if ((module && src) || ignore) {
contentPromise = Promise.resolve("");
// inline
} else if (!src) {
Expand All @@ -190,8 +190,10 @@ export default function importHTML(url: string, opts?: ImportEntryOpts): Promise
const fetch = opts.fetch ?? defaultFetch;
const { plugins, loadError } = opts;
const htmlLoader = plugins ? compose(plugins.map((plugin) => plugin.htmlLoader)) : defaultGetTemplate;
const jsExcludes = getExcludes("jsExcludes", plugins);
const cssExcludes = getExcludes("cssExcludes", plugins);
const jsExcludes = getEffectLoaders("jsExcludes", plugins);
const cssExcludes = getEffectLoaders("cssExcludes", plugins);
const jsIgnores = getEffectLoaders("jsIgnores", plugins);
const cssIgnores = getEffectLoaders("cssIgnores", plugins);
const getPublicPath = defaultGetPublicPath;

const getHtmlParseResult = (url, htmlLoader) =>
Expand All @@ -211,13 +213,17 @@ export default function importHTML(url: string, opts?: ImportEntryOpts): Promise
assetPublicPath,
getExternalScripts: () =>
getExternalScripts(
scripts.filter((script) => !script.src || !jsExcludes.length || !jsExcludes.includes(script.src)),
scripts
.filter((script) => !script.src || !isMatchUrl(script.src, jsExcludes))
.map((script) => ({ ...script, ignore: script.src && isMatchUrl(script.src, jsIgnores) })),
fetch,
loadError
),
getExternalStyleSheets: () =>
getExternalStyleSheets(
styles.filter((style) => !style.src || !cssExcludes.length || !cssExcludes.includes(style.src)),
styles
.filter((style) => !style.src || !isMatchUrl(style.src, cssExcludes))
.map((style) => ({ ...style, ignore: style.src && isMatchUrl(style.src, cssIgnores) })),
fetch,
loadError
),
Expand Down
4 changes: 4 additions & 0 deletions packages/wujie-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export interface plugin {
htmlLoader?: (code: string) => string;
/** js排除列表 */
jsExcludes?: Array<string | RegExp>;
/** js忽略列表 */
jsIgnores?: Array<string | RegExp>;
/** 处理js加载前的loader */
jsBeforeLoaders?: Array<ScriptObjectLoader>;
/** 处理js的loader */
Expand All @@ -38,6 +40,8 @@ export interface plugin {
jsAfterLoaders?: Array<ScriptObjectLoader>;
/** css排除列表 */
cssExcludes?: Array<string | RegExp>;
/** css忽略列表 */
cssIgnores?: Array<string | RegExp>;
/** 处理css加载前的loader */
cssBeforeLoaders?: Array<StyleObject>;
/** 处理css的loader */
Expand Down
Loading

0 comments on commit 7631e7c

Please sign in to comment.