Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(deps): update dependency com.google.template:soy to v2024 #702

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b61c4f8
fix(deps): update dependency io.micronaut.security:micronaut-security…
renovate[bot] Apr 22, 2024
43527f2
fix(deps): update dependency io.micronaut.logging:micronaut-logging-b…
renovate[bot] Apr 22, 2024
593e250
chore(deps): update gradle/gradle-build-action action to v3.3.1 (#780)
renovate[bot] Apr 22, 2024
65bfc7e
feat: add missing morph and refresh methods (#784)
sdelamo Apr 22, 2024
fce7b46
feat: Add a dataTurbo field to form and add test for fieldset TCK. (#…
wetted Apr 25, 2024
dd937bb
fix(deps): update micronaut to v4.4.6 (#789)
renovate[bot] Apr 25, 2024
213dfe3
chore(deps): update plugin io.micronaut.build.shared.settings to v6.7…
renovate[bot] Apr 25, 2024
600a626
chore(deps): update gradle/gradle-build-action action to v3.3.2 (#791)
renovate[bot] Apr 26, 2024
a61be63
[skip ci] Release v5.3.0
micronaut-build Apr 26, 2024
a58f747
chore: Bump version to 5.3.1-SNAPSHOT
micronaut-build Apr 26, 2024
a3c63ec
ci: projectVersion=5.4.0-SNAPSHOT [ci skip]
sdelamo Apr 26, 2024
6aeea9b
fix(deps): update dependency io.micronaut.gradle:micronaut-gradle-plu…
renovate[bot] Apr 30, 2024
bcddedb
fix(deps): update dependency io.micronaut.serde:micronaut-serde-bom t…
renovate[bot] Apr 30, 2024
10f5c48
chore(deps): update kotlin monorepo to v1.9.24 (#806)
renovate[bot] May 8, 2024
bc4628e
chore(deps): update plugin io.micronaut.build.shared.settings to v7 (…
renovate[bot] May 8, 2024
a6cfcfd
fix(deps): update dependency io.micronaut.validation:micronaut-valida…
renovate[bot] May 8, 2024
d85b8c4
Merge up 5.3.x into 5.4.x (#807)
timyates May 13, 2024
e6cd4b2
Feature: Support for React JS server side rendering (#770)
mikehearn May 27, 2024
16752f8
chore(deps): update managed.jte to v3.1.12 (#810)
renovate[bot] May 27, 2024
34d810d
fix(deps): update micronaut to v4.4.10 (#800)
renovate[bot] May 27, 2024
e4b375e
fix(deps): update dependency io.micronaut.sql:micronaut-sql-bom to v5…
renovate[bot] May 27, 2024
21471fe
fix(deps): update dependency io.micronaut.data:micronaut-data-bom to …
renovate[bot] May 27, 2024
52e70d3
Update common files (#798)
micronaut-build May 29, 2024
6437a17
fix(deps): update dependency com.google.template:soy to v2024
renovate[bot] May 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: "🔧 Setup Gradle"
uses: gradle/[email protected].0
uses: gradle/[email protected].1

- name: "❓ Optional setup step"
run: |
Expand Down
4 changes: 3 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
projectVersion=5.3.0-SNAPSHOT
projectVersion=5.4.0-SNAPSHOT
projectGroup=io.micronaut.views

title=Micronaut Views
Expand All @@ -18,6 +18,8 @@ testsviewsRocker=views-rocker/src/test
testsviewsPebble=views-pebble/src/test
testsviewsJte=views-jte/src/test
testsviewsJstachio=views-jstachio/src/test
srcjsReact=views-react/src/test/js
srcjsReactRender=views-react/src/main/resources/io/micronaut/views/react

org.gradle.caching=true
org.gradle.jvmargs=-Xmx1g
Expand Down
29 changes: 18 additions & 11 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
[versions]
micronaut = "4.4.4"
micronaut = "4.4.10"
micronaut-platform = "4.3.1"
micronaut-docs = '2.0.0'
micronaut-test = "4.0.1"
micronaut-data = "4.7.0"
micronaut-sql = "5.5.2"
micronaut-security = "4.6.9"
micronaut-serde = "2.8.2"
micronaut-validation = "4.4.4"
micronaut-gradle-plugin = "4.3.6"
micronaut-data = "4.7.1"
micronaut-sql = "5.6.0"
micronaut-security = "4.7.0"
micronaut-serde = "2.9.0"
micronaut-validation = "4.5.0"
micronaut-gradle-plugin = "4.3.8"
managed-freemarker = "2.3.32"
managed-handlebars = "4.3.1"
managed-jstachio = "1.3.5"
managed-jte = "3.1.10"
managed-jte = "3.1.12"
managed-rocker = "1.4.0"
managed-soy = "2023-09-13"
managed-soy = "2024-02-26"
managed-thymeleaf = "3.1.2.RELEASE"
managed-velocity = "2.3"

graal = "24.0.0"

pebble = "3.2.2"
thymeleaf-extra-java8time = "3.0.4.RELEASE"
kotlin = "1.9.23"
kotlin = "1.9.24"
kotlinx-coroutines = "1.8.0"

micronaut-logging = "1.2.3"
micronaut-logging = "1.3.0"

[libraries]
# Core
Expand Down Expand Up @@ -57,6 +59,11 @@ kotlin-stdlib-jdk8 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", versi
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
groovy-json = { module = "org.apache.groovy:groovy-json" }

graal-polyglot = { module = "org.graalvm.polyglot:polyglot", version.ref = "graal" }
graal-js = { module = "org.graalvm.polyglot:js", version.ref = "graal" }

jetbrains-annotations = { module = "org.jetbrains:annotations", version = "24.1.0" }

[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" }
Expand Down
3 changes: 2 additions & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pluginManagement {
}

plugins {
id("io.micronaut.build.shared.settings") version "6.7.0"
id("io.micronaut.build.shared.settings") version "7.0.1"
}

enableFeaturePreview 'TYPESAFE_PROJECT_ACCESSORS'
Expand All @@ -23,6 +23,7 @@ include 'views-handlebars'
include 'views-thymeleaf'
include 'views-htmx'
include 'views-velocity'
include 'views-react'
include 'views-rocker'
include 'views-pebble'
include 'views-jte'
Expand Down
9 changes: 9 additions & 0 deletions src/main/docs/guide/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ views:
title: JStachio
jstachioInstallation: JStachio Installation
jstachioExample: JStachio Example
react:
title: React SSR
reactpreparingjs: Preparing your Javascript
reactsettingproperties: Setting serving properties
preact: Integrating with Preact
reactrenderscripts:
title: Render scripts
reactheadmanagers: Using head managers
reacttodo: Known limitations
model:
title: Working with Models
custom: Dynamically Enriching Models
Expand Down
4 changes: 2 additions & 2 deletions src/main/docs/guide/views/htmx/outOfBandSwaps.adoc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
You can return an API:views.htmx.HtmxResponse[] in a controller method to render multiple views in a single HTMX response—for example, to do https://htmx.org/docs/#oob_swaps[Out Of Band Swaps].
You can return an api:views.htmx.http.HtmxResponse[] in a controller method to render multiple views in a single HTMX response—for example, to do https://htmx.org/docs/#oob_swaps[Out Of Band Swaps].

snippet::io.micronaut.views.docs.htmx.HtmxTest[tags="outOfBandSwaps",indent=0]
snippet::io.micronaut.views.docs.htmx.HtmxTest[tags="outOfBandSwaps",indent=0]
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ Additionally, you need to add JStachio's Java annotation processor.

dependency:jstachio-apt[groupId="io.jstach",scope="annotationProcessor"]

NOTE: For Kotlin, add the `jstachio-apt` dependency in https://docs.micronaut.io/4.4.3/guide/#kaptOrKsp[kapt or ksp scope], and for Groovy add `jstachio-apt` in compileOnly scope.

Read the https://jstach.io/doc/jstachio/1.2.1/apidocs/#installation[Jstachio's user guide] to learn more.
39 changes: 39 additions & 0 deletions src/main/docs/guide/views/templates/react.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
React server-side rendering (SSR) allows you to pre-render React components to HTML before the page is sent to the user.
This improves performance by ensuring the page appears before any Javascript has loaded (albeit in a non-responsive
state) and makes it easier for search engines to index your pages.

NOTE: This module is experimental and subject to change.

Micronaut's support for React SSR has the following useful features:

* Javascript runs using https://www.graalvm.org/[GraalJS], a high performance Javascript engine native to the JVM. Make sure to run your app on GraalVM or by compiling it to a native image to get full Javascript performance.
* Compatible out of the box with both React and https://www.preactjs.com/[Preact], an alternative lighter weight implementation of the React concept.
* Customize the Javascript used to invoke SSR to add features like head managers, or use the prepackaged default scripts to get going straight away.
* The Javascript can be sandboxed, ensuring that your server environment is protected from possible supply chain attacks.
* You can pass any `@Introspectable` Java objects to use as _props_ for your page components. This is convenient for passing in things like the user profile info.
* Logging from Javascript is sent to the Micronaut logs. `console.log` and related will go to the `INFO` level of the logger named `js`, `console.error` and Javascript exceptions will go to the `ERROR` level of the same.

To use React SSR you need to add two dependencies.

1. Add the `micronaut-views-react` dependency.
2. Add a dependency on `org.graalvm.polyglot:js` or `org.graalvm.polyglot:js-community`. The difference is to do with licensing and performance, with the `js` version being faster and free to use but not open source. https://www.graalvm.org/latest/docs/introduction/#licensing-and-support[Learn more about choosing an edition.]

dependency:micronaut-views-react[groupId="io.micronaut.views"]

The properties used can be customized by overriding the values of:

include::{includedir}configurationProperties/io.micronaut.views.react.ReactViewsRendererConfiguration.adoc[]

== How it fits together

Props can be supplied in the form of an introspectable bean or a `Map<String, Object>`. Both forms will be serialized to JSON and sent to the client for hydration, as well as used to render the root component. The URL of the current page will be taken from the request and added to the props under the `url` key, which is useful when working with libraries like https://github.com/preactjs/preact-router[`preact-router`]. If you use `Map<String, Object>` as your model type and use Micronaut Security, authenticated usernames and other security info will be added to your props automatically.

By default you will need React components that return the entire page, including the `<html>` tag. You'll also need to prepare your Javascript (see below). Then just name your required page component in the `@View` annotation on a controller, for example `@View("App")` will render the `<App/>` component with your page props.

If your page components don't render the whole page or you need better control over how the framework is invoked you can use _render scripts_ (see below).

== Sandbox

By default Javascript executing server side runs with the same privilege level as the server itself. This is similar to the Node security model, but exposes you to supply chain attacks. If a third party React component you depend on turns out to be malicious or simply buggy, it could allow an attacker to run code server side instead of only inside the browser sandbox.

Normally with React SSR you can't do much about this, but with Micronaut Views React you can enable a server-side sandbox if you use GraalVM 24.1 or higher. This prevents Javascript from accessing any Java host objects that haven't been specifically marked as accessible to the sandbox. To use this set `micronaut.views.react.sandbox` to true in your `application.properties`, and then ensure that any objects you use as props have their property getters annotated with `@org.graalvm.polyglot.HostAccess.Export`. If there are properties that happen to be on your beans that should _not_ be exposed to Javascript, just don't annotate them. Any properties not annotated will simply be invisible from inside the sandbox.
38 changes: 38 additions & 0 deletions src/main/docs/guide/views/templates/react/preact.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
The https://www.preactjs.com/[Preact] library is a smaller and lighter weight implementation of React, with a few nice enhancements as well. Like React it also supports server side rendering and can be used with Micronaut React SSR. It requires some small changes to how you prepare your Javascript. Please read and understand how to prepare your JS for regular React first, as this section only covers the differences.

Your `server.js` should look like this:

[source,javascript]
.src/main/js/server.js
----
include::{srcjsReact}/server.preact.js[]
----

Notice the differences: we're re-exporting the `h` symbol from Preact (which it uses instead of `React.createComponent`) and `renderToString` from the separate `preact-render-to-string` module. Otherwise the script is the same: we have to export each page component.

Your `client.js` should look like this:

[source,javascript]
.src/main/js/client.js
----
include::{srcjsReact}/client.preact.js[]
----

Finally, you need to tell Micronaut Views React to use a different render script (see below). Set the `micronaut.views.react.render-script` application property to be `classpath:/io/micronaut/views/react/preact.js`.

That's it. If you want to use existing React components then you will also need to set up aliases in your `webpack.{client,server}.js` files like this:

[source,javascript]
----
module.exports = {
// ... existing values
resolve: {
alias: {
"react": "preact/compat",
"react-dom/test-utils": "preact/test-utils",
"react-dom": "preact/compat", // Must be below test-utils
"react/jsx-runtime": "preact/jsx-runtime"
},
}
}
----
40 changes: 40 additions & 0 deletions src/main/docs/guide/views/templates/react/reactheadmanagers.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
Head managers are libraries that let you build up the contents of your `<head>` block as your `<body>` renders. One use of custom render scripts is to integrate a head manager with your code. Here's an example of a simple render script that usees the https://github.com/nfl/react-helmet[React Helmet] library in this way. Remember to export `Helmet` from your server-side bundle.

[source,javascript]
----
export async function ssr(component, props, callback, config) {
// Create the vdom.
const element = React.createElement(component, props, null);
// Render the given component, expecting it to fill a <div id="content"></div> in the <body> tag.
const body = ReactDOMServer.renderToString(element)
// Get the data that should populate the <head> from the Helmet library.
const helmet = Helmet.renderStatic();
// Data to be passed to the browser after the main HTML has finished loading.
const boot = {
rootProps: props,
rootComponent: component.name,
};

// Assemble the HTML.
const html = `
<!doctype html>
<html ${helmet.htmlAttributes.toString()}>
<head>
${helmet.title.toString()}
${helmet.meta.toString()}
${helmet.link.toString()}
</head>
<body ${helmet.bodyAttributes.toString()}>
<div id="content">
${body}
</div>

<script>var Micronaut = ${JSON.stringify(boot)};</script>
<script type="text/javascript" src="${config.getClientBundleURL()}" async="true"></script>
</body>
</html>`;

// Send it back.
callback.write(html);
}
----
53 changes: 53 additions & 0 deletions src/main/docs/guide/views/templates/react/reactpreparingjs.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
An app that uses SSR needs the React components to be bundled twice, once for the client and once for the server. For the server you need to make a Javascript module bundle that imports and then re-exports the (page) components you will render, along with `React` and `ReactServerDOM`. The bundle must be compatible with GraalJS, which is not NodeJS and thus doesn't support the same set of APIs. You will also need to create a client-side bundle as per usual, and change how you start up React.

TIP: This tutorial doesn't take you through how to create a ReactJS project from scratch - please refer to the React documentation for that.

To start we will need a `server.js` file. It should be a part of your frontend project and can be named and placed wherever you like, as the server will only need the final compiled bundle. Your `server.js` should look like this:

[source,javascript]
.src/main/js/server.js
----
include::{srcjsReact}/server.js[]
----

Add your page components as imports, and then also to the export line. We will now set up Webpack to turn this file into a bundle.

1. Run `npm i webpack node-polyfill-webpack-plugin text-encoding` to install some extra packages that are needed.
2. Create a config file called e.g. `webpack.server.js` like the following:

[source,javascript]
.src/main/js/webpack.server.js
----
include::{srcjsReact}/webpack.server.js[]
----

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 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>>).

Now create `client.js`. This will contain the Javascript that runs once the page is loaded, and which will "hydrate" the React app (reconnect the event handlers to the pre-existing DOM). It should look like this:

[source,javascript]
.src/main/js/client.js
----
include::{srcjsReact}/client.js[]
----

Depending on how you configure minification, you may also need to import your page components here. This small snippet of code reads the `Micronaut` object which is generated by the Micronaut React SSR renderer just before your `client.js` code is loaded. It contains the component named in your `@View("MyPageComponent")` annotation, which is then loaded assuming it is in a Javascript module of the same name. The props that will be passed to that page component as generated from the object you return from your controller method. If you wish you can wrap `<PageComponent/>` here with any contexts you need.

And now for the `webpack.client.js` config:

[source,javascript]
.src/main/js/webpack.client.js
----
include::{srcjsReact}/webpack.client.js[]
----

It tells Webpack to generate a series of JS files that are then placed in the `src/main/resources/static` directory.

Run Webpack to generate the needed Javascript files for both client and server.
40 changes: 40 additions & 0 deletions src/main/docs/guide/views/templates/react/reactrenderscripts.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
The code that kicks off the SSR process using your React libraries API is called a render script. Micronaut Views React ships with two pre-packaged render scripts, one for ReactJS and one for Preact, but you are also able to supply your own. This lets you take complete control over the server-side Javascript. To use a custom script, place it somewhere on your classpath or file system and then set the `micronaut.views.react.render-script` property to its path, prefixed with either `classpath:` or `file:` depending on where it should be found.

A render script should be an ESM module that exports a single function called `ssr` that takes four arguments:

1. A function object for the page component to render.
2. An object containing the root props.
3. A callback object that contains APIs used to communicate with Micronaut.
4. A string that receives the URL of the bundle that the browser should load. This is specified by the `micronaut.views.react.clientBundleURL` application property.

The default render script looks like this:

[source,javascript]
.classpath:/io/micronaut/views/react/react.js
----
include::{srcjsReactRender}/react.js[]
----

The default render script for Preact looks like this:

[source,javascript]
.classpath:/io/micronaut/views/react/preact.js
----
include::{srcjsReactRender}/preact.js[]
----

A more sophisticated render script might support the use of head managers (see below), do multiple renders, expose other APIs and so on.

A render script is evaluated _after_ your server side bundle, and has access to any symbols your server script exported. If you wish to access a JS module you should therefore include it in your `server.js` that gets fed to Webpack or similar bundler, and then re-export it like this:

[source,javascript]
----
import * as mymod from 'mymod';
export { mymod };
----

The callback object has a few different APIs you can use:

1. `write(string)`: Writes the given string to the network response.
2. `write(bytes)`: Writes the given array of bytes to the network response.
3. `url()`: Returns either null or a string containing the URL of the page being served. Useful for sending to page routers.
Loading
Loading