Skip to content
New issue

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

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

Already on GitHub? # to your account

Live-reload Dev Server for j2cl:watch #63

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

AugustNagro
Copy link
Contributor

@AugustNagro AugustNagro commented Jan 3, 2021

Happy new year J2CL!

This PR enhances j2cl:watch's incremental recompilation with a live-reloading dev-server. The server serves resources from the configured webappsDirectory for an artifact. When an incremental recompilation is triggered, or a resource in src/main/webapp is changed, j2cl:watch triggers a reload in connected browser sessions.

A video of the process in action is available here, and I've made a public example. I've also tested this feature on a multi-module, large SPA I've been working on.

Non Goals

It is not a goal of this PR to implement hot-swapping. Besides greater complexity, hot-swapping requires knowledge about the application to hot-swap (which is why it's typically a feature in frameworks, not build tools). Finally, hot-swapping allows applications to shirk the responsibility of handling location.reload() events in a graceful manner.

Because of these reasons, the vast majority of popular Javascript dev-servers implement live-reloading, and not hot-swapping. Examples include Facebook's react-scripts, es-dev-server and its successor @web/dev-server, webpack-dev-server, and even the (bare bones) j2cl Bazel plugin.

Configuration

The WatchMojo has four new configurable parameters:

    /**
     * Enable the live-reloading dev server
     */
    @Parameter(defaultValue = "true", property = "devServerEnable")
    protected boolean devServerEnable;

    /**
     * Port for the dev server to operate
     */
    @Parameter(defaultValue = "8085", property = "devServerPort")
    protected int devServerPort;

    /**
     * The 'main' artifact-id for this project that has the index.html
     * and other sources to host. If not configured, we try to pick the
     * first artifact with a `src/main/webapp/index.html`, defaulting
     * to {@link #webappDirectory}.
     */
    @Parameter(property = "devServerRootArtifactId")
    protected String devServerRootArtifactId;

    /**
     * The base href from which your application will be deployed
     * (and therefore, should be tested on). For example, if you will deploy
     * your app to myserver.com/my-app/, set devServerBaseHref=/my-app.
     * This way, requested resources will be served correctly. The default
     * value is '/'.
     * <p>
     * Note that using the {@code <base>} tag in index.html is a best practice
     * to allow relative hrefs.
     */
    @Parameter(defaultValue = "/", property = "devServerBaseHref")
    protected String devServerBaseHref;

Dependent Changes

The first commit improves the integration tests, which were configured without specifying the Java or maven-war-plugin versions.

The second commit harmonizes j2cl:build and j2cl:watch outputs by changing j2cl:build's initialScriptFilename to ${artifactId}.js.

Now that :watch copies resources like index.html to ${webappsDirectory}/${artifactId}/,
it was not clear which <script> src to use for the output js
(:watch needs src="/${artifactId}.js" to resolve, and :build
needs src="/${artifactId}/${artifactId}.js").

By setting :build's initialScriptFilename = ${artifactId}.js by default,
index.html can simply use <script src="/${artifactId}.js"></script> without
confusion.

WatchService Enhancements

One problem I know @mdproctor has experienced is that incremental reloads are very slow on MacOS. I have encountered this problem before, and it stems from the JDK using a polling implementation on that platform.

If you look here, you will see that I am registering Paths with the SensitivityWatchEventModifier.HIGH Modifier.

I booted up my old macbook, and can confirm that after the first compilation, performance dramatically improves and is comparable to my Linux desktop.

j2cl:watch Timeout

Instead of timing out after 30 seconds, j2cl:watch now exits after some System.in input.

Version Bump

Note that the version has been bumped to 0.17-SNAPSHOT when testing.

Implementation Details

The core addition is tools/DevServer.java.

I am quite happy with the features afforded in under 600 commented lines:

  • Performance is quite fast
  • Reloads are never done in unsafe build states (see use of Phaser),
  • Large resources and bundle.js files are cached by the browser, typically in-memory.
  • SPA routing is handled correctly
  • Source-maps debugging work great, no matter the subpath!
  • No dependencies added

Future Changes

In a future Issue, I would like to rename parameter webappDirectory to j2clBuildDirectory. This is much more clear, since the parameter isn't used to locate src/main/webapp, but rather the build output folder.

This change harmonizes j2cl:build and j2cl:watch outputs.

Now that :watch copies resources like index.html to ${webappsDirectory}/${artifactId}/,
it's not clear which <script> src to use for the output js
(:watch needs src="/${artifactId}.js" to resolve, and :build
needs src="/${artifactId}/${artifactId}.js").

By setting :build's initialScriptFilename = ${artifactId}.js by default,
index.html can simply use <script src="/${artifactId}.js"></script> without
confusion.
@treblereel treblereel requested a review from niloc132 April 20, 2022 06:57
@18106339109
Copy link

Hello, the content in the example does not seem to match the content in the video you provided. I wonder how the J2cl.js output from j2cl:watch is applied or referenced by index.html?
image
Uploading QQ截图20241022092229.jpg…

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants