Skip to content

Commit

Permalink
React Renderer: use the resource resolver API to locate the server bu…
Browse files Browse the repository at this point in the history
…ndle. (#833)

This makes paths work as you'd expect in the dev environment.
  • Loading branch information
mikehearn authored Jul 6, 2024
1 parent f98179d commit 18eeab7
Show file tree
Hide file tree
Showing 8 changed files with 39 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ This Webpack config does several things:

* It polyfills APIs that lack a native implementation in the GraalJS engine.
* It ensures the output is a native Javascript module.
* It names the result `ssr-components.mjs` which is the only name Micronaut React SSR accepts. All components must be in one server side bundle currently.
* It names the result `ssr-components.mjs`. You can use any name, but it's looked for under this name in the `views` resource directory by default. All components must be in one server side bundle.
* It makes the `SERVER` variable be statically true when the Javascript is being bundled for server-side rendering. This allows you to include/exclude code blocks at bundle optimization time.
You can use such a config by running `npx webpack --mode production --config webpack.server.js`. Add the `--watch` flag if you want the bundle to be recreated whenever an input file changes. Micronaut React SSR will notice if the bundle file has changed on disk and reload it (see <<react-dev-mode,Development>>).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ React SSR needs some Micronaut application properties to be set.
[configuration]
----
micronaut:
# Point to client and server JS
# Point to the server-side JS. This value is the default.
views:
folder: classes/views
react:
server-bundle-path: "classpath:views/ssr-components.mjs"
router:
static-resources:
js:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,20 @@
package io.micronaut.views.react;

import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.io.ResourceResolver;
import io.micronaut.views.ViewsConfiguration;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import org.graalvm.polyglot.Source;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.file.Path;
import java.util.Optional;

import static java.lang.String.format;

Expand All @@ -34,21 +39,37 @@
@Singleton
@Internal
class JSBundlePaths {
// Source code file name, for JS stack traces.
final String bundleFileName;

// URL of bundle file, could be a file:// or in a classpath jar.
final URL bundleURL;

// If a file:// (during development), the path of that file. Used for hot reloads.
@Nullable
final Path bundlePath;

@Inject
JSBundlePaths(ViewsConfiguration viewsConfiguration, ReactViewsRendererConfiguration reactConfiguration) throws IOException {
var folder = viewsConfiguration.getFolder();
bundlePath = Path.of(folder).resolve(reactConfiguration.getServerBundlePath()).toAbsolutePath().normalize();
if (!Files.exists(bundlePath)) {
throw new FileNotFoundException(format("Server bundle %s could not be found. Check your %s property.", bundlePath, ReactViewsRendererConfiguration.PREFIX + ".server-bundle-path"));
JSBundlePaths(
ViewsConfiguration viewsConfiguration,
ReactViewsRendererConfiguration reactConfiguration,
ResourceResolver resolver
) throws IOException {
Optional<URL> bundlePathOpt = resolver.getResource(reactConfiguration.getServerBundlePath());
if (bundlePathOpt.isEmpty()) {
throw new FileNotFoundException(format("Server bundle %s could not be found. Check your %s property.", reactConfiguration.getServerBundlePath(), ReactViewsRendererConfiguration.PREFIX + ".server-bundle-path"));
}
bundleURL = bundlePathOpt.get();
bundleFileName = bundleURL.getFile();
if (bundleURL.getProtocol().equals("file")) {
bundlePath = Path.of(bundleURL.getPath());
} else {
bundlePath = null;
}
bundleFileName = bundlePath.getFileName().toString();
}

Source readServerBundle() throws IOException {
try (var reader = Files.newBufferedReader(bundlePath)) {
try (var reader = new BufferedReader(new InputStreamReader(bundleURL.openStream()))) {
return Source.newBuilder("js", reader, bundleFileName)
.mimeType("application/javascript+module")
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ synchronized void release(JSContext jsContext) {

@Override
public synchronized void onApplicationEvent(FileChangedEvent event) {
if (event.getPath().equals(paths.bundlePath) && event.getEventType() != WatchEventType.DELETE) {
if (paths.bundlePath != null && event.getPath().equals(paths.bundlePath) && event.getEventType() != WatchEventType.DELETE) {
LOG.info("Reloading Javascript bundle due to file change.");
versionCounter++;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public interface ReactViewsRendererConfiguration {
String DEFAULT_CLIENT_BUNDLE_URL = "/static/client.js";

/** The default value for {@link #getServerBundlePath()}. */
String DEFAULT_SERVER_BUNDLE_PATH = "ssr-components.mjs";
String DEFAULT_SERVER_BUNDLE_PATH = "classpath:views/ssr-components.mjs";

/** The default value for {@link #getRenderScript()}. */
String DEFAULT_RENDER_SCRIPT = "classpath:/io/micronaut/views/react/react.js";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ import spock.lang.Specification
* provide.
*/
@MicronautTest(startApplication = false)
@Property(name = "micronaut.views.folder", value = "src/test/resources/views")
@Property(name = "micronaut.views.react.client-bundle-url", value = "/static/client.preact.js")
@Property(name = "micronaut.views.react.server-bundle-path", value = "ssr-components.preact.mjs")
@Property(name = "micronaut.views.react.server-bundle-path", value = "classpath:views/ssr-components.preact.mjs")
@Property(name = "micronaut.views.react.render-script", value = "classpath:/io/micronaut/views/react/preact.js")
class PreactViewRenderSpec extends Specification {
@Inject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import jakarta.inject.Inject
import spock.lang.Specification

@MicronautTest(startApplication = false, rebuildContext = true)
@Property(name = "micronaut.views.folder", value = "src/test/resources/views")
@Property(name = "micronaut.views.react.server-bundle-path", value = "classpath:views/ssr-components.mjs")
class ReactViewRenderSpec extends Specification {
@Inject
ReactViewsRenderer<?> renderer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import spock.lang.FailsWith
import spock.lang.Specification

@MicronautTest(startApplication = false, rebuildContext = true)
@Property(name = "micronaut.views.folder", value = "src/test/resources/views")
@Property(name = "micronaut.views.react.server-bundle-path", value = "classpath:views/ssr-components.mjs")
@Property(name = "micronaut.views.react.sandbox", value = "true")
class SandboxReactRenderSpec extends Specification {
@Inject
Expand Down

0 comments on commit 18eeab7

Please sign in to comment.