From a7e1eb2194d87bb3f144e40b7929a12a114f6a9c Mon Sep 17 00:00:00 2001 From: Kuitos Date: Mon, 6 Nov 2023 22:25:41 +0800 Subject: [PATCH 1/7] feat(loader): add lru cache for assets fetch by default --- .../qiankun/src/core/fetchWithLruCache.ts | 8 +++ packages/qiankun/src/core/loadApp.ts | 10 ++-- packages/qiankun/src/types.ts | 2 +- packages/sandbox/src/core/sandbox/index.ts | 9 ++-- .../src/patchers/dynamicAppend/common.ts | 50 ++++++++++++------- .../dynamicAppend/forStandardSandbox.ts | 6 ++- .../src/patchers/dynamicAppend/types.ts | 3 +- packages/sandbox/src/patchers/index.ts | 20 ++++++-- 8 files changed, 73 insertions(+), 35 deletions(-) create mode 100644 packages/qiankun/src/core/fetchWithLruCache.ts diff --git a/packages/qiankun/src/core/fetchWithLruCache.ts b/packages/qiankun/src/core/fetchWithLruCache.ts new file mode 100644 index 000000000..f6fde86a9 --- /dev/null +++ b/packages/qiankun/src/core/fetchWithLruCache.ts @@ -0,0 +1,8 @@ +/** + * @author Kuitos + * @since 2023-11-06 + */ + +export const wrapFetchWithLruCache: (fetch: typeof window.fetch) => typeof window.fetch = (fetch) => { + return fetch; +}; diff --git a/packages/qiankun/src/core/loadApp.ts b/packages/qiankun/src/core/loadApp.ts index bf6c09861..036a83a6f 100644 --- a/packages/qiankun/src/core/loadApp.ts +++ b/packages/qiankun/src/core/loadApp.ts @@ -19,6 +19,7 @@ import { toArray, } from '../utils'; import { version } from '../version'; +import { wrapFetchWithLruCache } from './fetchWithLruCache'; export type ParcelConfigObjectGetter = (remountContainer: HTMLElement) => ParcelConfigObject; @@ -28,7 +29,8 @@ export default async function loadApp( lifeCycles?: LifeCycles, ): Promise { const { name: appName, entry, container } = app; - const { fetch = window.fetch, sandbox, globalContext = window, streamTransformer } = configuration || {}; + const { fetch = window.fetch, sandbox, globalContext = window, ...restConfiguration } = configuration || {}; + const fetchWithLruCache = wrapFetchWithLruCache(fetch); const markName = `[qiankun] App ${appName} Loading`; if (process.env.NODE_ENV === 'development') { @@ -47,6 +49,8 @@ export default async function loadApp( const sandboxContainer = createSandboxContainer(appName, () => sandboxMicroAppContainer, { globalContext, extraGlobals: {}, + fetch: fetchWithLruCache, + nodeTransformer: restConfiguration.nodeTransformer, }); sandboxInstance = sandboxContainer.instance; @@ -56,7 +60,7 @@ export default async function loadApp( unmountSandbox = () => sandboxContainer.unmount(); } - const containerOpts: LoaderOpts = { fetch, sandbox: sandboxInstance, streamTransformer }; + const containerOpts: LoaderOpts = { fetch: fetchWithLruCache, sandbox: sandboxInstance, ...restConfiguration }; const lifecyclesPromise = loadEntry(entry, sandboxMicroAppContainer, containerOpts); @@ -109,7 +113,7 @@ export default async function loadApp( if (mountTimes > 1) { initContainer(mountContainer, appName, sandbox); // html scripts should be removed to avoid repeatedly execute - const htmlString = await getPureHTMLStringWithoutScripts(entry, fetch); + const htmlString = await getPureHTMLStringWithoutScripts(entry, fetchWithLruCache); await loadEntry(htmlString, mountContainer, containerOpts); } }, diff --git a/packages/qiankun/src/types.ts b/packages/qiankun/src/types.ts index f023d1860..10db98761 100644 --- a/packages/qiankun/src/types.ts +++ b/packages/qiankun/src/types.ts @@ -41,7 +41,7 @@ export type RegistrableApp = LoadableApp & { activeRule: RegisterApplicationConfig['activeWhen']; }; -export type AppConfiguration = Partial> & { +export type AppConfiguration = Partial> & { sandbox?: boolean; globalContext?: WindowProxy; }; diff --git a/packages/sandbox/src/core/sandbox/index.ts b/packages/sandbox/src/core/sandbox/index.ts index 47f94d98b..525d103a0 100644 --- a/packages/sandbox/src/core/sandbox/index.ts +++ b/packages/sandbox/src/core/sandbox/index.ts @@ -3,6 +3,7 @@ * @since 2019-04-11 */ import { patchAtBootstrapping, patchAtMounting } from '../../patchers'; +import type { SandboxConfig } from '../../patchers/dynamicAppend/types'; import type { Free, Rebuild } from '../../patchers/types'; import type { Endowments } from '../membrane'; import { StandardSandbox } from './StandardSandbox'; @@ -32,9 +33,9 @@ export function createSandboxContainer( opts: { globalContext?: WindowProxy; extraGlobals?: Endowments; - }, + } & Pick, ) { - const { globalContext, extraGlobals = {} } = opts; + const { globalContext, extraGlobals = {}, ...sandboxCfg } = opts; let sandbox: Sandbox; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (window.Proxy) { @@ -45,7 +46,7 @@ export function createSandboxContainer( } // some side effect could be invoked while bootstrapping, such as dynamic stylesheet injection with style-loader, especially during the development phase - const bootstrappingFrees = patchAtBootstrapping(appName, getContainer, sandbox); + const bootstrappingFrees = patchAtBootstrapping(appName, getContainer, { sandbox, ...sandboxCfg }); // mounting frees are one-off and should be re-init at every mounting time let mountingFrees: Free[] = []; @@ -77,7 +78,7 @@ export function createSandboxContainer( /* ------------------------------------------ 2. 开启全局变量补丁 ------------------------------------------*/ // render 沙箱启动时开始劫持各类全局监听,尽量不要在应用初始化阶段有 事件监听/定时器 等副作用 - mountingFrees = patchAtMounting(appName, getContainer, sandbox); + mountingFrees = patchAtMounting(appName, getContainer, { sandbox, ...sandboxCfg }); /* ------------------------------------------ 3. 重置一些初始化时的副作用 ------------------------------------------*/ // 存在 rebuilds 则表明有些副作用需要重建 diff --git a/packages/sandbox/src/patchers/dynamicAppend/common.ts b/packages/sandbox/src/patchers/dynamicAppend/common.ts index bd0328bdc..e5cecadeb 100644 --- a/packages/sandbox/src/patchers/dynamicAppend/common.ts +++ b/packages/sandbox/src/patchers/dynamicAppend/common.ts @@ -4,7 +4,7 @@ * @since 2019-10-21 */ import type { ScriptTranspilerOpts } from '@qiankunjs/shared'; -import { Deferred, transpileAssets, waitUntilSettled } from '@qiankunjs/shared'; +import { Deferred, waitUntilSettled } from '@qiankunjs/shared'; import { qiankunHeadTagName } from '../../consts'; import type { SandboxConfig } from './types'; @@ -134,9 +134,10 @@ export function getOverwrittenAppendChildOrInsertBefore( const appendChild = nativeFn; const element = newChild as unknown as HTMLElement; - const containerConfig = getSandboxConfig(element); + const sandboxConfig = getSandboxConfig(element); - if (!isHijackingTag(element.tagName) || !containerConfig) { + // no attached sandbox config means the element is not created from the sandbox environment + if (!isHijackingTag(element.tagName) || !sandboxConfig) { return appendChild.call(this, element, refChild) as T; } @@ -157,22 +158,31 @@ export function getOverwrittenAppendChildOrInsertBefore( refNo = Array.from(this.childNodes).indexOf(referenceNode as ChildNode); } - const result = appendChild.call(this, stylesheetElement, referenceNode); + const { sandbox, nodeTransformer, fetch } = sandboxConfig; + const transpiledStyleSheetElement = nodeTransformer + ? nodeTransformer(stylesheetElement, location.href, { + fetch, + sandbox, + rawNode: stylesheetElement, + }) + : stylesheetElement; + + const result = appendChild.call(this, transpiledStyleSheetElement, referenceNode); // record refNo thus we can keep order while remounting if (typeof refNo === 'number' && refNo !== -1) { - defineNonEnumerableProperty(stylesheetElement, styleElementRefNodeNo, refNo); + defineNonEnumerableProperty(transpiledStyleSheetElement, styleElementRefNodeNo, refNo); } - const { dynamicStyleSheetElements } = containerConfig; + const { dynamicStyleSheetElements } = sandboxConfig; // record dynamic style elements after insert succeed - dynamicStyleSheetElements.push(stylesheetElement); + dynamicStyleSheetElements.push(transpiledStyleSheetElement); return result as T; } case SCRIPT_TAG_NAME: { const scriptElement = element as HTMLScriptElement; - const { sandbox, dynamicExternalSyncScriptElements } = containerConfig; + const { sandbox, dynamicExternalSyncScriptElements, nodeTransformer, fetch } = sandboxConfig; const externalSyncMode = scriptElement.hasAttribute('src') && !scriptElement.hasAttribute('async'); @@ -190,26 +200,28 @@ export function getOverwrittenAppendChildOrInsertBefore( scriptTranspiledDeferred = new Deferred(); } - const transpiledScriptElement = transpileAssets(scriptElement, location.href, { - fetch, - sandbox, - rawNode: scriptElement, - prevScriptTranspiledDeferred, - scriptTranspiledDeferred, - } as ScriptTranspilerOpts); + const transpiledScriptElement = nodeTransformer + ? nodeTransformer(scriptElement, location.href, { + fetch, + sandbox, + rawNode: scriptElement, + prevScriptTranspiledDeferred, + scriptTranspiledDeferred, + } as ScriptTranspilerOpts) + : scriptElement; const result = appendChild.call(this, transpiledScriptElement, refChild) as T; // Previously it was an external synchronous script, and after the transpile, there was no src attribute, indicating that the script needs to wait for the src to be filled if (externalSyncMode && !transpiledScriptElement.hasAttribute('src')) { - dynamicExternalSyncScriptElements.push(scriptElement); - scriptFetchedDeferredWeakMap.set(scriptElement, scriptTranspiledDeferred!); + dynamicExternalSyncScriptElements.push(transpiledScriptElement); + scriptFetchedDeferredWeakMap.set(transpiledScriptElement, scriptTranspiledDeferred!); // clear the memory regardless the script loaded or failed void waitUntilSettled(scriptTranspiledDeferred!.promise).then(() => { - const scriptIndex = dynamicExternalSyncScriptElements.indexOf(scriptElement); + const scriptIndex = dynamicExternalSyncScriptElements.indexOf(transpiledScriptElement); dynamicExternalSyncScriptElements.splice(scriptIndex, 1); - scriptFetchedDeferredWeakMap.delete(scriptElement); + scriptFetchedDeferredWeakMap.delete(transpiledScriptElement); }); } diff --git a/packages/sandbox/src/patchers/dynamicAppend/forStandardSandbox.ts b/packages/sandbox/src/patchers/dynamicAppend/forStandardSandbox.ts index ff260d027..e148bb3b3 100644 --- a/packages/sandbox/src/patchers/dynamicAppend/forStandardSandbox.ts +++ b/packages/sandbox/src/patchers/dynamicAppend/forStandardSandbox.ts @@ -314,14 +314,16 @@ export function patchStandardSandbox( opts: { sandbox: Sandbox; mounting?: boolean; - }, + } & Pick, ): Free { - const { sandbox, mounting = true } = opts; + const { sandbox, mounting = true, nodeTransformer, fetch } = opts; let sandboxConfig = sandboxConfigWeakMap.get(sandbox); if (!sandboxConfig) { sandboxConfig = { appName, sandbox, + fetch, + nodeTransformer, dynamicStyleSheetElements: [], dynamicExternalSyncScriptElements: [], }; diff --git a/packages/sandbox/src/patchers/dynamicAppend/types.ts b/packages/sandbox/src/patchers/dynamicAppend/types.ts index 25ff9fb12..09e67cb2e 100644 --- a/packages/sandbox/src/patchers/dynamicAppend/types.ts +++ b/packages/sandbox/src/patchers/dynamicAppend/types.ts @@ -2,6 +2,7 @@ * @author Kuitos * @since 2023-05-04 */ +import type { BaseLoaderOpts, transpileAssets } from '@qiankunjs/shared'; import type { Sandbox } from '../../core/sandbox'; export type SandboxConfig = { @@ -9,4 +10,4 @@ export type SandboxConfig = { sandbox: Sandbox; dynamicStyleSheetElements: Array; dynamicExternalSyncScriptElements: HTMLScriptElement[]; -}; +} & BaseLoaderOpts & { nodeTransformer?: typeof transpileAssets }; diff --git a/packages/sandbox/src/patchers/index.ts b/packages/sandbox/src/patchers/index.ts index 7138150d8..7e14fae3f 100644 --- a/packages/sandbox/src/patchers/index.ts +++ b/packages/sandbox/src/patchers/index.ts @@ -3,24 +3,34 @@ * @since 2019-04-11 */ -import type { Sandbox } from '../core/sandbox'; import { SandboxType } from '../core/sandbox/types'; import { patchStandardSandbox } from './dynamicAppend'; +import type { SandboxConfig } from './dynamicAppend/types'; import patchHistoryListener from './historyListener'; import patchInterval from './interval'; import type { Free } from './types'; import patchWindowListener from './windowListener'; -export function patchAtBootstrapping(appName: string, getContainer: () => HTMLElement, sandbox: Sandbox): Free[] { +export function patchAtBootstrapping( + appName: string, + getContainer: () => HTMLElement, + opts: Pick, +): Free[] { const patchersInSandbox = { - [SandboxType.Standard]: [() => patchStandardSandbox(appName, getContainer, { sandbox, mounting: false })], + [SandboxType.Standard]: [() => patchStandardSandbox(appName, getContainer, { mounting: false, ...opts })], [SandboxType.Snapshot]: [], } as const; + const { sandbox } = opts; return patchersInSandbox[sandbox.type].map((patch) => patch()); } -export function patchAtMounting(appName: string, getContainer: () => HTMLElement, sandbox: Sandbox): Free[] { +export function patchAtMounting( + appName: string, + getContainer: () => HTMLElement, + opts: Pick, +): Free[] { + const { sandbox } = opts; const basePatchers = [ () => patchInterval(sandbox.globalThis), () => patchWindowListener(sandbox.globalThis), @@ -30,7 +40,7 @@ export function patchAtMounting(appName: string, getContainer: () => HTMLElement const patchersInSandbox = { [SandboxType.Standard]: [ ...basePatchers, - () => patchStandardSandbox(appName, getContainer, { sandbox, mounting: true }), + () => patchStandardSandbox(appName, getContainer, { mounting: true, ...opts }), ], [SandboxType.Snapshot]: basePatchers, }; From 8d1b3fd9acc5aadbe0c235aba9e854e822e8f01a Mon Sep 17 00:00:00 2001 From: Kuitos Date: Mon, 6 Nov 2023 22:27:24 +0800 Subject: [PATCH 2/7] Create itchy-snakes-tell.md --- .changeset/itchy-snakes-tell.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/itchy-snakes-tell.md diff --git a/.changeset/itchy-snakes-tell.md b/.changeset/itchy-snakes-tell.md new file mode 100644 index 000000000..4289c24b7 --- /dev/null +++ b/.changeset/itchy-snakes-tell.md @@ -0,0 +1,6 @@ +--- +"qiankun": patch +"@qiankunjs/sandbox": patch +--- + +feat(loader): add lru cache for assets fetch by default From ffed4f9ae5422aa1ad97dde63771146676936a86 Mon Sep 17 00:00:00 2001 From: Kuitos Date: Mon, 6 Nov 2023 23:41:32 +0800 Subject: [PATCH 3/7] feat(loader): add lru cache for assets fetch by default --- packages/qiankun/package.json | 1 + .../qiankun/src/core/fetchWithLruCache.ts | 8 ---- packages/qiankun/src/core/loadApp.ts | 2 +- packages/qiankun/src/core/wrapFetch.ts | 38 +++++++++++++++++++ pnpm-lock.yaml | 8 ++++ 5 files changed, 48 insertions(+), 9 deletions(-) delete mode 100644 packages/qiankun/src/core/fetchWithLruCache.ts create mode 100644 packages/qiankun/src/core/wrapFetch.ts diff --git a/packages/qiankun/package.json b/packages/qiankun/package.json index 7e3c6c652..2e5e86748 100644 --- a/packages/qiankun/package.json +++ b/packages/qiankun/package.json @@ -43,6 +43,7 @@ "@qiankunjs/sandbox": "workspace:^", "@qiankunjs/shared": "workspace:^", "lodash": "^4.17.11", + "lru-cache": "^10.0.1", "single-spa": "^6.0.0-beta.3" }, "devDependencies": { diff --git a/packages/qiankun/src/core/fetchWithLruCache.ts b/packages/qiankun/src/core/fetchWithLruCache.ts deleted file mode 100644 index f6fde86a9..000000000 --- a/packages/qiankun/src/core/fetchWithLruCache.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @author Kuitos - * @since 2023-11-06 - */ - -export const wrapFetchWithLruCache: (fetch: typeof window.fetch) => typeof window.fetch = (fetch) => { - return fetch; -}; diff --git a/packages/qiankun/src/core/loadApp.ts b/packages/qiankun/src/core/loadApp.ts index 036a83a6f..8f77057a3 100644 --- a/packages/qiankun/src/core/loadApp.ts +++ b/packages/qiankun/src/core/loadApp.ts @@ -19,7 +19,7 @@ import { toArray, } from '../utils'; import { version } from '../version'; -import { wrapFetchWithLruCache } from './fetchWithLruCache'; +import { wrapFetchWithLruCache } from './wrapFetch'; export type ParcelConfigObjectGetter = (remountContainer: HTMLElement) => ParcelConfigObject; diff --git a/packages/qiankun/src/core/wrapFetch.ts b/packages/qiankun/src/core/wrapFetch.ts new file mode 100644 index 000000000..8ecc6e1ed --- /dev/null +++ b/packages/qiankun/src/core/wrapFetch.ts @@ -0,0 +1,38 @@ +/** + * @author Kuitos + * @since 2023-11-06 + */ +import { LRUCache } from 'lru-cache'; + +type Fetch = typeof window.fetch; + +const getCacheKey = (input: Parameters[0]): string => { + return typeof input === 'string' ? input : 'url' in input ? input.url : input.href; +}; + +export const wrapFetchWithLruCache: (fetch: Fetch) => Fetch = (fetch) => { + const lruCache = new LRUCache>({ + max: 50, + ttl: 60 * 60, + }); + + const cachedFetch: Fetch = async (input, init) => { + const fetchInput = input as Parameters[0]; + const cacheKey = getCacheKey(fetchInput); + + const cachedPromise = lruCache.get(cacheKey); + if (cachedPromise) { + const res = await cachedPromise; + return res.clone(); + } + + const promise = fetch(fetchInput, init).catch((e) => { + lruCache.delete(cacheKey); + throw e; + }); + lruCache.set(cacheKey, promise); + return promise; + }; + + return cachedFetch; +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 456d9af68..e95550ec7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -99,6 +99,9 @@ importers: lodash: specifier: ^4.17.11 version: 4.17.11 + lru-cache: + specifier: ^10.0.1 + version: 10.0.1 single-spa: specifier: ^6.0.0-beta.3 version: 6.0.0-beta.3 @@ -9273,6 +9276,11 @@ packages: engines: {node: '>=8'} dev: true + /lru-cache@10.0.1: + resolution: {integrity: sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==} + engines: {node: 14 || >=16.14} + dev: false + /lru-cache@4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} dependencies: From 6d64206689cd2c4e0c085b32a9c52b00cf67167b Mon Sep 17 00:00:00 2001 From: Kuitos Date: Tue, 7 Nov 2023 12:23:39 +0800 Subject: [PATCH 4/7] chore: optimize code --- packages/qiankun/src/core/wrapFetch.ts | 41 +++++++++++++++++--------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/packages/qiankun/src/core/wrapFetch.ts b/packages/qiankun/src/core/wrapFetch.ts index 8ecc6e1ed..1edbd29a9 100644 --- a/packages/qiankun/src/core/wrapFetch.ts +++ b/packages/qiankun/src/core/wrapFetch.ts @@ -2,6 +2,7 @@ * @author Kuitos * @since 2023-11-06 */ +import { once } from 'lodash'; import { LRUCache } from 'lru-cache'; type Fetch = typeof window.fetch; @@ -10,28 +11,40 @@ const getCacheKey = (input: Parameters[0]): string => { return typeof input === 'string' ? input : 'url' in input ? input.url : input.href; }; -export const wrapFetchWithLruCache: (fetch: Fetch) => Fetch = (fetch) => { - const lruCache = new LRUCache>({ +const getGlobalCache = once(() => { + return new LRUCache>({ max: 50, - ttl: 60 * 60, + // 10 minutes + ttl: 10 * 60 * 1000, }); +}); + +export const wrapFetchWithLruCache: (fetch: Fetch) => Fetch = (fetch) => { + const lruCache = getGlobalCache(); - const cachedFetch: Fetch = async (input, init) => { + const cachedFetch: Fetch = (input, init) => { const fetchInput = input as Parameters[0]; const cacheKey = getCacheKey(fetchInput); + const wrapFetchPromise = async (promise: Promise): Promise => { + try { + const res = await promise; + // must clone the response as one response body can only be read once as a stream + return res.clone(); + } catch (e) { + lruCache.delete(cacheKey); + throw e; + } + }; - const cachedPromise = lruCache.get(cacheKey); - if (cachedPromise) { - const res = await cachedPromise; - return res.clone(); + const cachedFetchPromise = lruCache.get(cacheKey); + if (cachedFetchPromise) { + return wrapFetchPromise(cachedFetchPromise); } - const promise = fetch(fetchInput, init).catch((e) => { - lruCache.delete(cacheKey); - throw e; - }); - lruCache.set(cacheKey, promise); - return promise; + const fetchPromise = fetch(fetchInput, init); + lruCache.set(cacheKey, fetchPromise); + + return wrapFetchPromise(fetchPromise); }; return cachedFetch; From a94a2e0a1bad4d6955044498beff5636d22b37ff Mon Sep 17 00:00:00 2001 From: Kuitos Date: Tue, 7 Nov 2023 18:03:48 +0800 Subject: [PATCH 5/7] chore: add unit test --- .github/workflows/ci.yml | 43 ++- package.json | 8 +- packages/loader/package.json | 1 - packages/qiankun/package.json | 8 +- .../src/core/__tests__/wrapFetch.test.ts | 73 ++++ packages/qiankun/src/core/wrapFetch.ts | 10 + packages/sandbox/package.json | 3 +- packages/sandbox/src/__test__/index.test.ts | 7 - .../src/patchers/__tests__/css.test.ts | 364 ------------------ .../src/patchers/__tests__/interval.test.ts | 28 -- .../dynamicAppend/__tests__/common.test.ts | 60 --- .../__tests__/forStrictSandbox.test.ts | 55 --- packages/shared/package.json | 2 +- .../__tests__/{index.ts => index.test.ts} | 6 +- packages/ui-bindings/react/package.json | 3 +- packages/webpack-plugin/package.json | 2 +- packages/webpack-plugin/plugin.test.ts | 1 + pnpm-lock.yaml | 56 ++- vitest.config.ts | 4 +- vitest.workspace.ts | 2 +- 20 files changed, 195 insertions(+), 541 deletions(-) create mode 100644 packages/qiankun/src/core/__tests__/wrapFetch.test.ts delete mode 100644 packages/sandbox/src/__test__/index.test.ts delete mode 100644 packages/sandbox/src/patchers/__tests__/css.test.ts delete mode 100644 packages/sandbox/src/patchers/__tests__/interval.test.ts delete mode 100644 packages/sandbox/src/patchers/dynamicAppend/__tests__/common.test.ts delete mode 100644 packages/sandbox/src/patchers/dynamicAppend/__tests__/forStrictSandbox.test.ts rename packages/shared/src/module-resolver/__tests__/{index.ts => index.test.ts} (94%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cbb7fcbcb..716acb516 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ on: - 1.x jobs: - check: + build-check-and-lint: runs-on: ubuntu-latest strategy: @@ -29,6 +29,43 @@ jobs: cache: "pnpm" - run: corepack enable # https://nodejs.org/api/corepack.html + - run: pnpm install - - run: pnpm run ci - # - run: pnpm run docs:build #执行报错,暂时关闭 + + - name: TS Build Check + run: pnpm run build + + - name: Run eslint + run: pnpm run eslint + + - name: Run prettier + run: pnpm run prettier:check + + - name: Doc Build check + run: pnpm run docs:build + + unit-test: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [ lts/*, latest ] + + steps: + - uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v2.4.0 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + cache: "pnpm" + + - run: corepack enable # https://nodejs.org/api/corepack.html + + - run: pnpm install + + - name: Run unit test + run: pnpm run test diff --git a/package.json b/package.json index 6ea8ada81..da32bd186 100644 --- a/package.json +++ b/package.json @@ -11,18 +11,19 @@ "build": "pnpm -r run build", "prerelease:alpha": "changeset pre enter alpha && changeset && changeset version", "release:alpha": "pnpm run build && changeset publish && changeset pre exit", - "lint": "eslint packages/", + "eslint": "eslint packages/", "prettier": "prettier --write .", "prettier:check": "prettier -c .", "docs:dev": "dumi dev", "docs:build": "dumi build", - "ci": "pnpm run build && pnpm run lint && pnpm run prettier:check", - "ci:publish": "pnpm run build && changeset publish", + "ci": "pnpm run build && pnpm run eslint && pnpm run prettier:check", + "ci:publish": "changeset publish", "test": "pnpm -r run test", "prepare": "husky install && dumi setup" }, "devDependencies": { "@changesets/cli": "^2.26.2", + "@edge-runtime/vm": "^3.1.7", "@types/lodash": "^4.14.200", "@types/node": "^18.18.8", "@typescript-eslint/eslint-plugin": "^6.9.1", @@ -34,6 +35,7 @@ "eslint-config-prettier": "^9.0.0", "eslint-formatter-pretty": "^5.0.0", "father": "^4.3.6", + "happy-dom": "^12.10.3", "husky": "^8.0.3", "lint-staged": "^9.5.0", "prettier": "^3.0.3", diff --git a/packages/loader/package.json b/packages/loader/package.json index c7fc6491c..c9745a0af 100644 --- a/packages/loader/package.json +++ b/packages/loader/package.json @@ -8,7 +8,6 @@ "sideEffects": false, "scripts": { "build": "father build", - "test": "cross-env NODE_ENV=test vitest", "bench": "npm run build && tachometer ./benchmarks/parser/tern/import-html-entry.html ./benchmarks/parser/tern/parser.html --timeout=1" }, "author": "Kuitos", diff --git a/packages/qiankun/package.json b/packages/qiankun/package.json index 2e5e86748..934b477f5 100644 --- a/packages/qiankun/package.json +++ b/packages/qiankun/package.json @@ -16,13 +16,7 @@ "sideEffects": false, "scripts": { "build": "father build", - "lint": "yarn lint:js && yarn lint:prettier", - "lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src", - "lint:fix": "yarn lint:js -- --fix", - "lint:prettier": "prettier -c ./src/**/*", - "prettier": "prettier --write ./src/**/*", - "ci": "yarn lint && yarn build && yarn test", - "test": "cross-env NODE_ENV=test vitest" + "test": "vitest --run" }, "repository": { "type": "git", diff --git a/packages/qiankun/src/core/__tests__/wrapFetch.test.ts b/packages/qiankun/src/core/__tests__/wrapFetch.test.ts new file mode 100644 index 000000000..fd42e0a38 --- /dev/null +++ b/packages/qiankun/src/core/__tests__/wrapFetch.test.ts @@ -0,0 +1,73 @@ +// @vitest-environment edge-runtime + +import { describe, expect, it, vi } from 'vitest'; +import { wrapFetchWithLruCache } from '../wrapFetch'; + +const slogan = 'Hello Qiankun 3.0'; + +describe('wrapFetchWithLruCache', () => { + it('should just call fetch once while multiple request invoked parallel', () => { + const fetch = vi.fn(() => { + return Promise.resolve(new Response(slogan, { status: 200, statusText: 'OK' })); + }); + const wrappedFetch = wrapFetchWithLruCache(fetch); + const url = 'https://success.qiankun.org'; + wrappedFetch(url); + wrappedFetch(url); + wrappedFetch(url); + + expect(fetch).toHaveBeenCalledOnce(); + }); + + it('should support read response body as a stream multi times', async () => { + const fetch = vi.fn(() => { + return Promise.resolve(new Response(slogan, { status: 200, statusText: 'OK' })); + }); + const wrappedFetch = wrapFetchWithLruCache(fetch); + + const url = 'https://stream.qiankun.org'; + const response1 = await wrappedFetch(url); + const bodyStream1 = response1.body!; + expect(bodyStream1.locked).toBe(false); + const reader = bodyStream1.getReader(); + const { done, value } = await reader.read(); + expect(done).toBe(false); + expect(value).toStrictEqual(new TextEncoder().encode('Hello Qiankun 3.0')); + expect(bodyStream1.locked).toBe(true); + + const response2 = await wrappedFetch(url); + const bodyStream2 = response2.body!; + expect(bodyStream2.locked).toBe(false); + }); + + it('should clear cache while respond error with invalid status code', async () => { + const fetch = vi.fn(() => { + return Promise.resolve(new Response(slogan, { status: 400 })); + }); + const wrappedFetch = wrapFetchWithLruCache(fetch); + const url = 'https://errorStatusCode.qiankun.org'; + + const response1 = await wrappedFetch(url); + const result1 = await response1.text(); + expect(result1).toBe(slogan); + + const response2 = await wrappedFetch(url); + const result2 = await response2.text(); + expect(result2).toBe(slogan); + + expect(fetch).toHaveBeenCalledTimes(2); + }); + + it('should clear cache while respond error', async () => { + const fetch = vi.fn(() => { + return Promise.reject(new Error('error')); + }); + const wrappedFetch = wrapFetchWithLruCache(fetch); + + const url = 'https://error.qiankun.org'; + await expect(wrappedFetch(url)).rejects.toThrow('error'); + await expect(wrappedFetch(url)).rejects.toThrow('error'); + + expect(fetch).toHaveBeenCalledTimes(2); + }); +}); diff --git a/packages/qiankun/src/core/wrapFetch.ts b/packages/qiankun/src/core/wrapFetch.ts index 1edbd29a9..0f702ae91 100644 --- a/packages/qiankun/src/core/wrapFetch.ts +++ b/packages/qiankun/src/core/wrapFetch.ts @@ -19,6 +19,10 @@ const getGlobalCache = once(() => { }); }); +const isValidaResponse = (status: number): boolean => { + return status >= 200 && status < 400; +}; + export const wrapFetchWithLruCache: (fetch: Fetch) => Fetch = (fetch) => { const lruCache = getGlobalCache(); @@ -28,6 +32,12 @@ export const wrapFetchWithLruCache: (fetch: Fetch) => Fetch = (fetch) => { const wrapFetchPromise = async (promise: Promise): Promise => { try { const res = await promise; + + const { status } = res; + if (!isValidaResponse(status)) { + lruCache.delete(cacheKey); + } + // must clone the response as one response body can only be read once as a stream return res.clone(); } catch (e) { diff --git a/packages/sandbox/package.json b/packages/sandbox/package.json index f692e8313..baaa0469d 100644 --- a/packages/sandbox/package.json +++ b/packages/sandbox/package.json @@ -7,8 +7,7 @@ "types": "./dist/esm/index.d.ts", "sideEffects": false, "scripts": { - "build": "father build", - "test": "cross-env NODE_ENV=test vitest --config ../../vitest.config.ts" + "build": "father build" }, "files": [ "dist" diff --git a/packages/sandbox/src/__test__/index.test.ts b/packages/sandbox/src/__test__/index.test.ts deleted file mode 100644 index 56a488102..000000000 --- a/packages/sandbox/src/__test__/index.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -// import { greet } from '../index'; -// -// test('should greet with name', () => { -// const name = 'qiankun3'; -// -// expect(greet(name)).toBe('Hello qiankun3'); -// }); diff --git a/packages/sandbox/src/patchers/__tests__/css.test.ts b/packages/sandbox/src/patchers/__tests__/css.test.ts deleted file mode 100644 index 670513ae9..000000000 --- a/packages/sandbox/src/patchers/__tests__/css.test.ts +++ /dev/null @@ -1,364 +0,0 @@ -/** - * @author Saviio - * @since 2020-04-19 - */ - -import { sleep } from '../../utils'; -import { ScopedCSS } from '../css'; - -let CSSProcessor: ScopedCSS; -beforeAll(() => { - CSSProcessor = new ScopedCSS(); -}); - -afterAll(() => { - // @ts-ignore - CSSProcessor = null; -}); - -const fakeStyleNode = (css: string) => { - const styleNode = document.createElement('style'); - const textNode = document.createTextNode(css); - - styleNode.appendChild(textNode); - document.body.append(styleNode); - - return styleNode; -}; - -const removeWs = (s: string | null) => { - if (s == null) { - return s; - } - - const re = /\s/g; - return s.replace(re, ''); -}; - -test('should add attribute selector correctly', () => { - const actualValue = '.react15-main {display: flex; flex-direction: column; align-items: center;}'; - const expectValue = - 'div[data-qiankun="react15"] .react15-main {display: flex; flex-direction: column; align-items: center;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should add attribute selector correctly [2]', async () => { - const actualValue = '.react15-main {display: flex; flex-direction: column; align-items: center;}'; - const expectValue = - 'div[data-qiankun="react15"] .react15-main {display: flex; flex-direction: column; align-items: center;}'; - - const styleNode = fakeStyleNode(''); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - const textNode = document.createTextNode(actualValue); - styleNode.appendChild(textNode); - - await sleep(10); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should replace html correctly', () => { - const actualValue = 'html {font-size: 14px;}'; - const expectValue = 'div[data-qiankun="react15"] {font-size: 14px;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should replace body correctly', () => { - const actualValue = 'body {font-size: 14px;}'; - const expectValue = 'div[data-qiankun="react15"] {font-size: 14px;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should replace :root correctly [1]', () => { - const actualValue = ':root {--gray: #eee}'; - const expectValue = 'div[data-qiankun="react15"] {--gray: #eee;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should replace :root correctly [2]', () => { - const actualValue = `svg:not(:root) {overflow: hidden;}`; - const expectValue = `svg:not(div[data-qiankun="react15"]){overflow:hidden;}`; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should rewrite root-level correctly [1]', () => { - const actualValue = 'html + div {font-size: 14px;}'; - const expectValue = 'div[data-qiankun="react15"] + div {font-size: 14px;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should rewrite root-level correctly [2]', () => { - const actualValue = 'body + div {font-size: 14px;}'; - const expectValue = 'div[data-qiankun="react15"] + div {font-size: 14px;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should rewrite root-level correctly [3]', () => { - const actualValue = `[type="reset"], -[type="submit"], -button, -html [type="button"] { - -webkit-appearance: button; -}`; - const expectValue = `div[data-qiankun="react15"] [type="reset"], -div[data-qiankun="react15"] [type="submit"], -div[data-qiankun="react15"] button, -div[data-qiankun="react15"] [type="button"] {-webkit-appearance: button;}`; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should not replace body/html if body is part of class [1]', () => { - const actualValue = '.ant-card-body {color: #eee}'; - const expectValue = 'div[data-qiankun="react15"] .ant-card-body {color: #eee;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should not replace body/html if body is part of class [2]', () => { - const actualValue = '.ant-card_body {color: #eee}'; - const expectValue = 'div[data-qiankun="react15"] .ant-card_body {color: #eee;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should not replace body/html if body is part of class [3]', () => { - const actualValue = '.body {color: #eee}'; - const expectValue = 'div[data-qiankun="react15"] .body {color: #eee;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should not replace body/html if body is part of id [1]', () => { - const actualValue = '#body {color: #eee}'; - const expectValue = 'div[data-qiankun="react15"] #body {color: #eee;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should not replace body/html if body is part of id [2]', () => { - const actualValue = '#ant-card-body {color: #eee}'; - const expectValue = 'div[data-qiankun="react15"] #ant-card-body {color: #eee;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should not replace body/html if body is part of id [3]', () => { - const actualValue = '#ant-card__body {color: #eee}'; - const expectValue = 'div[data-qiankun="react15"] #ant-card__body {color: #eee;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should handle root-level descendant selector [1]', () => { - const actualValue = 'html > body {color: #eee;}'; - const expectValue = 'div[data-qiankun="react15"] {color: #eee;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should handle root-level descendant selector [2]', () => { - const actualValue = 'html body {color: #eee;}'; - const expectValue = 'div[data-qiankun="react15"] {color: #eee;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should handle root-level grouping selector [1]', () => { - const actualValue = 'html,body {color: #eee;}'; - const expectValue = 'div[data-qiankun="react15"] {color: #eee;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should handle root-level grouping selector [2]', () => { - const actualValue = 'body,html,div {color: #eee;}'; - const expectValue = 'div[data-qiankun="react15"],div[data-qiankun="react15"]div{color:#eee;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should handle root-level grouping selector [3]', () => { - const actualValue = 'a,body,html,div {color: #eee;}'; - const expectValue = - 'div[data-qiankun="react15"]a,div[data-qiankun="react15"],div[data-qiankun="react15"]div{color:#eee;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should not remove special root-level selector when rule is non-standard [1]', () => { - const actualValue = 'html + body {color: #eee;}'; - const expectValue = 'div[data-qiankun="react15"] + div[data-qiankun="react15"] {color: #eee;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should not remove special root-level selector when rule is non-standard [1]', () => { - const actualValue = 'html ~ body {color: #eee;}'; - const expectValue = 'div[data-qiankun="react15"] ~ div[data-qiankun="react15"] {color: #eee;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should transform @supports', () => { - const actualValue = '@supports (display: grid) {div{margin: 1cm;}}'; - const expectValue = '@supports (display: grid) {div[data-qiankun="react15"] div {margin: 1cm;}}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should not transform @keyframes', () => { - const actualValue = '@keyframes move {from {top: 0px;}to {top: 200px;}}'; - const expectValue = '@keyframes move {from {top: 0px;}to {top: 200px;}}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should not transform @font-face', () => { - const actualValue = '@font-face {font-family: "Open Sans";}'; - const expectValue = '@font-face {font-family: "Open Sans";}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should not transform style that has already been transform [1]', async () => { - const actualValue = '.react15-main {display: flex;}'; - const expectValue = 'div[data-qiankun="react15"] .react15-main {display: flex;}'; - - const styleNode = fakeStyleNode(''); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - const textNode = document.createTextNode(actualValue); - styleNode.appendChild(textNode); - - await sleep(10); - - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -test('should not transform style that has already been transform [2]', async () => { - const actualValue = '.react15-main {display: flex;}'; - const expectValue = 'div[data-qiankun="react15"] .react15-main {display: flex;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -// jest cannot handle @page directive correctly -test.skip('should not transform @page', () => { - const actualValue = '@page {margin: 1cm;}'; - const expectValue = '@page {margin: 1cm;}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -// jest cannot handle @import directive correctly -test.skip('should not transform @import', () => { - const actualValue = "@import 'custom.css'"; - const expectValue = "@import 'custom.css'"; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); - -// jest cannot handle @media directive correctly -test.skip('should transform @media', () => { - const actualValue = '@media screen and (max-width: 300px) {div{margin: 1cm;}}'; - const expectValue = '@media screen and (max-width: 300px) {div[data-qiankun="react15"] div {margin: 1cm;}}'; - - const styleNode = fakeStyleNode(actualValue); - CSSProcessor.process(styleNode, 'div[data-qiankun="react15"]'); - - expect(removeWs(styleNode.textContent)).toBe(removeWs(expectValue)); -}); diff --git a/packages/sandbox/src/patchers/__tests__/interval.test.ts b/packages/sandbox/src/patchers/__tests__/interval.test.ts deleted file mode 100644 index 44b09c5a0..000000000 --- a/packages/sandbox/src/patchers/__tests__/interval.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @author Kuitos - * @since 2020-03-30 - */ - -import { sleep } from '../../utils'; -import patch from '../interval'; - -test('patch setInterval', async () => { - const free = patch(window); - - const clearedListener = vi.fn(); - const unclearedListener = vi.fn(); - const unclearedListenerWithArgs = vi.fn(); - - const interval1 = window.setInterval(clearedListener, 60); - window.setInterval(unclearedListener, 8); - window.setInterval(unclearedListenerWithArgs, 30, 'kuitos'); - - window.clearInterval(interval1); - - await sleep(10); - free(); - - expect(clearedListener).toBeCalledTimes(0); - expect(unclearedListener).toBeCalledTimes(1); - expect(unclearedListenerWithArgs).toBeCalledTimes(0); -}); diff --git a/packages/sandbox/src/patchers/dynamicAppend/__tests__/common.test.ts b/packages/sandbox/src/patchers/dynamicAppend/__tests__/common.test.ts deleted file mode 100644 index e3615ad77..000000000 --- a/packages/sandbox/src/patchers/dynamicAppend/__tests__/common.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { getStyledElementCSSRules, rebuildCSSRules, recordStyledComponentsCSSRules } from '../common'; - -vi.mock('import-html-entry', () => ({ - default: { - execScripts: vi.fn(), - }, -})); - -const cssRuleText1 = '#foo { color: red; }'; -const cssRuleText2 = 'span { font-weight: bold; }'; - -const createStyleElement = () => { - document.body.innerHTML = ''; - return document.getElementById('style-under-test') as HTMLStyleElement; -}; - -beforeEach(() => { - document.body.innerHTML = ''; -}); - -test('should record Styled-Component CSS Rules correctly', () => { - const styleElement = createStyleElement(); - const cssStyleSheet = styleElement.sheet; - cssStyleSheet?.insertRule(cssRuleText1); - cssStyleSheet?.insertRule(cssRuleText2); - - recordStyledComponentsCSSRules([styleElement]); - - const cssRules: CSSRuleList | undefined = getStyledElementCSSRules(styleElement); - expect(cssRules).toBeDefined(); - expect(cssRules?.length).toEqual(2); - expect((cssStyleSheet?.cssRules[0] as CSSStyleRule).selectorText).toEqual('span'); - expect((cssStyleSheet?.cssRules[1] as CSSStyleRule).selectorText).toEqual('#foo'); -}); - -test('should rebuild Styled-Component CSS Rules in the correct order', () => { - const styleElement = createStyleElement(); - const cssStyleSheet = styleElement.sheet; - cssStyleSheet?.insertRule(cssRuleText1); - cssStyleSheet?.insertRule(cssRuleText2); - - recordStyledComponentsCSSRules([styleElement]); - - expect((cssStyleSheet?.cssRules[0] as CSSStyleRule).selectorText).toEqual('span'); - expect((cssStyleSheet?.cssRules[1] as CSSStyleRule).selectorText).toEqual('#foo'); - - // Set sheet to be writiable so we can overwrite the value for sheet - Object.defineProperty(window.HTMLStyleElement.prototype, 'sheet', { - writable: true, - value: {}, - }); - // @ts-ignore - styleElement.sheet = new CSSStyleSheet(); - rebuildCSSRules([styleElement], () => true); - - expect(styleElement.sheet.cssRules.length).toEqual(2); - // Verify that the order of the styles is the same as the recorded ones - expect((cssStyleSheet?.cssRules[0] as CSSStyleRule).selectorText).toEqual('span'); - expect((cssStyleSheet?.cssRules[1] as CSSStyleRule).selectorText).toEqual('#foo'); -}); diff --git a/packages/sandbox/src/patchers/dynamicAppend/__tests__/forStrictSandbox.test.ts b/packages/sandbox/src/patchers/dynamicAppend/__tests__/forStrictSandbox.test.ts deleted file mode 100644 index 75b65eff3..000000000 --- a/packages/sandbox/src/patchers/dynamicAppend/__tests__/forStrictSandbox.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { SandBoxType } from '../../../../interfaces'; -import { noop } from 'lodash'; -import { patchStandardSandbox } from '../forStrictSandbox'; - -vi.mock('import-html-entry', () => ({ - default: { - execScripts: vi.fn(), - }, -})); - -describe('forStrictSandbox test', () => { - const { - prototype: { createTreeWalker: originalCreateTreeWalker }, - } = Document; - - beforeAll(() => { - Document.prototype.createTreeWalker = function createTreeWalker( - this: Document, - root: Node, - whatToShow?: number | undefined, - filter?: NodeFilter | null | undefined, - ) { - if (document !== root) { - throw new TypeError('error'); - } - return originalCreateTreeWalker.call(this, root, whatToShow, filter); - }; - }); - - afterAll(() => { - Document.prototype.createTreeWalker = originalCreateTreeWalker; - }); - - it('should not throw on patched document', () => { - let patchedDocument!: Document; - const appName = 'test-app'; - const wrapper = document.createElement('div'); - const sandbox = { - name: appName, - type: SandBoxType.Proxy, - proxy: window, - sandboxRunning: true, - latestSetProp: null, - patchDocument: (patched: Document) => { - patchedDocument = patched; - }, - active: noop, - inactive: noop, - }; - patchStandardSandbox(appName, () => wrapper, sandbox, true, false, undefined, true); - - expect(patchedDocument).toBeDefined(); - expect(() => patchedDocument?.createTreeWalker(patchedDocument)).not.toThrow(); - }); -}); diff --git a/packages/shared/package.json b/packages/shared/package.json index 8ae0e43fa..5937112d7 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -8,7 +8,7 @@ "sideEffects": false, "scripts": { "build": "father build", - "test": "vitest --environment jsdom --run", + "test": "vitest --run", "bench": "npm run build && tachometer ./benchmarks/parser/tern/import-html-entry.html ./benchmarks/parser/tern/parser.html --timeout=1" }, "author": "Kuitos", diff --git a/packages/shared/src/module-resolver/__tests__/index.ts b/packages/shared/src/module-resolver/__tests__/index.test.ts similarity index 94% rename from packages/shared/src/module-resolver/__tests__/index.ts rename to packages/shared/src/module-resolver/__tests__/index.test.ts index cfc8d10bb..7f285ecf4 100644 --- a/packages/shared/src/module-resolver/__tests__/index.ts +++ b/packages/shared/src/module-resolver/__tests__/index.test.ts @@ -1,4 +1,4 @@ -import { describe, it } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { moduleResolver } from '../index'; @@ -28,7 +28,7 @@ describe('default module resolver', () => { `; - it('should works well', ({ expect }) => { + it('should works well', () => { const microAppContainer = document.createElement('div'); microAppContainer.innerHTML = `