1

I am trying to use the YUI Compressor plugin for maven to compress my CSS and JavaScript, but I'm experiencing two problems.

  1. My configuration properly compresses and aggregates the JavaScript files, but if I run mvn package to make a war file, maven will compress the JavaScript files BEFORE it copies over the src/main/webapp folder. This overwrites all of the compressed css and JavaScript files. How can I fix this?

  2. How do I get the tomcat maven plugin to use the compressed JavaScript files rather than the ones in my src/main/webapp/scripts folder? When my application is trying to read all.js, it is failing because it is located in the target/ directory and not in my src/main/webapps folder.

        <!-- Tomcat -->
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>tomcat-maven-plugin</artifactId>
            <version>1.1</version>
            <configuration>
                <warFile>target/myapp-1.0.war</warFile>
            </configuration>
        </plugin>
    
        <!-- YUI Compressor -->
        <plugin>
            <groupId>net.alchim31.maven</groupId>
            <artifactId>yuicompressor-maven-plugin</artifactId>
            <executions>
                <execution>
                    <goals>
                        <goal>compress</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <excludes>
                    <exclude>**/jwplayer.js</exclude>
                    <exclude>**/audio-player-noswfobject.js</exclude>
                    <exclude>**/audio-player-uncompressed.js</exclude>
                    <exclude>**/audio-player.js</exclude>
                    <exclude>**/jscharts.js</exclude>
                    <exclude>**/jquery-ui-1.8.16.custom.min.js</exclude>
                </excludes>
                <nosuffix>true</nosuffix>
                <jswarn>false</jswarn>
                <force>false</force>
                <aggregations>
                    <aggregation>
                        <removeIncluded>false</removeIncluded>
                        <insertNewLine>true</insertNewLine>
                        <output>${project.build.directory}/${project.build.finalName}/scripts/all.js</output>
                        <includes>
                            <include>**/json/json2.js</include>
                            <include>**/jwplayer/jwplayer.js</include>
                            <include>**/font/font.js</include>
                            <include>**/underscore/underscore.js</include>
                            <include>**/jquery/jquery-1.7.1.js</include>
                            <include>**/jquery/jquery-ui-1.8.16.custom.min.js</include>
                            <include>**/jquery/jquery.cookie.js</include>
                            <include>**/jquery/jquery.fancybox.js</include>
                            <include>**/jquery/jquery.highlight.js</include>
                            <include>**/jquery/jquery.jcrop.js</include>
                            <include>**/jquery/jquery.tmpl.js</include>
                            <include>**/jquery/farbtastic.js</include>
                            <include>**/jscharts/jscharts.js</include>
    
                            <include>**/myapp/homepage.js</include>
                        </includes>
                    </aggregation>
                </aggregations>
            </configuration>
        </plugin>
    

Or am I just going about my goal the wrong way?

Here is the maven output when packaging the war. You can see that even if I tell the yui:compress goal to in the pre-package phase, it still occurs before the copying of the resources since this occurs in the package phase:

[INFO] ------------------------------------------------------------------------
[INFO] Building My App 1.0
[INFO] ------------------------------------------------------------------------
Downloading: http://repo1.maven.org/maven2/net/alchim31/maven/yuicompressor-maven-plugin/maven-metadata.xml
Downloading: http://snapshots.repository.codehaus.org/net/alchim31/maven/yuicompressor-maven-plugin/maven-metadata.xml
Downloading: http://download.java.net/maven/2/net/alchim31/maven/yuicompressor-maven-plugin/maven-metadata.xml
Downloading: http://oss.sonatype.org/content/groups/public/net/alchim31/maven/yuicompressor-maven-plugin/maven-metadata.xml
Downloaded: http://oss.sonatype.org/content/groups/public/net/alchim31/maven/yuicompressor-maven-plugin/maven-metadata.xml (442 B at 2.1 KB/sec)
Downloaded: http://repo1.maven.org/maven2/net/alchim31/maven/yuicompressor-maven-plugin/maven-metadata.xml (403 B at 0.9 KB/sec)
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ myapp ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 26 resources
[INFO] Copying 4 resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ myapp ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:compile (default) @ myapp ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- aspectj-maven-plugin:1.3:compile (default) @ myapp ---
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ myapp ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 11 resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ myapp ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- aspectj-maven-plugin:1.3:test-compile (default) @ myapp ---
[INFO] No modifications found skipping aspectJ compile
[INFO] 
[INFO] --- maven-surefire-plugin:2.6:test (default-test) @ myapp ---
[INFO] Tests are skipped.
[INFO] 
[INFO] --- yuicompressor-maven-plugin:1.2:compress (default) @ myapp ---
[INFO] 960.css (9989b) -> 960.css (5897b)[59%]
[INFO] base.css (24210b) -> base.css (16437b)[67%]
[INFO] reset.css (2062b) -> reset.css (1096b)[53%]
[INFO] text.css (1270b) -> text.css (598b)[47%]
[INFO] tinymce.css (1994b) -> tinymce.css (1277b)[64%]
[INFO] jquery.fancybox-1.3.4.css (8852b) -> jquery.fancybox-1.3.4.css (6975b)[78%]
[INFO] farbtastic.css (1362b) -> farbtastic.css (478b)[35%]
[INFO] jquery.jcrop.css (748b) -> jquery.jcrop.css (582b)[77%]
[INFO] base.css (34567b) -> base.css (25034b)[72%]
[INFO] jquery-ui-1.8.11.custom.css (33994b) -> jquery-ui-1.8.11.custom.css (25351b)[74%]
.... (tons of javascript files)
[INFO] why.js (515b) -> why.js (354b)[68%]
[INFO] underscore.js (26960b) -> underscore.js (9472b)[35%]
[INFO] total input (1832512b) -> output (1198425b)[65%]
[INFO] generate aggregation : C:\Users\egervari\IdeaProjects\myapp-development\target\myapp-1.0\scripts\all.js
[INFO] all.js (564342b)
[INFO] nb warnings: 0, nb errors: 0
[INFO] 
[INFO] --- maven-war-plugin:2.1.1:war (default-war) @ myapp ---
[INFO] Packaging webapp
[INFO] Assembling webapp [myapp] in [C:\Users\egervari\IdeaProjects\myapp-development\target\myapp-1.0]
[INFO] Processing war project
[INFO] Copying webapp resources [C:\Users\egervari\IdeaProjects\myapp-development\src\main\webapp]

Even if changing the phase did work, then I would also have to get this compression to run before tomcat:run as well.

Is maven just the wrong tool for the job you think? Or is maven/java just not up-to-snuff with heavy javascript development? Why is this so hard?

egervari
  • 22,372
  • 32
  • 121
  • 175
  • Try to put the yui compressor into an other phase (e.g. prepare-package) – khmarbaise Dec 02 '11 at 12:08
  • @khmarbaise I've tried this. The problem is that this phase still occurs before the package phase. The copying occurs inside of the package phase. I am at a loss. I'm about to give up on this approach entirely. I think this compression/aggregation might have to happen on the server after I upload the war, which... to be honest... sucks :( I also will not really have a convenient way to test my production javascript via tomcat on my development machine either. Before, I just ran tomcat:run and that was it. I really don't want to complicate my development environment either. – egervari Dec 02 '11 at 12:42
  • @khmarbaise it just seems that maven wasn't meant for this. Neither was spring either. Most modern web frameworks have this feature now, but java is honestly in the stone age. It's too bad that this is a big existing project. I have to get something working. I guess I could output the production javascript back to my `src/main/webapp/script` directory. That's the only thing I can think off. Would it be so bad to live with normal and .min versions of every file in my source directory? That doesn't seem very clean :/ – egervari Dec 02 '11 at 12:43
  • One idea I have to fix this is maybe put the javascript in `src/main/scripts` and then compress everything to `src/main/webapp/scripts`... that way the `package` goal treats the regular source path as the final product. The real drawback to this though is that I won't be able to test my development javascript code anymore - I'd have to run the `yui:compress` goal EVERY time I make a chance to the css and javascript - that's just too much of hassle for me. Help anyone? – egervari Dec 03 '11 at 04:31
  • I refer you to the answer of this question: http://stackoverflow.com/questions/11494943/yuicompressor-maven-plugin-and-maven-war-plugin – Hisham Sep 30 '12 at 15:48

2 Answers2

4

I faced a similar issue and I changed my phase to package. Hopefully this will help someone else as well.

This is tricky. This has to do with the nosuffix config option. If you remove the nosuffix option, the minification works as expected.

If you really have to have nosuffix then you need to change the execution phase to "package".

    <executions>
      <execution>
        <phase>package</phase>
          <goals>
    ......
    .....

Any phase before prepare-resources doesn't work because when the war is built, it picks up the js from the original source location and thereby overwrites the minified js (created during prepare-resources phase) in the target directory.

Minification works when you remove nosuffix because during package phase, the file names are different, so there is no over-writing of the files and you will see both the minified and non-minified js files in your target directory.

Inxsible
  • 700
  • 5
  • 27
1

To work around this issue you can somethin like.

(1) Put your personal coded javascript in src/main/js not in src/main/webapp. Use the mvn convention for JS placement. *Note: for libraries that you include, e.g. jQuery or whatever else, you can just dump them in src/main/webapp, that is fine. Those will already have minimized javascript that you will never be modifying.

(2) Tune your YUI plugin to (a) ignore javascript you did not code, (b) to compile the compressed javascript to an output folder.

       <plugin>
            <groupId>net.alchim31.maven</groupId>
            <artifactId>yuicompressor-maven-plugin</artifactId>
            <version>1.5.1</version>
            <executions>
                <execution>
                    <goals>
                        <goal>compress</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <nosuffix>true</nosuffix>

                <excludes>
                    <exclude>**/ui-framework/**</exclude>
                </excludes>
                <outputDirectory>${yui.outputdir}</outputDirectory>
            </configuration>
        </plugin>

So with this, we can make the following exercise.

mvn process-resources


└── target
├── classes
│   ├── default.logback.xml
│   └── props
│       ├── default.props
│       └── production.default.props
├── packageLinkDefs.properties
└── yui-compressed
    └── jasmineDummyExample_element_mover.js

The point here is that YUI is compiling the compressed javascript to its own output folder and the war plugin has no idea about it.

(3) Finally you just need to tune your war plugin to copy the yui compiled sources.

As long as you are following the javascript convention for maven, where your javascript should have been coded under src/main/js/, your war plugin by default will not be copying your javascipt (sounds strange right?).

By default, your war plugin will be copying whatever static resources are under src/main/webapp. This is convenient, because it allows you to instruct the war plugin to keep its default behavior of copying the src/main/webapp to the final war file and, in addition, to copy your /src/target/yui-compressed folder. In this manner, there are not overlapping files in both folders, there is not file overwriting in the output directory.

This is illustrated in the following snippet:

$ mvn war:war
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building lift-someproject Project 1.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-war-plugin:2.5:war (default-cli) @ lift-someproject ---
[INFO] Packaging webapp
[INFO] Assembling webapp [lift-someproject] in [D:\scalaWorkspace\liftsomeprojectTwo\lift-someproject\target\lift-someproject]
[INFO] Processing war project
[INFO] Copying webapp webResources [D:\scalaWorkspace\liftsomeprojectTwo\lift-someproject\target\yui-compressed] to [D:\scalaWorkspace\liftsomeprojectTwo\lift-someproject\target\lift-someproject]
[INFO] Copying webapp resources [D:\scalaWorkspace\liftsomeprojectTwo\lift-someproject\src\main\webapp]
[INFO] Webapp assembled in [820 msecs]
[INFO] Building war: D:\scalaWorkspace\liftsomeprojectTwo\lift-someproject\target\lift-someproject.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 12.968s
[INFO] Finished at: Tue Dec 23 20:17:59 CET 2014
[INFO] Final Memory: 11M/246M
[INFO] ------------------------------------------------------------------------

You can tune your war file to be aware of the special folder with your not-crushed yui compressed JS files:

   <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>2.5</version>
            <configuration>
                <webResources>
                    <resource>
                        <directory>${yui.outputdir}</directory>
                        <targetPath>js</targetPath>
                    </resource>
                </webResources>
            </configuration>
        </plugin>

If you use Jetty for rapid development, you will want some further teaking on that plugin, to make sure that it finds your hand coded javascript in src/main/js.

That should work.

99Sono
  • 3,554
  • 27
  • 39