micronaut-projects / micronaut-views

Micronaut Integration with Server Side View Rendering
Apache License 2.0
30 stars 32 forks source link

Feature: Support for React JS server side rendering #770

Closed mikehearn closed 4 months ago

mikehearn commented 5 months ago

This work in progress PR adds support for rendering ReactJS views server side. It sports:

To do list:

CLAassistant commented 5 months ago

CLA assistant check
All committers have signed the CLA.

mikehearn commented 5 months ago

I've been talking with @sgammon about maybe using the code he's got in Elide for this PR. It would mean adding some new MIT licensed dependencies to the React Renderer module. In turn his code depends on the Kotlin standard library and GraalJS.

The benefits would be that his team has implemented some of the core Web/Node APIs on top of Micronaut already, for example the fetch API. The current PR provides a very MVP/prototype implementation of server side fetching. It's super basic, not parallelized at all, basically expects you to do overrides and hot patches to your code in order to use it. Implementing the fetch API and others would improve compatibility with existing JS, especially for larger projects. Doing this well though is a lot of work, so it makes sense to reuse Sam's effort.

I'll park this work for a week or two, to let Sam work out if/when he'd be able to do this effort. If we can just add a few deps and use them, I'd rather add it all into this one PR. If Sam won't have time, then I might take server-side fetch out of this PR and then look at adding it back in later after extracting the code myself, but that would depend on user demand.

sgammon commented 5 months ago

I should be able to review and get back to this within a day or two

mikehearn commented 5 months ago

I've removed the half-baked server side fetch support for now, as that's the part that would change the most given Sam's work. The remaining tasks are small and can hopefully be finished soon. Then it should be ready to merge.

mikehearn commented 5 months ago

@graemerocher I've done a few improvements in the last couple of commits, including to the docs and I also now expose the request URL to the Javascript (useful for page router components).

W.R.T. using ResourceLoader, what's the right way to do it with this API for:

  1. Reloading resources automatically if they change on disk, but without polling on every request.
  2. Taking into account viewsConfiguration.getFolder() as the base?
graemerocher commented 5 months ago

You can probably use a scheduled job to check for changes and reload I imagine

graemerocher commented 5 months ago

Actually I think we have a file watch API already. You need to configure the watch paths (see FileWatchConfiguration), then you can write a @EventListener for FileChangedEvent:

@EventListener
void filesChanged(FileChangedEvent event) {

}
mikehearn commented 4 months ago

File watching support using the standard API is added, though using it requires disabling the build plugin auto-restart behavior of course, as well as configuring it via extra application.properties which the old solution didn't need.

graemerocher commented 4 months ago

you could disable (or document how to disable) restart. See https://docs.micronaut.io/latest/api/io/micronaut/scheduling/io/watch/FileWatchConfiguration.html#setRestart(boolean)

mikehearn commented 4 months ago

@graemerocher Done, thanks. Marked this PR as ready for review.

mikehearn commented 4 months ago

CI failed due to:

Execution failed for task ':micronaut-views-react:findBaseline'.
> Could not find a previous version for 5.4.0

I guess that's expected?

graemerocher commented 4 months ago

yes it is expected our builds have binary compatibility verification to ensure we don't break public API from the previous version. In this case this is a new module so there is no previous version so you need to add in the Gradle build of any new modules:

micronautBuild {
    binaryCompatibility.enabled.set(false)
}
mikehearn commented 4 months ago

Now the CI failure is a bit harder to fix. I am assuming the very latest version of GraalVM, but it seems that the CI setup provides a GraalVM already which is older (17 instead of 21). Locally my tests pass, presumably because I'm using the newer GraalVM that unbundles the truffle languages. Changing the GraalVM build used by CI for the whole project might be a separate PR or project though.

graemerocher commented 4 months ago

we have a 21 CI as well in the matrix, can you exclude the tests for 17?

mikehearn commented 4 months ago

I just noticed a new problem. Some rebase has brought in a change where the blocking executor is now using virtual threads (loom) by default. Unfortunately Truffle is currently incompatible with that. It's being worked on. I will update the PR to explain this in the docs and ask users to add a new executor for js instead of using the regular blocking executor. It's not very nice, but like the restriction on using the sandbox, I hope that this will go away with new versions and the instructions can then be simplified.

graemerocher commented 4 months ago

you could always define a separate executor for JS view rendering.

mikehearn commented 4 months ago

I updated the docs to tell users to just disable Loom for the blocking executor. It's only a temporary limitation and should not be a big deal.

I started writing a guide, but it seems the infrastructure has a lot of dependencies on the feature being actually published. So I will go back to that after there's been a release with this in it, as then dependency resolution etc will work.

Do I need to update the starter system for the new feature or does that happen automatically?

graemerocher commented 4 months ago

no starter will be need to be updated