diff --git a/cypress/e2e/1-the-whole-process/visit-sub-app.spec.js b/cypress/e2e/1-the-whole-process/visit-sub-app.spec.js
index 3c65a56e8..0adc2b038 100644
--- a/cypress/e2e/1-the-whole-process/visit-sub-app.spec.js
+++ b/cypress/e2e/1-the-whole-process/visit-sub-app.spec.js
@@ -16,7 +16,7 @@ describe('whole process app render', () => {
},
sandbox: {
snapshot: false,
- }
+ },
},
});
@@ -148,8 +148,8 @@ describe('whole process app render', () => {
cy.wait(4000);
cy.contains('[data-test=title]', HomeTitle)
.then(() => {
- cy.get('[data-test=vite-count-btn]').dblclick();
- cy.contains('button', 'count is: 2');
+ cy.get('[data-test=vite-count-btn]').click({ force: true });
+ cy.contains('button', 'count is: 1');
})
.then(() => {
win.Garfish.router.push({ path: '/vue2/home' });
diff --git a/dev/app-main/src/constant.ts b/dev/app-main/src/constant.ts
index 6faeea49d..87a4e0c85 100644
--- a/dev/app-main/src/constant.ts
+++ b/dev/app-main/src/constant.ts
@@ -17,6 +17,7 @@ export const localApps: AppInfo = [
activeWhen: '/react17',
sandbox: {
fixStaticResourceBaseUrl: false,
+ disableLinkTransformToStyle: false,
},
// 子应用的入口地址,可以为 HTML 地址和 JS 地址
// 注意:entry 地址不可以与主应用+子应用激活地址相同,否则刷新时将会直接返回子应用内容
diff --git a/dev/app-react-17/public/a.css b/dev/app-react-17/public/a.css
new file mode 100644
index 000000000..bcc3583df
--- /dev/null
+++ b/dev/app-react-17/public/a.css
@@ -0,0 +1,3 @@
+.hello {
+ color: red;
+}
diff --git a/dev/app-react-17/public/b.css b/dev/app-react-17/public/b.css
new file mode 100644
index 000000000..c021f52a0
--- /dev/null
+++ b/dev/app-react-17/public/b.css
@@ -0,0 +1,3 @@
+.world {
+ color: blue;
+}
diff --git a/dev/app-react-17/public/index.html b/dev/app-react-17/public/index.html
index a58d73c00..82f1b3947 100644
--- a/dev/app-react-17/public/index.html
+++ b/dev/app-react-17/public/index.html
@@ -3,6 +3,14 @@
app react v17
+
+
diff --git a/packages/browser-vm/__tests__/dynamic-link.spec.ts b/packages/browser-vm/__tests__/dynamic-link.spec.ts
index 4770144db..05a4b6008 100644
--- a/packages/browser-vm/__tests__/dynamic-link.spec.ts
+++ b/packages/browser-vm/__tests__/dynamic-link.spec.ts
@@ -42,7 +42,7 @@ describe('Sandbox: dynamic link', () => {
[linkOrder3]: {
'Content-Type': styleType,
timeConsuming: 300,
- }
+ },
},
});
@@ -90,7 +90,7 @@ describe('Sandbox: dynamic link', () => {
go(`
const dynamicLink = document.createElement('link');
dynamicLink.href = "${withSuffix}";
- dynamicLink.rel = 'stylesheet';
+ dynamicLink.setAttribute('rel', 'stylesheet');
dynamicLink.onload = function () {
expect(document.body).toMatchSnapshot();
jestDone();
@@ -106,7 +106,7 @@ describe('Sandbox: dynamic link', () => {
go(`
const dynamicLink = document.createElement('link');
dynamicLink.href = "${withoutSuffix}";
- dynamicLink.rel = 'stylesheet';
+ dynamicLink.setAttribute('rel', 'stylesheet');
dynamicLink.onload = function () {
expect(document.body).toMatchSnapshot();
jestDone();
@@ -122,7 +122,7 @@ describe('Sandbox: dynamic link', () => {
go(`
const dynamicLink = document.createElement('link');
dynamicLink.href = "${withoutSuffixAndContentType}";
- dynamicLink.rel = 'stylesheet';
+ dynamicLink.setAttribute('rel', 'stylesheet');
dynamicLink.onload = function () {
expect(document.body).toMatchSnapshot();
jestDone();
diff --git a/packages/browser-vm/src/dynamicNode/processor.ts b/packages/browser-vm/src/dynamicNode/processor.ts
index 7b8f2a574..a8d77636f 100644
--- a/packages/browser-vm/src/dynamicNode/processor.ts
+++ b/packages/browser-vm/src/dynamicNode/processor.ts
@@ -368,9 +368,12 @@ export class DynamicNodeProcessor {
this.monitorChangesOfStyle();
}
// The link node of the request css needs to be changed to style node
- else if (this.is('link')) {
+ else if (
+ this.is('link') &&
+ this.sandbox.options.disableLinkTransformToStyle !== true
+ ) {
parentNode = this.findParentNodeInApp(context, 'head');
- if (this.el.rel === 'stylesheet' && this.el.href) {
+ if (this.el.getAttribute('rel') === 'stylesheet' && this.el.href) {
convertedNode = this.addDynamicLinkNode((styleNode) => {
this.nativeAppend.call(parentNode, styleNode);
});
diff --git a/packages/browser-vm/src/pluginify.ts b/packages/browser-vm/src/pluginify.ts
index ffcf81687..7b463ab1a 100644
--- a/packages/browser-vm/src/pluginify.ts
+++ b/packages/browser-vm/src/pluginify.ts
@@ -134,6 +134,9 @@ function createOptions(Garfish: interfaces.Garfish) {
fixOwnerDocument: Boolean(appInfo.sandbox?.fixOwnerDocument),
disableWith: Boolean(appInfo.sandbox?.disableWith),
disableElementtiming: Boolean(appInfo.sandbox?.disableElementtiming),
+ disableLinkTransformToStyle: Boolean(
+ appInfo.sandbox?.disableLinkTransformToStyle,
+ ),
strictIsolation: Boolean(appInfo.sandbox?.strictIsolation),
excludeAssetFilter: appInfo.sandbox?.excludeAssetFilter,
// 缓存模式,不收集副作用
diff --git a/packages/browser-vm/src/sandbox.ts b/packages/browser-vm/src/sandbox.ts
index 026605be3..5e6c6b15a 100644
--- a/packages/browser-vm/src/sandbox.ts
+++ b/packages/browser-vm/src/sandbox.ts
@@ -97,6 +97,7 @@ export class Sandbox {
fixStaticResourceBaseUrl: true,
disableWith: false,
strictIsolation: false,
+ disableLinkTransformToStyle: false,
disableCollect: false,
el: () => null,
styleScopeId: () => '',
diff --git a/packages/browser-vm/src/types.ts b/packages/browser-vm/src/types.ts
index 0786b09ed..9ba67a8e7 100644
--- a/packages/browser-vm/src/types.ts
+++ b/packages/browser-vm/src/types.ts
@@ -27,6 +27,7 @@ export interface SandboxOptions {
disableWith?: boolean;
strictIsolation?: boolean;
disableElementtiming?: boolean;
+ disableLinkTransformToStyle?: boolean;
disableCollect?: boolean;
modules?: Array;
excludeAssetFilter?: (url: string) => boolean;
diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts
index 23d49105d..40d0c4231 100644
--- a/packages/core/src/config.ts
+++ b/packages/core/src/config.ts
@@ -125,6 +125,7 @@ export const createDefaultOptions = () => {
disableWith: false,
strictIsolation: false,
disableElementtiming: false,
+ disableLinkTransformToStyle: false,
fixOwnerDocument: false,
},
// global hooks
diff --git a/packages/core/src/interface.ts b/packages/core/src/interface.ts
index 03ae9b43f..01e2f336d 100644
--- a/packages/core/src/interface.ts
+++ b/packages/core/src/interface.ts
@@ -81,6 +81,7 @@ export namespace interfaces {
disableWith?: boolean;
strictIsolation?: boolean;
disableElementtiming?: boolean;
+ disableLinkTransformToStyle?: boolean;
fixOwnerDocument?: boolean;
excludeAssetFilter?: (url: string) => boolean;
}
diff --git a/packages/core/src/module/app.ts b/packages/core/src/module/app.ts
index a403d0aee..46da21646 100644
--- a/packages/core/src/module/app.ts
+++ b/packages/core/src/module/app.ts
@@ -765,6 +765,14 @@ export class App {
},
link: (node) => {
+ if (
+ this.appInfo.sandbox &&
+ typeof this.appInfo.sandbox === 'object' &&
+ this.appInfo.sandbox.disableLinkTransformToStyle
+ ) {
+ return DOMApis.createElement(node);
+ }
+
if (DOMApis.isCssLinkNode(node)) {
const styleManager = this.resources.link.find((manager) =>
manager.isSameOrigin(node),
diff --git a/packages/core/src/module/resource.ts b/packages/core/src/module/resource.ts
index d998d107c..0131527ff 100644
--- a/packages/core/src/module/resource.ts
+++ b/packages/core/src/module/resource.ts
@@ -10,7 +10,7 @@ import type { interfaces } from '../interface';
// Fetch `script`, `link` and `module meta` elements
function fetchStaticResources(
- appName: string,
+ appInfo: AppInfo,
loader: Loader,
entryManager: TemplateManager,
sandboxConfig: false | interfaces.SandboxConfig | undefined,
@@ -50,7 +50,7 @@ function fetchStaticResources(
// we have a preload mechanism, so we don’t need to deal with it.
return loader
.load({
- scope: appName,
+ scope: appInfo.name,
url: fetchUrl,
crossOrigin,
defaultContentType: type,
@@ -63,7 +63,7 @@ function fetchStaticResources(
jsManager.setDefferAttribute(toBoolean(defer));
return jsManager;
} else {
- warn(`[${appName}] Failed to load script: ${fetchUrl}`);
+ warn(`[${appInfo.name}] Failed to load script: ${fetchUrl}`);
}
})
.catch(() => null);
@@ -86,20 +86,26 @@ function fetchStaticResources(
.findAllLinkNodes()
.map((node) => {
if (!entryManager.DOMApis.isCssLinkNode(node)) return;
+ if (
+ appInfo.sandbox &&
+ typeof appInfo.sandbox === 'object' &&
+ appInfo.sandbox.disableLinkTransformToStyle
+ )
+ return;
const href = entryManager.findAttributeValue(node, 'href');
if (href) {
const fetchUrl = entryManager.url
? transformUrl(entryManager.url, href)
: href;
return loader
- .load({ scope: appName, url: fetchUrl })
+ .load({ scope: appInfo.name, url: fetchUrl })
.then(({ resourceManager: styleManager }) => {
if (styleManager) {
styleManager.setDep(node);
styleManager?.correctPath();
return styleManager;
} else {
- warn(`${appName} Failed to load link: ${fetchUrl}`);
+ warn(`${appInfo.name} Failed to load link: ${fetchUrl}`);
}
})
.catch(() => null);
@@ -155,7 +161,7 @@ export async function processAppResources(loader: Loader, appInfo: AppInfo) {
if (entryManager instanceof loader.TemplateManager) {
isHtmlMode = true;
const [js, link, modules] = await fetchStaticResources(
- appInfo.name,
+ appInfo,
loader,
entryManager,
appInfo.sandbox,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8ef71aa87..3771df748 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -3975,7 +3975,7 @@ packages:
css-minimizer-webpack-plugin: 4.2.2_z2fhrcdfcwgcfi7cwpl76xfc2y
cssnano: 5.1.15_postcss@8.4.36
del: 6.1.1
- detect-port: 1.6.1
+ detect-port: 1.5.1
escape-html: 1.0.3
eta: 1.14.2
file-loader: 6.2.0_webpack@5.90.3
@@ -4076,7 +4076,7 @@ packages:
css-minimizer-webpack-plugin: 4.2.2_z2fhrcdfcwgcfi7cwpl76xfc2y
cssnano: 5.1.15_postcss@8.4.36
del: 6.1.1
- detect-port: 1.6.1
+ detect-port: 1.5.1
escape-html: 1.0.3
eta: 1.14.2
file-loader: 6.2.0_webpack@5.90.3
@@ -4177,7 +4177,7 @@ packages:
css-minimizer-webpack-plugin: 4.2.2_z2fhrcdfcwgcfi7cwpl76xfc2y
cssnano: 5.1.15_postcss@8.4.36
del: 6.1.1
- detect-port: 1.6.1
+ detect-port: 1.5.1
escape-html: 1.0.3
eta: 2.2.0
file-loader: 6.2.0_webpack@5.90.3
@@ -4438,7 +4438,7 @@ packages:
'@types/react-router-dom': 5.3.3
react: 17.0.2
react-dom: 17.0.2_react@17.0.2
- react-helmet-async: 2.0.5_react@17.0.2
+ react-helmet-async: 2.0.4_sfoxds7t5ydpegc3knd667wn6m
react-loadable: /@docusaurus/react-loadable/5.5.2_react@17.0.2
transitivePeerDependencies:
- '@swc/core'
@@ -4461,7 +4461,7 @@ packages:
'@types/react-router-dom': 5.3.3
react: 17.0.2
react-dom: 17.0.2_react@17.0.2
- react-helmet-async: 2.0.5_react@17.0.2
+ react-helmet-async: 2.0.4_sfoxds7t5ydpegc3knd667wn6m
react-loadable: /@docusaurus/react-loadable/5.5.2_react@17.0.2
transitivePeerDependencies:
- '@swc/core'
@@ -4878,7 +4878,7 @@ packages:
fs-extra: 10.1.0
react: 17.0.2
react-dom: 17.0.2_react@17.0.2
- sitemap: 7.1.2
+ sitemap: 7.1.1
tslib: 2.6.2
transitivePeerDependencies:
- '@parcel/css'
@@ -5135,7 +5135,7 @@ packages:
'@docusaurus/utils': 2.0.0-rc.1_fh5bds7ccduypwmpulad3xjppm
'@docusaurus/utils-validation': 2.0.0-rc.1_fh5bds7ccduypwmpulad3xjppm
algoliasearch: 4.23.3
- algoliasearch-helper: 3.20.0_algoliasearch@4.23.3
+ algoliasearch-helper: 3.19.0_algoliasearch@4.23.3
clsx: 1.2.1
eta: 1.14.2
fs-extra: 10.1.0
@@ -5534,25 +5534,16 @@ packages:
- webpack-cli
dev: false
- /@emnapi/core/1.2.0:
- resolution: {integrity: sha512-E7Vgw78I93we4ZWdYCb4DGAwRROGkMIXk7/y87UmANR+J6qsWusmC3gLt0H+O0KOt5e6O38U8oJamgbudrES/w==}
+ /@emnapi/core/1.1.1:
+ resolution: {integrity: sha512-eu4KjHfXg3I+UUR7vSuwZXpRo4c8h4Rtb5Lu2F7Z4JqJFl/eidquONEBiRs6viXKpWBC3BaJBy68xGJ2j56idw==}
requiresBuild: true
dependencies:
- '@emnapi/wasi-threads': 1.0.1
tslib: 2.6.2
dev: false
optional: true
- /@emnapi/runtime/1.2.0:
- resolution: {integrity: sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==}
- requiresBuild: true
- dependencies:
- tslib: 2.6.2
- dev: false
- optional: true
-
- /@emnapi/wasi-threads/1.0.1:
- resolution: {integrity: sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw==}
+ /@emnapi/runtime/1.1.1:
+ resolution: {integrity: sha512-3bfqkzuR1KLx57nZfjr2NLnFOobvyS0aTszaEGCGqmYMVDRaGvgIZbjGSV/MHSSmLgQ/b9JFHQ5xm5WRZYd+XQ==}
requiresBuild: true
dependencies:
tslib: 2.6.2
@@ -6063,13 +6054,13 @@ packages:
glob-to-regexp: 0.3.0
dev: true
- /@napi-rs/wasm-runtime/0.2.4:
- resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==}
+ /@napi-rs/wasm-runtime/0.2.3:
+ resolution: {integrity: sha512-e4qmGDzXu2MYjj/XiKSgJ7XS7Z83MYVRN1yYaYXeQNVEO56zmshqmzFaELfdb612sLq/GmiPfRIwSji+bIlyCw==}
requiresBuild: true
dependencies:
- '@emnapi/core': 1.2.0
- '@emnapi/runtime': 1.2.0
- '@tybys/wasm-util': 0.9.0
+ '@emnapi/core': 1.1.1
+ '@emnapi/runtime': 1.1.1
+ '@tybys/wasm-util': 0.8.3
dev: false
optional: true
@@ -6189,7 +6180,7 @@ packages:
cpu: [wasm32]
requiresBuild: true
dependencies:
- '@napi-rs/wasm-runtime': 0.2.4
+ '@napi-rs/wasm-runtime': 0.2.3
dev: false
optional: true
@@ -6815,8 +6806,8 @@ packages:
resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==}
dev: true
- /@tybys/wasm-util/0.9.0:
- resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==}
+ /@tybys/wasm-util/0.8.3:
+ resolution: {integrity: sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q==}
requiresBuild: true
dependencies:
tslib: 2.6.2
@@ -9210,8 +9201,8 @@ packages:
require-from-string: 2.0.2
uri-js: 4.4.1
- /algoliasearch-helper/3.20.0_algoliasearch@4.23.3:
- resolution: {integrity: sha512-6EVhAmVug0+hdRHWbubF7hLHHhLoQ8NjLk6iS6d4k5chWawpS5EDexrF6Jx/hPZvUKIeNrzsbTpjAkcvrjNLHg==}
+ /algoliasearch-helper/3.19.0_algoliasearch@4.23.3:
+ resolution: {integrity: sha512-AaSb5DZDMZmDQyIy6lf4aL0OZGgyIdqvLIIvSuVQOIOqfhrYSY7TvotIFI2x0Q3cP3xUpTd7lI1astUC4aXBJw==}
peerDependencies:
algoliasearch: '>= 3.1 < 6'
dependencies:
@@ -9384,7 +9375,6 @@ packages:
/are-we-there-yet/2.0.0:
resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==}
engines: {node: '>=10'}
- deprecated: This package is no longer supported.
dependencies:
delegates: 1.0.0
readable-stream: 3.6.2
@@ -12958,9 +12948,8 @@ packages:
- supports-color
dev: false
- /detect-port/1.6.1:
- resolution: {integrity: sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==}
- engines: {node: '>= 4.0.0'}
+ /detect-port/1.5.1:
+ resolution: {integrity: sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==}
hasBin: true
dependencies:
address: 1.2.2
@@ -15174,7 +15163,6 @@ packages:
/gauge/3.0.2:
resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
engines: {node: '>=10'}
- deprecated: This package is no longer supported.
dependencies:
aproba: 2.0.0
color-support: 1.1.3
@@ -20231,7 +20219,6 @@ packages:
/npmlog/5.0.1:
resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
- deprecated: This package is no longer supported.
dependencies:
are-we-there-yet: 2.0.0
console-control-strings: 1.1.0
@@ -22858,13 +22845,15 @@ packages:
shallowequal: 1.1.0
dev: false
- /react-helmet-async/2.0.5_react@17.0.2:
- resolution: {integrity: sha512-rYUYHeus+i27MvFE+Jaa4WsyBKGkL6qVgbJvSBoX8mbsWoABJXdEO0bZyi0F6i+4f0NuIb8AvqPMj3iXFHkMwg==}
+ /react-helmet-async/2.0.4_sfoxds7t5ydpegc3knd667wn6m:
+ resolution: {integrity: sha512-yxjQMWposw+akRfvpl5+8xejl4JtUlHnEBcji6u8/e6oc7ozT+P9PNTWMhCbz2y9tc5zPegw2BvKjQA+NwdEjQ==}
peerDependencies:
react: ^16.6.0 || ^17.0.0 || ^18.0.0
+ react-dom: ^16.6.0 || ^17.0.0 || ^18.0.0
dependencies:
invariant: 2.2.4
react: 17.0.2
+ react-dom: 17.0.2_react@17.0.2
react-fast-compare: 3.2.2
shallowequal: 1.1.0
dev: false
@@ -24580,8 +24569,8 @@ packages:
/sisteransi/1.0.5:
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
- /sitemap/7.1.2:
- resolution: {integrity: sha512-ARCqzHJ0p4gWt+j7NlU5eDlIO9+Rkr/JhPFZKKQ1l5GCus7rJH4UdrlVAh0xC/gDS/Qir2UMxqYNHtsKr2rpCw==}
+ /sitemap/7.1.1:
+ resolution: {integrity: sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg==}
engines: {node: '>=12.0.0', npm: '>=5.6.0'}
hasBin: true
dependencies:
diff --git a/website/src/components/config/_sandbox.mdx b/website/src/components/config/_sandbox.mdx
index 1bd813fdb..2eda8dafb 100644
--- a/website/src/components/config/_sandbox.mdx
+++ b/website/src/components/config/_sandbox.mdx
@@ -20,6 +20,8 @@ interface SandboxConfig {
disableElementtiming?: boolean;
// fixOwnerDocument 1.17.2 版本提供 ,默认值 false,目前可能会存在 ownerDocument 逃逸的情况,设置为 true 之后将会避免 ownerDocument 逃逸
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;
}