diff --git a/dev/app-main/src/constant.ts b/dev/app-main/src/constant.ts index 1aabd8753..87a4e0c85 100644 --- a/dev/app-main/src/constant.ts +++ b/dev/app-main/src/constant.ts @@ -27,6 +27,11 @@ export const localApps: AppInfo = [ name: 'react16', activeWhen: '/react16', entry: getProxyHost('dev/react16'), + sandbox: { + excludeAssetFilter: (url: string) => { + return url.includes('exclude'); + }, + }, }, { name: 'react18', diff --git a/dev/app-react-16/public/exclude-dynamic.js b/dev/app-react-16/public/exclude-dynamic.js new file mode 100644 index 000000000..236af5ed2 --- /dev/null +++ b/dev/app-react-16/public/exclude-dynamic.js @@ -0,0 +1 @@ +window.dynamic2 = 1; diff --git a/dev/app-react-16/public/exclude.js b/dev/app-react-16/public/exclude.js new file mode 100644 index 000000000..bb618ed09 --- /dev/null +++ b/dev/app-react-16/public/exclude.js @@ -0,0 +1 @@ +window.dynamic = 1; diff --git a/dev/app-react-16/public/index.html b/dev/app-react-16/public/index.html index 0bb206616..0966b904b 100644 --- a/dev/app-react-16/public/index.html +++ b/dev/app-react-16/public/index.html @@ -3,6 +3,12 @@ app react v17 + +
diff --git a/package.json b/package.json index 4de0a8bbc..4133f7f3f 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "workspace-tools": "0.16.2", "zx": "4.2.0" }, - "version": "1.17.6", + "version": "1.18.0", "packageManager": "pnpm@7.6.0", "engines": { "node": ">=16.14.0" diff --git a/packages/bridge-react-v18/package.json b/packages/bridge-react-v18/package.json index d9fc0c8da..7a82154e1 100644 --- a/packages/bridge-react-v18/package.json +++ b/packages/bridge-react-v18/package.json @@ -1,6 +1,6 @@ { "name": "@garfish/bridge-react-v18", - "version": "1.17.6", + "version": "1.18.0", "description": "garfish module.", "keywords": [ "garfish", diff --git a/packages/bridge-react/package.json b/packages/bridge-react/package.json index 5f5b5665d..bdd77c057 100644 --- a/packages/bridge-react/package.json +++ b/packages/bridge-react/package.json @@ -1,6 +1,6 @@ { "name": "@garfish/bridge-react", - "version": "1.17.6", + "version": "1.18.0", "description": "garfish module.", "keywords": [ "garfish", diff --git a/packages/bridge-vue-v2/package.json b/packages/bridge-vue-v2/package.json index 868f8b733..2308cf1b2 100644 --- a/packages/bridge-vue-v2/package.json +++ b/packages/bridge-vue-v2/package.json @@ -1,6 +1,6 @@ { "name": "@garfish/bridge-vue-v2", - "version": "1.17.6", + "version": "1.18.0", "description": "garfish vue bridge for v2.", "keywords": [ "garfish", diff --git a/packages/bridge-vue-v3/package.json b/packages/bridge-vue-v3/package.json index ec485895c..dbde61163 100644 --- a/packages/bridge-vue-v3/package.json +++ b/packages/bridge-vue-v3/package.json @@ -1,6 +1,6 @@ { "name": "@garfish/bridge-vue-v3", - "version": "1.17.6", + "version": "1.18.0", "description": "garfish vue bridge for v3.", "keywords": [ "garfish", diff --git a/packages/browser-snapshot/package.json b/packages/browser-snapshot/package.json index e47d7261f..f8e5ac3f8 100644 --- a/packages/browser-snapshot/package.json +++ b/packages/browser-snapshot/package.json @@ -1,6 +1,6 @@ { "name": "@garfish/browser-snapshot", - "version": "1.17.6", + "version": "1.18.0", "description": "browser-snapshot module.", "keywords": [ "garfish", diff --git a/packages/browser-vm/package.json b/packages/browser-vm/package.json index 6eba56c0e..992c155fc 100644 --- a/packages/browser-vm/package.json +++ b/packages/browser-vm/package.json @@ -1,6 +1,6 @@ { "name": "@garfish/browser-vm", - "version": "1.17.6", + "version": "1.18.0", "description": "vm-sandbox module.", "keywords": [ "garfish", diff --git a/packages/browser-vm/src/dynamicNode/processor.ts b/packages/browser-vm/src/dynamicNode/processor.ts index 9efee5c16..a8d77636f 100644 --- a/packages/browser-vm/src/dynamicNode/processor.ts +++ b/packages/browser-vm/src/dynamicNode/processor.ts @@ -52,8 +52,16 @@ export class DynamicNodeProcessor { const src = el.getAttribute('src'); const href = el.getAttribute('href'); if (this.sandbox.options.fixStaticResourceBaseUrl) { - src && (el.src = transformUrl(baseUrl, src)); - href && (el.href = transformUrl(baseUrl, href)); + const transformUrlSrc = src && transformUrl(baseUrl, src); + const transformUrlHref = href && transformUrl(baseUrl, href); + // Consistent values do not need to be overwritten + if (transformUrlSrc !== src) { + el.src = transformUrlSrc; + } + + if (transformUrlHref !== href) { + el.href = transformUrlHref; + } } const url = el.src || el.href; @@ -150,7 +158,11 @@ export class DynamicNodeProcessor { const isModule = type === 'module'; const code = this.el.textContent || this.el.text || ''; - if (!type || isJsType({ src, type })) { + // Returning true indicates that execution in the sandbox is not required + const excludeAssetFilter = this.sandbox.options.excludeAssetFilter; + const excludeUrl = excludeAssetFilter?.(src); + + if ((!type || isJsType({ src, type })) && !excludeUrl) { // The "src" higher priority const { baseUrl, namespace } = this.sandbox.options; if (src) { diff --git a/packages/browser-vm/src/pluginify.ts b/packages/browser-vm/src/pluginify.ts index debb6fbc0..7b463ab1a 100644 --- a/packages/browser-vm/src/pluginify.ts +++ b/packages/browser-vm/src/pluginify.ts @@ -22,6 +22,7 @@ declare module '@garfish/core' { } export interface AppInfo { + excludeAssetFilter?: (url: string) => boolean; protectVariable?: PropertyKey[]; insulationVariable?: PropertyKey[]; } @@ -137,6 +138,7 @@ function createOptions(Garfish: interfaces.Garfish) { appInfo.sandbox?.disableLinkTransformToStyle, ), strictIsolation: Boolean(appInfo.sandbox?.strictIsolation), + excludeAssetFilter: appInfo.sandbox?.excludeAssetFilter, // 缓存模式,不收集副作用 disableCollect: appInfo.cache === undefined ? true : Boolean(appInfo.cache), diff --git a/packages/browser-vm/src/types.ts b/packages/browser-vm/src/types.ts index 24dd254aa..9ba67a8e7 100644 --- a/packages/browser-vm/src/types.ts +++ b/packages/browser-vm/src/types.ts @@ -30,6 +30,7 @@ export interface SandboxOptions { disableLinkTransformToStyle?: boolean; disableCollect?: boolean; modules?: Array; + excludeAssetFilter?: (url: string) => boolean; addSourceList?: ( sourceInfo: | Array<{ tagName: string; url: string | URL | Request }> diff --git a/packages/core/package.json b/packages/core/package.json index 8c449bba8..c749556cd 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@garfish/core", - "version": "1.17.6", + "version": "1.18.0", "description": "core module.", "keywords": [ "garfish", diff --git a/packages/core/src/interface.ts b/packages/core/src/interface.ts index 423580d4b..01e2f336d 100644 --- a/packages/core/src/interface.ts +++ b/packages/core/src/interface.ts @@ -83,6 +83,7 @@ export namespace interfaces { disableElementtiming?: boolean; disableLinkTransformToStyle?: boolean; fixOwnerDocument?: boolean; + excludeAssetFilter?: (url: string) => boolean; } export interface Config { diff --git a/packages/core/src/module/app.ts b/packages/core/src/module/app.ts index 58f6b8075..46da21646 100644 --- a/packages/core/src/module/app.ts +++ b/packages/core/src/module/app.ts @@ -689,6 +689,19 @@ export class App { return DOMApis.createElement(node); } } + + // Filter out code that does not require sandboxed execution + if (this.appInfo.sandbox) { + const scriptUrl = entryManager.findAttributeValue(node, 'src'); + if ( + scriptUrl && + this.appInfo.sandbox?.excludeAssetFilter && + this.appInfo.sandbox?.excludeAssetFilter(scriptUrl) + ) { + return DOMApis.createElement(node); + } + } + const jsManager = resources.js.find((manager) => { return !manager.async ? manager.isSameOrigin(node) : false; }); diff --git a/packages/core/src/module/resource.ts b/packages/core/src/module/resource.ts index 6ef90698f..0131527ff 100644 --- a/packages/core/src/module/resource.ts +++ b/packages/core/src/module/resource.ts @@ -5,13 +5,15 @@ import type { TemplateManager, JavaScriptManager, } from '@garfish/loader'; -import { AppInfo } from './app'; +import type { AppInfo } from './app'; +import type { interfaces } from '../interface'; // Fetch `script`, `link` and `module meta` elements function fetchStaticResources( appInfo: AppInfo, loader: Loader, entryManager: TemplateManager, + sandboxConfig: false | interfaces.SandboxConfig | undefined, ) { const toBoolean = (val) => typeof val !== 'undefined' && val !== 'false'; @@ -19,6 +21,15 @@ function fetchStaticResources( const jsNodes = Promise.all( entryManager .findAllJsNodes() + .filter((node) => { + if (sandboxConfig && sandboxConfig.excludeAssetFilter) { + const src = entryManager.findAttributeValue(node, 'src'); + if (src && sandboxConfig.excludeAssetFilter(src)) { + return false; + } + } + return true; + }) .map((node) => { const src = entryManager.findAttributeValue(node, 'src'); const type = entryManager.findAttributeValue(node, 'type'); @@ -153,6 +164,7 @@ export async function processAppResources(loader: Loader, appInfo: AppInfo) { appInfo, loader, entryManager, + appInfo.sandbox, ); resources.js = js; resources.link = link; diff --git a/packages/css-scope/package.json b/packages/css-scope/package.json index 6898624ee..58fce5e74 100644 --- a/packages/css-scope/package.json +++ b/packages/css-scope/package.json @@ -1,6 +1,6 @@ { "name": "@garfish/css-scope", - "version": "1.17.6", + "version": "1.18.0", "description": "css scope", "keywords": [ "garfish", diff --git a/packages/es-module/package.json b/packages/es-module/package.json index 9fa0aee23..285ce9e23 100644 --- a/packages/es-module/package.json +++ b/packages/es-module/package.json @@ -1,6 +1,6 @@ { "name": "@garfish/es-module", - "version": "1.17.6", + "version": "1.18.0", "description": "es module polyfill", "keywords": [ "garfish", diff --git a/packages/garfish/package.json b/packages/garfish/package.json index c2cdd807e..bb7ab8161 100644 --- a/packages/garfish/package.json +++ b/packages/garfish/package.json @@ -1,6 +1,6 @@ { "name": "garfish", - "version": "1.17.6", + "version": "1.18.0", "description": "garfish module.", "keywords": [ "garfish", diff --git a/packages/hooks/package.json b/packages/hooks/package.json index ce81d336f..2948820bc 100644 --- a/packages/hooks/package.json +++ b/packages/hooks/package.json @@ -1,6 +1,6 @@ { "name": "@garfish/hooks", - "version": "1.17.6", + "version": "1.18.0", "description": "hooks module.", "keywords": [ "garfish", diff --git a/packages/loader/package.json b/packages/loader/package.json index 5a6f80781..27b86e8b9 100644 --- a/packages/loader/package.json +++ b/packages/loader/package.json @@ -1,6 +1,6 @@ { "name": "@garfish/loader", - "version": "1.17.6", + "version": "1.18.0", "description": "loader module.", "keywords": [ "garfish", diff --git a/packages/remote-module/package.json b/packages/remote-module/package.json index 1c8e5a2f1..78603c698 100644 --- a/packages/remote-module/package.json +++ b/packages/remote-module/package.json @@ -1,6 +1,6 @@ { "name": "@garfish/remote-module", - "version": "1.17.6", + "version": "1.18.0", "description": "remote-module module.", "keywords": [ "garfish", diff --git a/packages/router/package.json b/packages/router/package.json index 6e4b592b2..0a6572007 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -1,6 +1,6 @@ { "name": "@garfish/router", - "version": "1.17.6", + "version": "1.18.0", "description": "router module.", "keywords": [ "garfish", diff --git a/packages/test-suite/package.json b/packages/test-suite/package.json index 4c064c643..cdc0e4a33 100644 --- a/packages/test-suite/package.json +++ b/packages/test-suite/package.json @@ -1,6 +1,6 @@ { "name": "@garfish/test-suite", - "version": "1.17.6", + "version": "1.18.0", "description": "garfish test suite.", "keywords": [ "garfish", diff --git a/packages/utils/package.json b/packages/utils/package.json index 8f441b943..eae590af8 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@garfish/utils", - "version": "1.17.6", + "version": "1.18.0", "description": "utils module.", "keywords": [ "garfish", diff --git a/packages/utils/src/utils.ts b/packages/utils/src/utils.ts index 6013066fb..9482ea32c 100644 --- a/packages/utils/src/utils.ts +++ b/packages/utils/src/utils.ts @@ -371,7 +371,11 @@ export function isAbsolute(url: string) { } export function transformUrl(resolvePath: string, curPath: string) { - if (curPath.startsWith('http') || curPath.startsWith('//')) { + if ( + curPath.startsWith('http') || + curPath.startsWith('//') || + curPath.startsWith('blob:') + ) { return curPath; } const baseUrl = new URL(resolvePath, location.href); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 93eadf760..3771df748 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6276,7 +6276,7 @@ packages: lru-cache: 6.0.0 mkdirp: 1.0.4 npm-pick-manifest: 6.1.1 - promise-inflight: 1.0.1 + promise-inflight: 1.0.1_bluebird@3.7.2 promise-retry: 2.0.1 semver: 7.3.5 which: 2.0.2 @@ -10575,7 +10575,7 @@ packages: mkdirp: 0.5.6 move-concurrently: 1.0.1 p-map: 3.0.0 - promise-inflight: 1.0.1 + promise-inflight: 1.0.1_bluebird@3.7.2 rimraf: 2.7.1 ssri: 7.1.1 unique-filename: 1.1.1 @@ -10626,7 +10626,7 @@ packages: minipass-pipeline: 1.2.4 mkdirp: 1.0.4 p-map: 4.0.0 - promise-inflight: 1.0.1 + promise-inflight: 1.0.1_bluebird@3.7.2 rimraf: 3.0.2 ssri: 9.0.1 tar: 6.2.0 diff --git a/website/docs/issues/childApp.md b/website/docs/issues/childApp.md index acdd69d3f..917245fae 100644 --- a/website/docs/issues/childApp.md +++ b/website/docs/issues/childApp.md @@ -28,6 +28,13 @@ if (window.__GARFISH__ && typeof __GARFISH_EXPORTS__ !== 'undefined') { } ``` +## 微应用 JSONP 跨域错误怎么处理? + +在使用 Garfish 时,微应用的动态脚本(如 JSONP)会被转化为 fetch 请求,这要求后端服务支持跨域请求,否则会产生错误。 + +可以使用 [excludeAssetFilter](/api/registerApp#sandbox) 参数来放行这些资源请求,但请注意,被该参数放行的资源会逃逸出沙箱,可能导致副作用,需自行处理。 + + ## Uncaught (in promise) TypeError: [Garfish warning]: Cannot read properties of undefined (reading 'call') - 错误原因 diff --git a/website/src/components/config/_sandbox.mdx b/website/src/components/config/_sandbox.mdx index 51a772769..2eda8dafb 100644 --- a/website/src/components/config/_sandbox.mdx +++ b/website/src/components/config/_sandbox.mdx @@ -22,6 +22,8 @@ interface SandboxConfig { fixOwnerDocument?: boolean; // disableLinkTransformToStyle 1.18.0 版本提供 ,默认值 false,禁用掉 link 自动 transform 成 style 的行为 disableLinkTransformToStyle?: boolean; + // excludeAssetFilter 1.18.0 版本提供,默认值为 undefined,用于过滤不需要再子应用沙箱中执行的资源例如 jsonp,url 参数为对应 script 的地址,返回 true 则会过滤掉该资源 + excludeAssetFilter?: (url: string) => boolean; } type Module = (sandbox: Sandbox) => OverridesData | void;