Skip to content

Commit

Permalink
fix: HMR for translation files
Browse files Browse the repository at this point in the history
* Clears resource bundle cache on translation resource redeployment
* Fires a HMR event to the browser when translations are reloaded

Limitation: Only supports DefaultI18NHandler where the paths are known. If you have a custom I18N handler, you might need a custom HMR supporting HotswapListener.

For #20118
Requires vaadin/hilla#2795 to fully fix the issue
  • Loading branch information
Artur- committed Oct 4, 2024
1 parent 4cf08cf commit de04118
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 8 deletions.
27 changes: 25 additions & 2 deletions flow-server/src/main/java/com/vaadin/flow/hotswap/Hotswapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
Expand All @@ -51,6 +52,9 @@
import com.vaadin.flow.server.VaadinService;
import com.vaadin.flow.server.VaadinSession;

import elemental.json.Json;
import elemental.json.JsonObject;

/**
* Entry point for application classes hot reloads.
* <p>
Expand Down Expand Up @@ -165,13 +169,32 @@ public void onHotswap(URI[] createdResources, URI[] modifiedResources,
"Hotswap resources change event ignored because VaadinService has been destroyed.");
return;
}
// no-op for the moment, just logging for debugging purpose
// entry point for future implementations, like reloading I18n provider
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(
"Created resources: {}, modified resources: {}, deletedResources: {}.",
createdResources, modifiedResources, deletedResources);
}

if (anyMatches(".*/vaadin-i18n/.*\\.properties", createdResources,
modifiedResources, deletedResources)) {
// Clear resource bundle cache so that translations (and other
// resources) are reloaded
ResourceBundle.clearCache();
// Trigger any potential Hilla translation updates
liveReload.sendHmrEvent("translations-update", Json.createObject());
}

}

private boolean anyMatches(String regexp, URI[]... resources) {
for (URI[] uris : resources) {
for (URI uri : uris) {
if (uri.toString().matches(regexp)) {
return true;
}
}
}
return false;
}

private void onHotswapInternal(HashSet<Class<?>> classes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@

import org.atmosphere.cpr.AtmosphereResource;

import com.vaadin.flow.component.UI;
import com.vaadin.flow.server.communication.FragmentedMessageHolder;

import elemental.json.JsonObject;

/**
* Provides a way to reload browser tabs via web socket connection passed as a
* {@link AtmosphereResource}.
Expand Down Expand Up @@ -117,4 +118,14 @@ default void refresh(boolean refreshLayouts) {
*/
void onMessage(AtmosphereResource resource, String msg);

/**
* Send a client side HMR event.
*
* @param event
* the event name
* @param eventData
* the event data
*/
void sendHmrEvent(String event, JsonObject eventData);

}
2 changes: 1 addition & 1 deletion vaadin-dev-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"@web/dev-server-esbuild": "^0.3.3",
"prettier": "^2.8.4",
"tslib": "^2.5.3",
"vite": "^4.1.4"
"vite": "^5.4.8"
},
"dependencies": {
"construct-style-sheets-polyfill": "^3.1.0",
Expand Down
16 changes: 15 additions & 1 deletion vaadin-dev-server/src/main/frontend/vaadin-dev-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ type DevToolsConf = {
liveReloadPort: number;
token?: string;
};

// @ts-ignore
const hmrClient: any = import.meta.hot ? import.meta.hot.hmrClient : undefined;

@customElement('vaadin-dev-tools')
export class VaadinDevTools extends LitElement {
unhandledMessages: ServerMessage[] = [];
Expand Down Expand Up @@ -711,12 +715,22 @@ export class VaadinDevTools extends LitElement {
}
handleFrontendMessage(message: ServerMessage) {
if (message.command === 'featureFlags') {
} else if (handleLicenseMessage(message)) {
} else if (handleLicenseMessage(message) || this.handleHmrMessage(message)) {
} else {
this.unhandledMessages.push(message);
}
}

handleHmrMessage(message: ServerMessage): boolean {
if (message.command !== 'hmr') {
return false;
}
if (hmrClient) {
hmrClient.notifyListeners(message.data.event, message.data.eventData);
}
return true;
}

getDedicatedWebSocketUrl(): string | undefined {
function getAbsoluteUrl(relative: string) {
// Use innerHTML to obtain an absolute URL
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,4 +381,15 @@ public void clearFragmentedMessage(AtmosphereResource resource) {
resources.put(ref, new FragmentedMessage());
}

@Override
public void sendHmrEvent(String event, JsonObject eventData) {
JsonObject msg = Json.createObject();
msg.put("command", "hmr");
JsonObject data = Json.createObject();
msg.put("data", data);
data.put("event", event);
data.put("eventData", eventData);
broadcast(msg);
}

}
6 changes: 3 additions & 3 deletions vaadin-dev-server/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { fileURLToPath } from 'url';
import { defineConfig } from 'vite';
import typescript from '@rollup/plugin-typescript';

const { execSync } = require('child_process');

export default defineConfig({
build: {
// Write output to resources to include it in Maven package
Expand All @@ -27,5 +25,7 @@ export default defineConfig({
/^@vaadin.*/,
]
}
}
},
// Preserve import.meta.hot in the built file so it can be replaced in the application instead
define: { 'import.meta.hot': 'import.meta.hot' }
});

0 comments on commit de04118

Please sign in to comment.