Skip to main content

ReactJS, NPM and Maven

I'm just starting to get into working with ReactJS, Facebook's open source rendering framework. My project uses SpringBoot for annotation-driven dependency injection and MVC. I thought it would be great if I could use a bit of ReactJS to enhance the application.

If you're looking for a basic conceptual intro, I recommend ReactJS for Stupid People and of course the official documentation is quite good. In full disclosure, I still have no idea how to do "flux" yet. As an experienced Java backend developer, I'm pretty decent at hacking Maven builds - which is precisely what this blog post is going to be about.

First, a word about how React likes to be built. Like many front-end tools, there is a toolkit for the node package manager (NPM). From the command prompt, one might run npm install -g react-tools which installs the jsx command. The jsx command provides the ability to transform JSX syntax into ordinary JavaScript, which is precisely what I want. Of course, running NPM at all implies that it has been installed and configured on the system.

Here are my goals for the build process:

  1. I want to precompile JSX. The templating capabilities of JSX are nifty, but since I have to build the backend code anyway, we might as well provide clean "regular JS" to the browser.
  2. I don't want to use a global installation of NPM. My build will eventually need to run on a continuous integration server (Jenkins) and I want to be a good neighbor to any other projects that might also use NPM with ReactJS.
  3. I want to play nice with Maven and avoid writing any shell scripts. Yuck!

Installing NPM

The first thing we need to do is to have the build install a copy of NPM that we can use for installing react tools. We want NPM binaries to be contained within the software project and, ideally, to be someplace where we won't trip over it and it won't get committed to version control by accident. Installing NPM as a subdirectory of "target" during the project initialization phase would be ideal.

Thanks to Google, I found a Maven plugin already available to do much of this. The frontend plugin makes short work of installing NPM. Here's the plugin setup:

<plugin>
    <groupId>com.github.eirslett</groupId>
    <artifactId>frontend-maven-plugin</artifactId>
    <version>0.0.24</version>
    <configuration>
        <workingDirectory>${pom.basedir}/target/node</workingDirectory>
        <installDirectory>${pom.basedir}/target/node</installDirectory>
    </configuration>
    <executions>
        <execution>
            <id>frontend-step1-install-npm</id>
            <goals>
                <goal>install-node-and-npm</goal>
            </goals>
            <phase>initialize</phase>
            <configuration>
                <nodeVersion>v0.12.7</nodeVersion>
                <npmVersion>2.11.3</npmVersion>
            </configuration>
        </execution>
        ...
</executions> </plugin>

The only gotcha is that the default location of the node stuff is in a "node" directory in the project base directory. I used the install directory argument to configure the build to put node binaries into "target/node" instead. Obviously this is a personal preference you can ignore in your projects.

Installing JSX

The next thing the build needs to accomplish is installing react tools, and of course we want to use the NPM we just installed to do that. We'll add this as a second execution of the frontend plugin:

<execution>
    <id>frontend-step2-install-jsx</id>
    <goals>
        <goal>npm</goal>
    </goals>
    <phase>initialize</phase>
    <configuration>
        <arguments>install -g react-tools</arguments>
    </configuration>
</execution>

Recall we've already configured the installation directory path as "target/node", and this execution will install the jsx binary as "target/node/bin/jsx". We use this jsx binary in the next section to compile our JSX files down to plain JavaScript.

Compile JSX

With the jsx binary installed, the build should now be able to generate the JavaScript files. Here's where the frontend plugin isn't useful - we need to reach into our Maven toolkit and find another plugin. Fortunately, Maven has an execution plugin that can invoke shell commands.

<plugin>
    <artifactId>exec-maven-plugin</artifactId>
    <groupId>org.codehaus.mojo</groupId>
    <executions>
        <execution>
            <id>frontend-step3-run-jsx</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>exec</goal>
            </goals>
            <configuration>
                <executable>${basedir}/target/node/bin/jsx</executable>
                <environmentVariables>
                    <PATH>${env.PATH}:${pom.basedir}/target/node/node</PATH>                </environmentVariables>
<arguments> <argument>--extension</argument> <argument>jsx</argument> <argument>${basedir}/src/main/resources/static/js</argument> <argument>${basedir}/target/classes/static/js</argument> </arguments> </configuration> </execution> </executions> </plugin>

Few things to note about this. First, the JSX compilation is in a different phase of the build from the NPM installation. The generate-sources phase is after the initialize phase, so we're guaranteed that all the NPM and JSX goodness is ready to use in the "target" directory tree.

The arguments are specified as an array. This may seem strange at first to developers who have not used the Java Runtime and ProcessBuilder classes, but under the hood the JVM thinks of shell commands as tokenized arrays. This means that we do not have to use quotes around each logical grouping, even if they contain whitespace (which here they don't).

The extension parameter setting of "jsx" means that only files ending in ".jsx" are going to be compiled. This produces ".js" files in the same location in the compiled code. Because we write them to the target directory, those files won't be included in version control or lie around where developers might get confused by having JS and JSX files together.

Summary

Using plugins, we were able to set up JSX compilation and meet our other goals. NPM is installed only in the "target" directory and therefore won't interfere with any other projects. Best of all, there was no shell scripting and only one execution of the jsx executable via the Maven execution plugin.

Here's the whole thing put together:

<plugins>
    <plugin>
        <groupId>com.github.eirslett</groupId>
        <artifactId>frontend-maven-plugin</artifactId>
        <version>0.0.24</version>
        <configuration>
            <workingDirectory>${pom.basedir}/target/node</workingDirectory>
        </configuration>
        <executions>
            <execution>
                <id>frontend-step1-install-npm</id>
                <goals>
                    <goal>install-node-and-npm</goal>
                </goals>
                <phase>initialize</phase>
                <configuration>
                    <nodeVersion>v0.12.7</nodeVersion>
                    <npmVersion>2.11.3</npmVersion>
                    <installDirectory>${pom.basedir}/target/node</installDirectory>
                </configuration>
            </execution>
            <execution>
                <id>frontend-step2-install-jsx</id>
                <goals>
                    <goal>npm</goal>
                </goals>
                <phase>initialize</phase>
                <configuration>
                    <arguments>install -g react-tools</arguments>
                    <installDirectory>${pom.basedir}/target/node</installDirectory>
                </configuration>
            </execution>
        </executions>
    </plugin>
    <plugin>
        <artifactId>exec-maven-plugin</artifactId>
        <groupId>org.codehaus.mojo</groupId>
        <executions>
            <execution>
                <id>frontend-step3-run-jsx</id>
                <phase>generate-sources</phase>
                <goals>
                    <goal>exec</goal>
                </goals>
                <environmentVariables>
                    <PATH>${env.PATH}:${pom.basedir}/target/node/node</PATH>                </environmentVariables>
<configuration> <executable>${basedir}/target/node/bin/jsx</executable> <arguments> <argument>--extension</argument> <argument>jsx</argument> <argument>${basedir}/src/main/resources/static/js</argument> <argument>${basedir}/target/classes/static/js</argument> </arguments> </configuration> </execution> </executions> </plugin>
    ...
</plugins>

Comments

  1. This is the kind of post I have been dreaming to find! Great work and thanks for sharing! I will try to get babel, web pack, etc install this way as well.

    ReplyDelete
  2. Looks a bit of yucky but it's a good try. I was thinking rather than tear your brains out trying to get ReactJS/Angular2 or any gulp, webpack based toolkit working with Maven. I found it was a bit saner to just keep it as a separate build process.

    In the end you're building something that should be decoupled anyway so why not?

    Another approach that you can incorporate to your Maven build system is to have a webjar building project and then include that jar as a dependency.

    ReplyDelete
    Replies
    1. Thanks for your comment. I prefer using vanilla build commands because they make packaging the project more intuitive to run. Running "mvn clean install" builds, tests, and packages the entire project. We run a continuous integration process -- using standard build commands means the configuration on Jenkins is very easy for our ops team.

      Delete
  3. I have tried with the attached plugin details in eclipse the URL showing an empty page can you please help me out to on this?

    ReplyDelete
  4. Enjoyed your approach to explaining how it works, hope to see more blog posts from you. thank you!

    eiffeltowerfacts
    Article submission sites

    ReplyDelete
  5. Actually I read it yesterday but I had some thoughts about it and today I wanted to read it again because it is very well written. keep update lot
    Ai & Artificial Intelligence Course in Chennai
    PHP Training in Chennai
    Ethical Hacking Course in Chennai Blue Prism Training in Chennai
    UiPath Training in Chennai

    ReplyDelete
  6. Great breakdown of IBM’s hiring process for software developers! As someone who recently transitioned into a tech role at IBM through their early-career program, I found your insights about the coding assessments spot-on. For aspiring candidates, I’ve shared a detailed guide on preparing for IBM’s technical interviews, including mock questions and role-specific tips: How I Cracked IBM’s Coding Round: A Fresher’s Guide. Keep up the fantastic work, HireBACE! 👏"

    ReplyDelete
  7. helleo https://yourwebsite.com/ibm-coding-prep eee

    ReplyDelete
  8. Thanks for your reply. Another thing you should mention in your reply and it is: You can use this hiring software developer

    ReplyDelete

Post a Comment

Popular posts from this blog

AWS S3 versus CloudFront Performance

Yesterday I took Amazon CloudFront for a spin. Creating the CloudFront distribution was pretty simple - the wizard process flowed nicely. I found myself relying on the help text in places, but the most surprising thing was how long it took for the distribution to become enabled. I didn't time it exactly, but I probably spent 45 minutes waiting for my new CloudFront distribution to change from "In Progress" to "Enabled" status. The performance is a bit confusing. Compared to the S3 bucket, I didn't see any improvement in performance in a few tries - in fact, the CloudFront CDN performance was worse than the S3 bucket on its own for my 217 KB image file. I decided to take a larger sample, loading the same image 30 times in Chrome and noting the timing data from the "network" tab in the developer tools. I'm located in Brooklyn, have CloudFront configured for the US/Europe with download mode configured. My S3 bucket is in the US Standard zone, w...

IntelliJ Annotations and Maven

IntelliJ has a code inspection feature that is designed to prevent null pointer exceptions based on static code analysis - actually a kind of interesting idea, and the folks over at IntelliJ have recommended that the annotations be included in the Java SDK in the future. I noticed this feature when I was cleaning up some code today and found a cryptic error message in the IntelliJ code inspection tool: Not annotated method overrides method annotated with @NotNull The community documentation for IntelliJ has a nice explanation of this feature , and there is a Maven repository available. The dependency for version 12 of IntelliJ is the following: <!-- STATIC CODE INSPECTIONS -->  <dependency> <groupId>com.intellij</groupId> <artifactId>annotations</artifactId> <version>12.0</version> </dependency> Adding the @NotNull annotation to my overridden method and to a parameter seems to have cleared up the issue. I'm ...