refactor: test converting GWT to TS#24486
Draft
Artur- wants to merge 135 commits into
Draft
Conversation
Test Results 1 400 files - 30 1 400 suites - 30 1h 56m 28s ⏱️ + 32m 39s For more details on these failures and errors, see this check. Results for commit 8e73a1e. ± Comparison against base commit 8fb5047. ♻️ This comment has been updated with latest results. |
Replace the GWT JSNI implementation of `Console` with a TypeScript module published at `window.Vaadin.Flow.internal.client.Console` and reached from remaining Java code through a `@JsType(isNative = true)` shim. JVM unit tests still hit a `GWT.isScript()` fallback that prints to `System.out`/`System.err`. This is the pilot migration. The new `internal/bridge.ts` is a registry that publishes every TS-migrated implementation under the `Vaadin.Flow.internal.*` namespace before `FlowClient.init()` runs; it is re-installed before each init to survive tests that wipe `window.Vaadin`. `ClientEngineTestBase` installs equivalent pass-through stubs so existing `Gwt*Test` classes continue to work. MIGRATION.md documents the proven pattern, the per-PR checklist and the migration order for the remaining ~106 GWT-compiled classes.
LocationParser had no production callers (only its own JUnit test referenced it); StorageUtil had no callers at all. Neither needs to be migrated to TS.
Replace the JSNI implementation of `LitUtils.isLitElement` and `whenRendered` with a TypeScript module published at `window.Vaadin.Flow.internal.client.LitUtils`. Establishes the callback-bridging pattern: GWT marshals Java `Runnable` through a sibling `@JsFunction JsRunnable` interface declared on the `NativeLitUtils` shim, so existing call sites in `SimpleElementBindingStrategy` are unchanged. LitUtils is GWT-only (no JVM callers), so the Java shim does not need a `System.out` fallback — it just no-ops outside `GWT.isScript()`.
Replace the JSNI `addReadyCallback` with a TypeScript module published at `window.Vaadin.Flow.internal.client.ReactUtils`. Reuses the `JsRunnable` @JsFunction declared on `NativeLitUtils` for callback marshalling. `ReactUtils.isInitialized` is a plain null check with no browser API to delegate to and stays in Java.
Move the JSNI accesses to `window.Vaadin.connectionState` and `window.Vaadin.connectionIndicator` into a TypeScript module published at `window.Vaadin.Flow.internal.client.ConnectionIndicator`. The Java class keeps its public `CONNECTED`/`LOADING`/`RECONNECTING`/`CONNECTION_LOST` string constants (used as keys throughout the codebase) and delegates the six methods through a `NativeConnectionIndicator` JsType shim. Pure JVM callers get no-op methods and a `null` `getState()`.
Move `getElementById` and `getElementByName` to a TypeScript module published at `window.Vaadin.Flow.internal.client.ElementUtil`. `hasTag` is a pure-Java `instanceof` + name comparison with no browser API to delegate to and stays in `ElementUtil.java`. JVM tests get a `null` fallback from the two delegating methods.
Move the 17 JSNI helpers in `com.vaadin.client.WidgetUtil` to a TypeScript module published at `window.Vaadin.Flow.internal.client.WidgetUtil`. The pure-Java helpers (`refresh`, `getAbsoluteUrl`, `updateAttribute`, the `toPrettyJson` wrapper and `equals`) stay in `WidgetUtil.java`. JVM paths preserve their existing behavior — only the JSNI bodies move.
Move the 13 JSNI helpers in `com.vaadin.client.PolymerUtils` to a TypeScript module published at `window.Vaadin.Flow.internal.client.PolymerUtils`. The pure-Java logic (createModelTree, getCustomElement, addReadyListener, fireReadyEvent, the registered-change-handler wiring, etc.) stays in PolymerUtils.java. JVM paths keep their existing no-op-when-not-script behavior.
Move the 3 JSNI helpers (`checkForTouchDevice`, `getBrowserString`, `isIos`) to a TypeScript module published at `window.Vaadin.Flow.internal.client.BrowserInfo`. The pure-Java `BrowserDetails`-backed query methods (`isFirefox`, `isMacOSX`, ...) stay in `BrowserInfo.java`. JVM tests get an empty user-agent string and `false` for the touch/iOS probes.
Move the 5 JSNI helpers (`supportsHtmlWhenReady`, `addHtmlImportsReadyHandler`, `addOnloadHandler`, `getStyleSheetLength`, `runPromiseExpression`) to a TypeScript module published at `window.Vaadin.Flow.internal.client.ResourceLoader`. `addOnloadHandler` no longer crosses the boundary with a Java listener + event object; the Java shim builds two `JsRunnable` closures that capture the event so the TS side only sees two `() => void` callbacks. Likewise `runPromiseExpression` accepts a `JsSupplier` (new `@JsFunction` declared on `NativeResourceLoader`) for the promise factory.
Move the 3 JSNI helpers (`recreateNodes`, `showPopover`, `getShadowRootElement`) to a TypeScript module published at `window.Vaadin.Flow.internal.client.SystemErrorHandler`. The error UI rendering logic stays in `SystemErrorHandler.java`.
…resendRequest to TS
Both classes had a single JSNI helper each; the JS bodies move to
`window.Vaadin.Flow.internal.client.communication.{MessageSender,XhrConnection}`.
The surrounding Java orchestration (request queues, retry timers, XHR
plumbing) stays in place.
Move the 5 JSNI helpers (`removeStylesheetByIdFromDom`, `callAfterServerUpdates`, `calculateBootstrapTime`, `parseJSONResponse`, `getFetchStartTime`) to a TypeScript module published at `window.Vaadin.Flow.internal.client.communication.MessageHandler`. The UIDL processing orchestration stays in `MessageHandler.java`.
Move the 5 JSNI helpers (`getJsoConfiguration`, `vaadinBootstrapLoaded`, `deferStartApplication`, `startApplicationImmediately`, `registerCallback`) to a TypeScript module published at `window.Vaadin.Flow.internal.client.bootstrap.Bootstrapper`. Callbacks taking `String` use a new `JsStringConsumer` `@JsFunction` on `NativeBootstrapper`.
…nCodec JSNI Each had one JSNI helper: - `ExecuteJavaScriptElementUtils.isPropertyDefined` checks for a Polymer-style property descriptor on the element's constructor; moved to `window.Vaadin.Flow.internal.client.ExecuteJavaScriptElementUtils`. - `ClientJsonCodec.createReturnChannelCallback` returns a JS function that forwards its variadic args to a Java consumer. Moved to `window.Vaadin.Flow.internal.client.flow.util.ClientJsonCodec`; the Java shim wraps `(nodeId, channelId, ServerConnector::sendReturnChannelMessage)` into a new `JsArgsConsumer` `@JsFunction` so the TS side stays ServerConnector-agnostic.
…+ status - Document the `@JsFunction` interfaces established for callback marshalling (JsRunnable, JsSupplier, JsStringConsumer, JsArgsConsumer) and the "split-listener" trick used for ResourceLoader.addOnloadHandler. - Record migration status: which classes have shipped, which are deferred because their JSNI is a bidirectional `this`-bound API surface rather than a browser-API helper, and which need no JSNI bridge.
… bridge The JSNI body of `getContextExecutionObject` built the `this` object exposed to user JS executed by `executeJs`: a `getNode(element)` lookup, the cleaned `$appId` string, the `registry` handle, three element-utility invocations that auto-resolve elements to StateNodes, and a `stopApplication()` callback. Move the object construction to `window.Vaadin.Flow.internal.client.flow.ExecuteJavaScriptProcessor`. The Java shim now does the `$appId` cleanup, passes the `JsMap<Object, Object>` node-parameter map straight through, and supplies three `@JsFunction` callbacks for the element-utility dispatch. `JsRunnable` moved to a top-level `com.vaadin.client.JsRunnable` since it is now used from three packages.
…ia bridge Move the two JSNI bodies in `com.vaadin.client.flow.binding.SimpleElementBindingStrategy`: - `bindPolymerModelProperties` checks `PolymerUtils.isPolymerElement` and the `customElements.whenDefined` + 1s race, then calls the Java `hookUpPolymer` hand-off through a `JsRunnable`. TS imports `PolymerUtils` from the already-migrated module rather than going through the bridge namespace. - `hookUpPolymerElement` replaces the element's `_propertiesChanged` and `ready` methods, and on `ready` patches the `dom-repeat` prototype's `_propertiesChanged` to forward per-item changes back to the server. The Java shim provides three callbacks: handlePropertiesChanged (consumes the changed-props JS object), fireReadyEvent (no-arg), and handleListItemPropertyChange (nodeId, host, propertyName, value), each closing over the right Java context (node / element / tree).
…ridge Move the 5 outer JSNI helpers in `com.vaadin.client.communication.AtmospherePushConnection`: - `createConfig` returns the Atmosphere config literal. - `doConnect` wires Atmosphere's callbacks to nine Java methods bundled into a new `AtmosphereConnectCallbacks` @jstype POJO (three new JsFunction shapes declared as siblings: response/reconnect/object-supplier). - `doPush`, `doDisconnect`, `isAtmosphereLoaded` are trivial wrappers. The six JSO accessors on the inner `AtmosphereConfiguration` (`getStringValue`, `setStringValue`, …) stay Java — they are property accessors on a JS instance and don't fit a separate TS module.
Move the two `publishJavascriptMethods` and `publishDevelopmentModeJavascriptMethods` JSNI bodies in `com.vaadin.client.ApplicationConnection` to a TypeScript module published at `window.Vaadin.Flow.internal.client.ApplicationConnection`. Both methods build the `window.Vaadin.Flow.clients[appId]` API surface from positional `@JsFunction` callbacks passed by the Java shim — method references into ApplicationConnection, Poller, Registry, ServerConnector, StateTree, StateNode and URIResolver — replacing the 12 cross-class `@JsType::method` JSNI calls. Added a public `MessageHandler.getProfilingData()` helper so the cross- package access to its package-private timing fields can stay Java instead of having to be reached via @-syntax JSNI. `ApplicationConnection.Styles.set` (JSO property accessor) stays Java.
Move the 8 outer static + 2 relative-time JSNI helpers in `com.vaadin.client.Profiler` to a TypeScript module published at `window.Vaadin.Flow.internal.client.Profiler`. The Java shim passes the `EVT_GROUP` constant and `GWT.getModuleName()` value as explicit args to `logGwtEvent`, replacing the JSNI's `@EVT_GROUP` / `@GWT::getModuleName()` field-syntax references. JVM fallbacks use `System.currentTimeMillis`. The inner `GwtStatsEvent` JSO accessors (`getEvtGroup`, `getMillis`, ...) stay Java — they sit on a JS instance and don't fit a separate TS module.
All structurally-migratable JSNI bodies are now published via the bridge: 17 classes across client, client.bootstrap, client.communication, client.flow, client.flow.binding, client.flow.util. Document why each remaining JSNI stays in Java (JavaScriptObject subclasses whose methods sit on a JS instance, already-native `@JsType` collections, and vendored gwt/** code) and outline the tear-down phase that will drop src/main/java, src/test, src/test-gwt, the GWT plugin and the bridge registry once the pure-Java logic has been ported to TS.
First non-JSNI migration. Demonstrates the pattern for stateful classes that are instantiated by JVM unit tests: - The TS class owns the real logic. - The Java public class becomes a thin facade that delegates to a `NativeExistingElementMap` JsType under GWT, and keeps a parallel `HashMap`/`ArrayList`-backed implementation for the JVM path so `ExistingElementMapTest` (and any other JUnit code that calls `new ExistingElementMap()`) keeps passing unchanged. `ExistingElementMapTest` (JVM, 2 tests) still passes.
…pattern) Second instance of the stateful pattern (a class instantiated by Java code that needs to keep working under both GWT and JVM): the public Java class keeps a `HashSet`-backed JVM implementation alongside the `NativeUpdatableModelProperties` JsType binding to the TS implementation. Used by `SimpleElementBindingStrategy.handlePropertiesChanged` and `ExecuteJavaScriptElementUtils.populateModelProperty`; both keep calling the same Java public API.
Add the recipe used by `ExistingElementMap` and `UpdatableModelProperties` for migrating stateful classes that are instantiated by JUnit tests: TS class for the real implementation, `Native<Name>` JsType binding for the constructor + instance methods, public Java class becomes a facade with a JVM fallback (HashMap/HashSet/ ArrayList) so existing JUnit tests keep passing. The fallback goes away together with the JUnit suite in the tear-down phase.
…java Companion to MIGRATION.md. MIGRATION.md describes the per-class patterns; this file says what to do in what order, in PRs small enough to review, with the tests that prove each step is safe. Strategy: drop JUnit one-by-one as target code migrates, port their coverage to mocha first, each Java class becomes a pure `@JsType` native shim (no JVM fallback) — facades would just be make-work that gets deleted in tear-down anyway. 13 tiers, leaves up: reactive → nodefeature → dom → state tree → binding → application kernel → communication (3 sub-tiers) → ApplicationConnection → tear-down. Plus three pre-work commits (P1 inventory, P2 mocha harness, P3 confirm GwtTest CI status). Estimated 21-30 days of focused work across ~25-40 commits.
…TION.md Reframe the recommended migration pattern to match MIGRATION_PLAN.md: - The pure `@JsType(isNative = true)` declaration (no JVM branch, no Native<Name> sibling unless callback bundling demands one) is the recommended shape for new migrations. - The `GWT.isScript()` shim + JVM fallback pattern used by the 24 in-flight migrations is now framed as transitional. Per the plan, these shims collapse to the recommended form during Tier 7 (top-level utilities) and the matching tiers for communication and bootstrap classes. - Per-PR checklist updated: tests get ported to mocha *first*, JUnit and GwtTest get deleted in the same commit as the migration. The GwtTest stub map shrinks rather than grows. - JsRunnable noted as the shared top-level callback shape; the `JsArrayLength` table now reflects its move from NativeLitUtils to com.vaadin.client. - "PR size and order" defers the ordering specifics to MIGRATION_PLAN.md to keep one source of truth.
Classifies every test method in src/test/java and src/test-gwt as PORT (port to mocha before its class migrates), DROP (tests JVM fallback / GWT-build quirks / JS-engine-guaranteed behaviour), or INTEGRATION (already covered by flow-tests at the right level). 176 methods across 49 test files: - ~130 PORT — protocol-critical state-tree, binding, communication - ~40 DROP — Jre* collections, JsArray/JsMap/JsSet/JsWeakMap (already native), GWT-specific quirks (assertion flag, gwt generics), GWT bundle size budget - ~6 INTEGRATION — reconnect flow, web-component navigation, unrecoverable-error UI; flow-tests covers these adequately Each PORT row maps to a tier in MIGRATION_PLAN.md; that's where the mocha port lands alongside the class migration. The biggest single ports are GwtBasicElementBinderTest (71 tests) and GwtPolymerModelTest (21 tests), both in T5. Open questions captured at the bottom: bundle-size budget for T13, splitting binding tests across mocha files, eslint encoding of DomApiAbstractionUsage intent, real-CI status of GwtTest (P3).
…cript Replace the @JsOverlay sendEventToServer / sendNodePropertySyncToServer / sendTemplateEventToServer / sendExistingElementAttachToServer / sendExistingElementWithIdAttachToServer Java methods with native bindings to TS-side equivalents on StateTree. The TS class now holds a serverConnector reference (wired by DefaultRegistry via setServerConnector after construction, same install-after-construct pattern used for InitialPropertiesHandler). sendNodePropertySyncToServer also consults the InitialPropertiesHandler to suppress sync messages that the handler will replay; the IPH type binding gains the handlePropertyUpdate signature accordingly. Tests: 89/89 mocha pass.
Replace the Java @JsOverlay registerNode / unregisterNode methods with native bindings to TS-side methods on StateTree. The TS versions take over the assertions (now thrown as Error) and the InitialPropertiesHandler.nodeRegistered call when an update is in progress -- the IPH binding type gains the nodeRegistered signature. The @JSmethod aliases registerNodeImpl / unregisterNodeImpl are dropped as the same method names register cleanly across the bridge. Tests: 89/89 mocha pass.
Replace the @JsOverlay prepareForResync / clearLists / assertValidNode / isValidNode helpers with TS-side equivalents. The Java side becomes a single native prepareForResync binding. The ServerEventObject.getIfPresent + rejectPromises JSNI pair is inlined in TS as direct `$server` property reads and a promise-array iteration that rejects each pending @ClientCallable promise. The RPC_PROMISE_CALLBACK_NAME literal is mirrored as a TS constant so the module doesn't depend on Java's JsonConstants. Drops the forEachNodeImpl @JSmethod alias and JsForEachNodeCallback inner interface (no longer referenced), plus six now-unused imports. Tests: 89/89 mocha pass.
Convert WidgetUtil from a static-method wrapper that forwarded to NativeWidgetUtil into a pure @jstype(isNative=true) facade onto the TS WidgetUtil module. The Java-only helpers (refresh, getAbsoluteUrl, isAbsoluteUrl, crazyJsCast, crazyJsoCast, toPrettyJson, updateAttribute, createJsonObject, equals) are added to the TS module so the facade can cover all callers. Drops NativeWidgetUtil.java -- the same name no longer needs a separate binding class. Tests: 89/89 mocha pass.
Inline the case-insensitive tag-name comparison in the TS module so Java's @jstype native facade can declare hasTag native rather than keep it as Java-side @JsOverlay logic. Tests: 89/89 mocha pass.
…o TS Replace MapProperty's @JsOverlay syncToServer / getSyncToServerCommand with TS-side equivalents. The new TS methods reach into the existing StateTree.sendNodePropertySyncToServer dispatch (already in TS) so the whole sync-command chain is now JS-native. The isServerUpdate / markServerUpdate / doSetValue @JSmethod aliases are dropped now that callers go through the TS methods directly. The Objects.equals null-safe comparison becomes a small private TS helper (non-null GWT objects collapse to reference equality anyway, which matches === on the TS side). getPreviousDomValue stays Java-side as @JsOverlay because the Java Optional<Object> shape doesn't survive a JS-only port. Tests: 89/89 mocha pass.
…ta API Promote setNodeData(String, Object) / getNodeData(String) / clearNodeData(String) to public native methods on the @jstype native StateNode facade. The previous @JsOverlay versions keyed on Class<T>.getName() are kept as Java-only convenience overloads, but cross-language call sites can now use an explicit string-key constant that survives a TypeScript port. Adds NODE_DATA_KEY = "UpdatableModelProperties" on UpdatableModelProperties, and updates ExecuteJavaScriptElementUtils to use the constant instead of UpdatableModelProperties.class -- preparation for migrating those @JsOverlay methods to TS. Tests: 89/89 mocha pass.
… to TS Move attachExistingElement, populateModelProperties, and registerUpdatableModelProperties from @JsOverlay Java helpers into the TS ExecuteJavaScriptElementUtils module. The Java side becomes a pure @jstype(isNative=true) facade. PolymerUtils.getTag is inlined as a direct ELEMENT_DATA/TAG lookup so the TS module doesn't need to add a PolymerUtils method. The UpdatableModelProperties lookup uses the explicit UPDATABLE_MODEL_PROPERTIES_KEY string (mirroring UpdatableModelProperties.NODE_DATA_KEY) so cross-language storage hits the same slot. registerUpdatableModelProperties resolves the constructor through window.Vaadin.Flow.internal.client.flow.model.UpdatableModelProperties since the existing JsType native facade publishes it at that path. Drops ExecuteJavaScriptCallbacks.java -- the adapter is no longer needed now that the TS processor calls TS utilities directly. The ExecuteJavaScriptProcessor constructor parameter list shrinks back to just (registry). Tests: 89/89 mocha pass.
Move the remaining Java-only logic (createModelTree, registerChangeHandlers and the property/list-notification machinery, addReadyListener / fireReadyEvent, getCustomElement, getTag, hasTag, getChildIgnoringStyles) to the TS PolymerUtils module. The Java class becomes a pure @jstype(isNative=true) facade. Drops NativePolymerUtils.java -- the JsInterop binding name is now the public PolymerUtils class itself. The static readyListeners JsWeakMap migrates to a module-level WeakMap<Element, Set<() => void>>. createModelTree's recursion uses the convert(...) hooks already exposed on the TS NodeMap/NodeList and registers change/splice listeners through the existing event-router surface; getNotificationPath / getPropertiesNotificationPath / getListNotificationPath are ported verbatim. Tests: 89/89 mocha pass.
Remove the @JsOverlay setNodeData(T) / getNodeData(Class<T>) / clearNodeData(T) Java-only convenience overloads now that all call sites have been switched to the explicit string-keyed natives. Updates the three remaining Class<T> sites in SimpleElementBindingStrategy to use string-key constants: UpdatableModelProperties.NODE_DATA_KEY for the model-property lookup in handlePropertyChange, and a new InitialPropertyUpdate.NODE_DATA_KEY private constant for the inner class's setNodeData / getNodeData / clearNodeData triplet. The native string-keyed methods are the only public surface left on StateNode for node-data storage; cross-language callers (Java + TS) agree on the key strings without depending on GWT's Class.getName() runtime output. Tests: 89/89 mocha pass.
Move the implementation to src/main/frontend/internal/client/communication/RequestResponseTracker.ts; the Java class becomes a pure @jstype(isNative=true) facade. The GWT EventBus is replaced with simple per-event-type Set<Handler> queues, and the four Event<H> subclasses (RequestStartingEvent, ResponseHandlingStartedEvent, ResponseHandlingEndedEvent, ReconnectionAttemptEvent) are eliminated. Handlers become JsRunnable or JsIntConsumer @JsFunction callbacks (a new JsIntConsumer is added alongside JsRunnable / JsBooleanSupplier). The Java facade still returns GWT's HandlerRegistration, which structurally matches the TS { removeHandler(): void } shape. The endRequest "should we flush queued invocations now?" decision is hoisted into a JsRunnable constructor argument, wired by DefaultRegistry to the same UILifecycle / ServerRpcQueue / MessageSender checks the original Java version performed inline. MessageSender's getResynchronizationState becomes public so the wiring lambda can read it from outside the package. Java callers updated: - XhrConnection drops the unused event arg from the response-ended handler - MessageSender takes the int attempt number directly instead of ev.getAttempt() - MessageHandler.fireEvent(new ResponseHandlingStartedEvent()) -> fireResponseHandlingStarted() - DefaultConnectionStateHandler.fireEvent(new ReconnectionAttemptEvent(n)) -> fireReconnectionAttempt(n) Tests: 89/89 mocha pass.
Move the implementation (defineMethod / removeMethod / getMethods / rejectPromises / initPromiseHandler / event-data resolution / expression cache) to src/main/frontend/internal/client/flow/binding/ServerEventObject.ts; the Java class becomes a pure @jstype(isNative=true) facade. JSNI patterns convert to direct JS: `this[name] = function(...) {...}` for defineMethod, `Object.defineProperty(this, PROMISE_CALLBACK_NAME, ...)` for initPromiseHandler, `new Function('event', 'element', body)` for the data-expression cache. ServerEventObject no longer extends JavaScriptObject, so SimpleElementBindingStrategy's `WidgetUtil.crazyJsoCast` cast switches to the generic-typed `crazyJsCast<ServerEventObject>` (identity-cast in both forms; the difference was just Java type-checking). MessageSender.getResynchronizationState is promoted to public so DefaultRegistry's RequestResponseTracker wiring lambda can reach it from outside the package -- already needed by the previous RRT commit. Tests: 89/89 mocha pass.
Move bindServerEventHandlerNames (long form) to src/main/frontend/internal/client/flow/binding/ServerEventHandlerBinder.ts; the Java class becomes a pure @jstype(isNative=true) facade. The original `Supplier<ServerEventObject>` parameter becomes a new inner @JsFunction ServerEventObjectProvider so Java lambdas can be passed across the JS boundary. The two-arg convenience overload (Element, StateNode) stays Java-side as an @JsOverlay delegating to the long form with the CLIENT_DELEGATE_HANDLERS feature id. Tests: 89/89 mocha pass.
The original Profiler collected timings via the GWT `__gwtStatsEvent` hook, and only when the `vaadin.profiler` GWT compile flag was set; production builds always used the disabled no-op base class. Both the compile flag and the stats-event stream disappear with the GWT runtime, so the aggregation, Node/RelativeTimeSupplier types, pretty-printer, and ProfilerResultConsumer plumbing become unreachable. Profiler.ts keeps the same public surface as a thin no-op: isEnabled returns false, enter/leave/reset/initialize/logTimings/logBootstrapTimings are no-ops, and the time helpers wrap performance.now(). Existing Java guards (`if (Profiler.isEnabled())`) keep linking but never execute their bodies. Profiler.java becomes a @jstype(isNative=true) facade over the TS module; NativeProfiler.java is removed since its functions were only used by the old aggregation paths that no longer exist.
The replace-with referenced Profiler.EnabledProfiler, removed in the prior commit when Profiler became a TS no-op facade. The compile-time switch was gated on vaadin.profiler=true and the default value was false, so no production build was affected.
Move the error-routing, rendering, and resync flow into SystemErrorHandler.ts. Construction takes a new SystemErrorHandlerCallbacks adapter so the TS class does not depend on the Java Registry facade; the adapter is built in DefaultRegistry where the wiring lives and is added last so its callbacks reach already-built Heartbeat / MessageSender / PushConfiguration / MessageHandler. The Java SystemErrorHandler becomes a @jstype(isNative=true) facade. The Throwable overload of handleError() stays so GWT.setUncaughtExceptionHandler can still take it as a method reference; the body unwraps UmbrellaException and forwards a string to TS so the TS side doesn't need to model GWT throwables. Xhr.getWithCredentials is replaced by XMLHttpRequest with withCredentials; Scheduler.scheduleDeferred is replaced by setTimeout(fn, 0) which matches its semantics. NativeSystemErrorHandler is removed since its helpers are inlined into the TS module. Registry.getSystemErrorHandler is keyed by an explicit "SystemErrorHandler" string rather than Class.getName() — GWT collapses every native @jstype to JavaScriptObject so a second Class-keyed registration would collide with the existing Heartbeat one.
Replace the BrowserDetails-backed Java BrowserInfo with a TS class that inlines a minimal UA-string parser covering the six methods used by external Java callers (isChrome, isIE, isEdge, isSafariOrIOS, isOpera, isWebkit) plus the original public surface (isFirefox, isSafari, isGecko, isAndroid, isTouchDevice, isAndroidWithBrokenScrollTop, version getters). The TS class is wired through Vaadin.Flow.internal.client.BrowserInfo so Java callers keep the BrowserInfo.get().isXxx() pattern unchanged — both the static .get() factory and the instance method names survive GWT-OBF compilation because the call sites address the JS namespace verbatim. Static helpers getBrowserString / checkForTouchDevice / isIos remain on the same namespace so the existing GWT-compiled call sites keep linking. NativeBrowserInfo is removed since its helpers are now part of the migrated class.
The vendored SimpleEventBus (a copy of com.google.web.bindery.event.shared.SimpleEventBus) has no callers in flow-client. The original EventBus that the four request/response trackers used was inlined into the TS-side RequestResponseTracker migration, so the supporting class became dead code.
Move BindingStrategy / BinderContext interfaces, TextBindingStrategy, and the 1500-LOC SimpleElementBindingStrategy from Java into TS. Cross-strategy references become TS-to-TS, sidestepping the GWT-OBF callback name-mangling that blocked earlier callback-adapter migrations. The Java side keeps Binder as a thin @jstype(isNative=true) facade with just static bind(StateNode, Node) so ApplicationConnection's single call site at line 71 keeps working through the namespace path Vaadin.Flow.internal.client.flow.binding.Binder.bind.
Port the script / stylesheet / dynamic-import / HTML-imports-readiness subsystem to TS. ResourceLoader and DependencyLoader move together so the internal listener plumbing (ResourceLoadListener with onLoad/onError) stays TS-to-TS; at the Java->TS boundary, @JsOverlay shims unwrap the listener into two @JsFunction callbacks (OnLoad, OnError) before crossing, sidestepping the GWT-OBF method-name mangling that affects TS calling methods on Java anonymous interface implementations. ResourceLoader's only Registry dependency (SystemErrorHandler::handleError for "could not load X" messages) is now passed directly to the constructor as an ErrorHandler @JsFunction. DependencyLoader takes URIResolver and ResourceLoader directly. Neither receives the Java Registry instance, which would have required calling Registry.getXxx() from TS — those Java instance- method names are mangled by GWT OBF. NativeResourceLoader is removed; its helpers (getStyleSheetLength, runScriptElement, etc.) are inlined into ResourceLoader.ts.
…ler to TS Port the communication tier (MessageHandler, MessageSender, ConnectionStateHandler interface, DefaultConnectionStateHandler) as one cluster so cyclic dependencies become TS-to-TS. The Java side keeps thin @jstype(isNative=true) facades preserving the public method names that ApplicationConnection, XhrConnection, AtmospherePushConnection, Heartbeat, and DefaultRegistry call by name. Cross-boundary Java->TS callbacks where TS needs to reach still-Java services (Registry.getXxx(), XhrConnection.send, etc.) are passed as @JsFunction lambdas at construction time through a @jstype(isNative=true, namespace=GLOBAL, name=Object) struct with explicit @JsProperty(name=...) setters. That setter-with-explicit-name pattern is the proven workaround for the GWT-OBF method-name mangling that affects Java instance methods and anonymous-class @jstype interface implementations.
…n, XhrConnection) to TS Port the AtmospherePushConnection and XhrConnection transports along with the vendored Xhr helper as one cluster. With MessageSender, MessageHandler, and ConnectionStateHandler already TS, the transports' cross-references become TS-to-TS. Java side keeps thin @jstype(isNative=true) facades so the public API (MessageSender's send/push paths, DefaultRegistry's wiring) keeps working through the JS namespace where method names are preserved. PushConnectionFactory's GWT.create() deferred-binding indirection is removed; DefaultRegistry now constructs AtmospherePushConnection directly through the MessageSenderCallbacks.createPushConnection lambda, since the single AtmospherePushConnection$Factory implementation has no other consumers. PushConnection itself becomes a native @jstype interface so the now-native AtmospherePushConnection can declare it as an implemented interface.
Port the last Java bootstrap pieces (Bootstrapper EntryPoint, ApplicationConnection orchestrator, Registry/DefaultRegistry DI container, JsoConfiguration view) to TypeScript. With the EntryPoint gone there is no longer anything to feed the GWT compile. Remove gwt-maven-plugin and the gwt-* dependencies from pom.xml, delete the ClientEngine.gwt.xml / ClientEngineXSI.gwt.xml module descriptors, the ClientEngineLinker single-script template and TrackingScheduler. The hand-written FlowClient.js entry point now drives Bootstrapper.initModule directly through the window.Vaadin.Flow.internal.client.bootstrap.Bootstrapper namespace that bridge.ts exposes. All migrated facade Java files (Console, Profiler, WidgetUtil, MessageHandler, StateTree, Reactive, ...) become unreferenced once ApplicationConnection / DefaultRegistry are gone and are deleted in the same commit. src/main/java ends up empty; the produced jar is content-only (META-INF/frontend/*.js).
The GWT-compiled Java is gone, so all the indirections that existed for it can go too: - Delete the window.Vaadin.Flow.internal.* bridge (bridge.ts + registry.ts) and the FlowClient.js wrapper. Flow.ts now imports Bootstrapper directly via dynamic import. The only remaining bridge read (ExecuteJavaScriptElementUtils -> UpdatableModelProperties) is replaced with a normal ES import. - Strip ResourceLoader's 5 "retained for legacy GWT call sites" statics (zero callers) and MessageSender's getResynchronizationStateName Java-enum alias. - Move BrowserInfo.checkForTouchDevice + isIos to module-local helpers and delete the unused getBrowserString static. - Shrink Profiler to just getRelativeTimeMillis / getRelativeTimeString; the rest were no-ops kept "so existing Java guards keep linking" and the guards are gone. Drop the now-dead Profiler.enter/leave/initialize callsites in Bootstrapper.ts and MessageHandler.ts. - Trim Bootstrapper public surface to initModule(); the getRunningApplications / getJsoConfiguration / startApplicationImmediately / deferStartApplication / registerCallback / vaadinBootstrapLoaded exports were only there for bridge consumers. - Sweep stale comments referencing @jstype native facades, @JsOverlay helpers, NativeX shims, JSNI bodies, and "Reached from GWT-compiled code" across 20+ files. Kept the GWT Scheduler.scheduleDeferred-style comments that explain why setTimeout(0) is used. - Drop gwt-unitCache/ from .gitignore. FlowTests.ts pre-bundling import updated to point at the new entry module; this also unblocked 25 previously failing tests (64 -> 89 passing). Net diff: -589/+70 across 31 files.
The previous cleanup commit ("drop GWT bridge + dead helpers
post-migration") deleted FlowClient.js because the only in-tree caller
(Flow.ts) had been switched to import Bootstrapper directly. That missed
an out-of-tree caller: flow-build-tools' TaskGenerateWebComponentBootstrap
generates a web-component bootstrap that does
import { init } from 'Frontend/generated/jar-resources/FlowClient.js';
init();
and flow-server's vite.generated.ts lists the same path. Both reference
the file by name in production builds, so FlowClient.js is part of the
public API of the flow-client jar.
Restore it. The new body skips the deleted window.Vaadin.Flow.internal
bridge lookup and statically imports Bootstrapper instead. FlowClient.d.ts
already declares the same `init` signature so no consumer change is
needed.
Caught by 15/15 it-tests failures on the PR build with
'[UNLOADABLE_DEPENDENCY] Could not load src/main/frontend/generated/jar-resources/FlowClient.js'.
Found by running test-eager-bootstrap locally and watching the browser
console. Each bug was a Java→TS porting miss where the new code retained
Java method-call syntax against a JS object that has no methods, or kept
a stale Java type assumption in a TS callback wrapper.
1. ConstantPool.importFromJson called `json.keys()` and `json.get(key)`
on a plain JS object (Java's `elemental.json.JsonObject.keys()` does
not exist on `Object`). Use `Object.keys(json)` and bracket access.
2. NodeMap / NodeList / MapProperty wired their ReactiveEventRouter wrap
callback to return a *function* `(event) => listener.onValueChange(event)`,
but the matching dispatchFn calls `listener.onPropertyAdd(event)` /
`listener.onSplice(event)` / `listener.onPropertyChange(event)`. The
stored listener has to be an object with the right method name (Java's
`wrap()` returned a functional-interface instance, which has the
method). Return `{ onPropertyAdd: ... }` / `{ onSplice: ... }` /
`{ onPropertyChange: ... }` so the dispatch finds the method.
3. addPropertyAddListener / addSpliceListener / addChangeListener accept
raw `(event) => void` lambdas (Java was a functional interface, the
TS port type-signature still allows it). Calls to dispatchFn then
crash because `function.onPropertyAdd` etc. don't exist. Normalize
the input: if it's a bare function, wrap it in the expected object
shape before storing.
4. ExecuteJavaScriptProcessor + URIResolver called Java-style
`appConfig.getApplicationId()`, `appConfig.isProductionMode()`,
`appConfig.getContextRootUrl()` on `ApplicationConfiguration`, which
was migrated as a plain-fields TS class (no methods). Switch to
field access (`applicationId`, `productionMode`, `contextRootUrl`)
and update the local `RegistryLike` types to match.
After these four fixes, test-eager-bootstrap renders RootView and
HelloView with 0 console errors.
`client.getProfilingData()` is contract-tested by ExportedJSFunctionIT.profilingInfoAvailableInDevelopmentMode, which asserts the array has exactly 5 entries: [lastProcessingTime, totalProcessingTime, serverTime0, serverTime1, bootstrapTime] The TS port pushed `this.serverTimingInfo` as a single entry (the raw object) when present, dropping to 4 total. Spread it into its two numeric slots like the Java original did.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



This is not intended to be merged