diff --git a/build.gradle b/build.gradle index 97789118b..6fbeb433b 100644 --- a/build.gradle +++ b/build.gradle @@ -121,17 +121,8 @@ tasks.register('bundleReactForTest', NpmTask) { environment = ['FORCE_COLOR': 'true'] } -tasks.register('bundleReactServerBrowserForTest', NpmTask) { - dependsOn npmInstall - args = ['run', 'build:react-server-browser-test-bundle'] - inputs.file('src/test/js/react-bundle-server-browser-entry.js') - inputs.file('package-lock.json') - outputs.file('build/resources/test/test-fixtures/react-bundle-server-browser.js') - environment = ['FORCE_COLOR': 'true'] -} - test { - dependsOn bundleReactForTest, bundleReactServerBrowserForTest + dependsOn bundleReactForTest useJUnitPlatform { } } diff --git a/package.json b/package.json index 9a18b194b..8df8c799c 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,6 @@ "postcov": "npm run lcov", "reinstall": "npm run clean && npm run local", "build:react-test-bundle": "esbuild src/test/js/react-bundle-entry.js --bundle --format=iife --platform=neutral --define:process.env.NODE_ENV=\\\"production\\\" --outfile=build/resources/test/test-fixtures/react-bundle.js", - "build:react-server-browser-test-bundle": "esbuild src/test/js/react-bundle-server-browser-entry.js --bundle --format=iife --platform=neutral --define:process.env.NODE_ENV=\\\"production\\\" --outfile=build/resources/test/test-fixtures/react-bundle-server-browser.js", "rollup:nodePolyfills": "npx rollup --config rollup.config.nodePolyfills.mjs", "test": "npm run test:jest && npm run test:deno", "test:deno": "deno test --coverage=build/deno/coverage --env-file=src/deno/.env --importmap=src/deno/importmap.json --no-check src/deno/**/*.test.ts", diff --git a/src/main/resources/lib/enonic/polyfill-react4xp/nodePolyfills.ts b/src/main/resources/lib/enonic/polyfill-react4xp/nodePolyfills.ts index fc0fe4c23..c82c3a769 100644 --- a/src/main/resources/lib/enonic/polyfill-react4xp/nodePolyfills.ts +++ b/src/main/resources/lib/enonic/polyfill-react4xp/nodePolyfills.ts @@ -9,23 +9,6 @@ import {TextEncoder} from '@sinonjs/text-encoding'; import 'core-js/modules/web.atob.js'; import 'core-js/modules/web.btoa.js'; -// Transitional MessageChannel shim: react-dom/server.browser constructs a -// MessageChannel at module-load time (unconditionally, before any render call) for -// its streaming scheduler. With this minimal polyfill, the module loads and -// renderToString — which doesn't touch the channel — works. -// -// LIMITATIONS: postMessage is intentionally a no-op. The streaming APIs -// (renderToReadableStream, prerender) WILL silently hang because the no-op timers -// can never deliver messages. For streaming, consumers must use -// react-dom/server.edge (which has no MessageChannel dependency). -// -// Remove this once @enonic/react4xp's webpack config aliases react-dom/server to -// react-dom/server.edge — see https://github.com/facebook/react/issues/31827 -function MessagePortShim(this: { onmessage: ((ev: { data: unknown }) => void) | null; postMessage: (data: unknown) => void }) { - this.onmessage = null; - this.postMessage = function () { /* no-op: SSR has no async */ }; -} - (function (context) { //────────────────────────────────────────────────────────────────────────── // Timers: When doing SSR it makes no sense to do anything asyncronously. @@ -38,17 +21,5 @@ function MessagePortShim(this: { onmessage: ((ev: { data: unknown }) => void) | if (typeof context['TextEncoder'] === 'undefined') { context['TextEncoder'] = TextEncoder; } - - //────────────────────────────────────────────────────────────────────────── - // MessageChannel: see comment above MessagePortShim. - //────────────────────────────────────────────────────────────────────────── - if (typeof context['MessageChannel'] === 'undefined') { - context['MessageChannel'] = function MessageChannel(this: { port1: object; port2: object }) { - // @ts-expect-error TS2350: 'new' expression with non-constructor type. - this.port1 = new MessagePortShim(); - // @ts-expect-error TS2350: 'new' expression with non-constructor type. - this.port2 = new MessagePortShim(); - }; - } //@ts-expect-error TS2695: Left side of comma operator is unused and has no side effects. })((1, eval)('this') as Partial); diff --git a/src/test/java/com/enonic/lib/react4xp/ssr/renderer/RendererTest.java b/src/test/java/com/enonic/lib/react4xp/ssr/renderer/RendererTest.java index c310c87e4..c86e1a0fb 100644 --- a/src/test/java/com/enonic/lib/react4xp/ssr/renderer/RendererTest.java +++ b/src/test/java/com/enonic/lib/react4xp/ssr/renderer/RendererTest.java @@ -144,36 +144,6 @@ void render_real_react_component_via_server_edge() } } - @Test - void render_real_react_component_via_server_browser_with_message_channel_shim() - { - // server.browser constructs `new MessageChannel()` at module-load time. This test - // pins the contract that the transitional shim in nodePolyfills.ts lets the module - // load and that renderToString (via server-legacy.browser) produces HTML. - final String reactBundle = readClasspathResource( "/test-fixtures/react-bundle-server-browser.js" ); - - final Map fixtures = new HashMap<>(); - fixtures.put( POLYFILL_BASICS, readClasspathResource( POLYFILL_BASICS ) ); - fixtures.put( POLYFILL_NODE, readClasspathResource( POLYFILL_NODE ) ); - fixtures.put( CHUNKS_GLOBALS_JSON, "{ \"main\": { \"js\": \"globals.js\" } }" ); - fixtures.put( GLOBALS_JS, - reactBundle + "\n" + "globalThis.React4xp = {" + " '_components/Greeter': {" + " default: function(props) {" + - " return React.createElement('h1', null, 'Hello, ' + props.name + '!');" + " }" + " }" + "};" ); - - final Renderer renderer = new Renderer( strictReader( fixtures ), testConfig(), 11L, engine, freshSource() ); - try - { - final Map result = renderer.render( "_components/Greeter", "{\"name\":\"World\"}", new String[0] ); - - assertNull( result.get( KEY_ERROR ), "expected success, got error: " + result.get( KEY_ERROR ) ); - assertEquals( "

Hello, World!

", result.get( KEY_HTML ) ); - } - finally - { - renderer.close(); - } - } - @Test void render_unknown_entry_propagates_illegal_state() { diff --git a/src/test/js/react-bundle-server-browser-entry.js b/src/test/js/react-bundle-server-browser-entry.js deleted file mode 100644 index b315196df..000000000 --- a/src/test/js/react-bundle-server-browser-entry.js +++ /dev/null @@ -1,15 +0,0 @@ -// Bundles real React 19 + react-dom/server.browser for the GraalJS integration test. -// server.browser is what @enonic/react4xp's webpack config currently resolves to (default -// 'browser' condition); see https://github.com/facebook/react/issues/31827. -// -// At module-load time, server.browser does `new MessageChannel()` for its streaming -// scheduler. Without the MessageChannel polyfill in nodePolyfills.ts, this would throw -// ReferenceError before any render call. With the polyfill, the module loads and -// `renderToString` (which goes through server-legacy.browser and never touches the -// channel) works. Streaming APIs would silently hang — they're not exercised here. - -const React = require('react'); -const ReactDOMServer = require('react-dom/server.browser'); - -globalThis.React = React; -globalThis.ReactDOMServer = { renderToString: ReactDOMServer.renderToString };