diff --git a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js index 94d37cfc902d..fc1039faaa0c 100644 --- a/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js +++ b/packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js @@ -183,6 +183,7 @@ export type Props = { checked?: boolean, defaultChecked?: boolean, multiple?: boolean, + type?: string, src?: string | Blob | MediaSource | MediaStream, // TODO: Response srcSet?: string, loading?: 'eager' | 'lazy', @@ -469,6 +470,44 @@ export function createHoistableInstance( } let didWarnScriptTags = false; +function isScriptDataBlock(props: Props): boolean { + const scriptType = props.type; + if (typeof scriptType !== 'string' || scriptType === '') { + return false; + } + const lower = scriptType.toLowerCase(); + // Special non-MIME keywords recognized by the HTML spec + // TODO: May be fine to also not warn about having these types be parsed as "parser-inserted" + if ( + lower === 'module' || + lower === 'importmap' || + lower === 'speculationrules' + ) { + return false; + } + // JavaScript MIME types per https://mimesniff.spec.whatwg.org/#javascript-mime-type + switch (lower) { + case 'application/ecmascript': + case 'application/javascript': + case 'application/x-ecmascript': + case 'application/x-javascript': + case 'text/ecmascript': + case 'text/javascript': + case 'text/javascript1.0': + case 'text/javascript1.1': + case 'text/javascript1.2': + case 'text/javascript1.3': + case 'text/javascript1.4': + case 'text/javascript1.5': + case 'text/jscript': + case 'text/livescript': + case 'text/x-ecmascript': + case 'text/x-javascript': + return false; + } + // Any other non-empty type value means this is a data block + return true; +} const warnedUnknownTags: { [key: string]: boolean, } = { @@ -526,7 +565,13 @@ export function createInstance( // set to true and it does not execute const div = ownerDocument.createElement('div'); if (__DEV__) { - if (enableTrustedTypesIntegration && !didWarnScriptTags) { + if ( + enableTrustedTypesIntegration && + !didWarnScriptTags && + // Data block scripts are not executed by UAs anyway so + // we don't need to warn: https://html.spec.whatwg.org/multipage/scripting.html#attr-script-type + !isScriptDataBlock(props) + ) { console.error( 'Encountered a script tag while rendering React component. ' + 'Scripts inside React components are never executed when rendering ' + diff --git a/packages/react-dom/src/client/__tests__/trustedTypes-test.internal.js b/packages/react-dom/src/client/__tests__/trustedTypes-test.internal.js index 06744581ae95..2d7fa85880e7 100644 --- a/packages/react-dom/src/client/__tests__/trustedTypes-test.internal.js +++ b/packages/react-dom/src/client/__tests__/trustedTypes-test.internal.js @@ -248,4 +248,24 @@ describe('when Trusted Types are available in global object', () => { root.render(); }); }); + + it('should not warn when rendering a data block script tag', async () => { + const root = ReactDOMClient.createRoot(container); + await act(() => { + root.render( + , + ); + }); + }); + + it('should not warn when rendering a ld+json script tag', async () => { + const root = ReactDOMClient.createRoot(container); + await act(() => { + root.render( + , + ); + }); + }); });