Javascript Compression in Nexus


November 9, 2009 By Damian Bradicich

The Nexus UI uses Ext JS, a very powerful javascript library. The ext javascript library is a fairly large file on its own (even though it is an aggregation itself of all extjs library files, and compressed), not to mention all of the javascript files that we introduced to build the Nexus UI.  In total, we are working with roughly 3-4mb of javascript and css files.  As you can imagine, the first time you crank up your browser to access Nexus, it can take a long period of time to download all these resources.  As the UI is the user’s perspective on Nexus, this just needed to change.  So we decided to compress our resources, and did this using the YUI Compressor Maven Plugin.

Using this plugin, we were able to take our 30-40 javascript files (plus the monolithic ext-all.js file) and 4-5 css files, and aggregate them into 2 files, sonatype-all.js and sonatype-all.css.  The total size now less that 1mb (including the ext-all.js file) for both.  So not only have we made the content size 3-4 times smaller, but the number of requests has been cut 20 fold (or more!!).  This makes the UI load blazingly fast.

So I just wanted to get the word out, about how handy this maven plugin is, and how easy it is to use.  We only use the aggregation and compression offered by the plugin, but it offers a lot more than I can cover here. You can browse their website to get more details.

So here is the plugin configuration in our nexus-webapp pom.xml file

<plugin>
  <groupId>net.sf.alchim</groupId>
  <artifactId>yuicompressor-maven-plugin</artifactId>
  <executions>
    <execution>
      <id>jsfiles</id>
      <goals>
        <goal>compress</goal>
      </goals>
      <configuration>
        <aggregations>
          <aggregation>
            <!-- the ultimate javascript file that is served up to the browser -->
            <output>${compressed-dir}/js/sonatype-all.js</output>
            <!-- files to include, path relative to output's directory or absolute path-->
            <includes>
              <include>${basedir}/src/main/webapp/ext-2.2/adapter/ext/ext-base.js</include>
              <include>${basedir}/src/main/webapp/ext-2.2/ext-all.js</include>
              <include>${compressed-dir}/js/extensions/ext-override.js</include>
              <include>${compressed-dir}/js/filetree/js/Ext.ux.form.BrowseButton.js</include>
              <include>${compressed-dir}/js/Sonatype.js</include>
              <include>${compressed-dir}/js/extensions/Ext.messagebox.js</include>
              <include>${compressed-dir}/js/extensions/Ext.form.js</include>
              <include>${compressed-dir}/js/extensions/SearchField.js</include>
              <include>${compressed-dir}/js/extensions/Sonatype.ext.FeedGrid.js</include>
              <include>${compressed-dir}/js/extensions/Ext.tree.js</include>
              <include>${compressed-dir}/js/extensions/MultiSelectTree.js</include>
              <include>${compressed-dir}/js/extensions/TwinPanelController.js</include>
              <include>${compressed-dir}/js/extensions/Sonatype.events.js</include>
              <include>${compressed-dir}/js/extensions/Sonatype.navigation.js</include>
              <include>${compressed-dir}/js/extensions/Sonatype.panels.js</include>
              <include>${compressed-dir}/js/Sonatype.utils.js</include>
              <include>${compressed-dir}/js/Sonatype.config.js</include>
              <include>${compressed-dir}/js/Sonatype.lib.js</include>
              <include>${compressed-dir}/js/Sonatype.resources.js</include>
              <include>${compressed-dir}/js/Sonatype.headLinks.js</include>
              <include>${compressed-dir}/js/Sonatype.view.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.referenceData.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.resources.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.FeedViewPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.ArtifactInformationPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.ArtifactContainer.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.SearchResultGrid.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.SearchPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.LogsViewPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.ServerEditPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.RoutesEditPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.SchedulesEditPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.UserEditPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.RoleEditPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.PrivilegeEditPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.AbstractRepoPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.FileUploadPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.RepoMaintPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.IndexBrowserPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.RepositoryBrowserContainer.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.RepoEditPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.GroupsEditPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.RepoServer.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.RepoTargetEditPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.helpAbout.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.LogEditPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.MirrorConfigPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.UserBrowserPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.UserPrivilegeBrowserPanel.js</include>
              <include>${compressed-dir}/js/repoServer/repoServer.RepoSummaryPanel.js</include>
            </includes>
          </aggregation>
        </aggregations>
      </configuration>
    </execution>
    <execution>
      <id>cssfiles</id>
      <goals>
        <goal>compress</goal>
      </goals>
      <configuration>
        <aggregations>
          <aggregation>
            <!-- the ultimate css file that is served up to the browser -->
            <output>${compressed-dir}/style/sonatype-all.css</output>
            <!-- files to include, path relative to output's directory or absolute path-->
            <includes>
              <include>${compressed-dir}/style/Sonatype.css</include>
            </includes>
          </aggregation>
        </aggregations>
      </configuration>
    </execution>
  </executions>
  <configuration>
    <!-- where to put the generate files -->
    <outputDirectory>${compressed-dir}</outputDirectory>
    <!-- directory where source files will be pulled from -->
    <sourceDirectory>${basedir}/src/main/webapp</sourceDirectory>
    <webappDirectory>${compressed-dir}</webappDirectory>
    <nosuffix>true</nosuffix>
    <nomunge>true</nomunge>
    <jswarn>false</jswarn>
    <!-- we keep the whole ext library in our src for nexus-webapp, we only need their single aggregated js file -->
    <!-- we also don't want to include any of our webapp launch scripts -->
    <excludes>
      <exclude>**/ext-2.2/**/*.js</exclude>
      <exclude>**/*.so</exclude>
      <exclude>**/*.dll</exclude>
      <exclude>**/*.jnilib</exclude>
      <exclude>**/*.exe</exclude>
      <exclude>**/*.a</exclude>
      <exclude>**/*.sl</exclude>
      <exclude>**/wrapper</exclude>
    </excludes>
    <aggregations>
      <aggregation>
        <!-- insert new line after each concatenation (default: false) -->
        <insertNewLine>true</insertNewLine>
      </aggregation>
    </aggregations>
  </configuration>
  <dependencies>
    <!-- we use specific version of plexus-utils, this dependency probably won't be needed for most folks -->
    <dependency>
      <groupId>org.codehaus.plexus</groupId>
      <artifactId>plexus-utils</artifactId>
      <version>1.1</version>
    </dependency>
  </dependencies>
</plugin>

And that’s pretty much it. If you have a javascript application that is getting bloated, use the YUI Compressor maven plugin to get your webapp loading within a reasonable time again.