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

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant