I changed the my work frontend module build optimize flow few day ago.
The original build flow is use wro4j plugin and my buildhelper. The cons is that there’re many trivial steps when adding a new page.
I decide to update it with new way. I study the YEOMAN build flow and apply it in my module. The result is good.
YEOMAN uses nodejs and Grunt to do optimize.
But the company module system is using Maven. The first step is to find a maven plugin to run grunt. I choose grunt-maven-plugin and it’s easy to integrate.
See my snippet code from pom.xml. I copy all files from src/main/webapp to ${project.build.directory}/original and run grunt in prepare package phase. I don’t want to let grunt access src/main/webapp so the ${project.build.directory}/original will be my grunt project source folder.
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-resources-plugin</artifactId><version>2.6</version><executions><!-- copy webapp to target folder --><execution><id>copy-webapp</id><phase>process-resources</phase><goals><goal>copy-resources</goal></goals><configuration><outputDirectory>${project.build.directory}/original</outputDirectory><resources><resource><directory>src/main/webapp/</directory></resource></resources></configuration></execution></executions></plugin><plugin><groupId>pl.allegro</groupId><artifactId>grunt-maven-plugin</artifactId><version>1.2.1</version><configuration><showColors>true</showColors></configuration><executions><execution><id>grunt-build</id><phase>prepare-package</phase><goals><goal>npm</goal><goal>grunt</goal></goals></execution></executions></plugin>
Here i will skip the installation of yeoman. Let’s see how yeoman build flow work.
I set the gamenow.app to target/original that is the folder copy from src/main/webapp. gamenow.dist will be target/storefront and this folder will be packaged to WAR file in maven build.
Gruntfile.js
1234567891011121314151617
module.exports=function(grunt){// Load grunt tasks automaticallyrequire('load-grunt-tasks')(grunt);// Time how long tasks take. Can help when optimizing build timesrequire('time-grunt')(grunt);grunt.initConfig({gamenow:{// Configurable pathsapp:'target/original',dist:'target/storefront'},....});
YEOMAN uses usemin grunt plugin to create concat,minify,rewrite. In useminPrepare setting i set the html sources in <%= gamenow.app %>/*.html. It means usemin plugin will search all .html files and find the build block description to create configuration automatically. You will see how to set build block description in html later. The flow.steps enables us to add/modify step ourselves. Here i add a new step copy and it only executes concat. The js and css steps are default.
In usemin setting i add a copy function definition in blockReplacements. The function is quite simple. We need to set html and css folders too because usemin plugin needs to find files to modify in these folders.
Let’s take a look at sample.html. Use <!-- build:step path --> to wrap your css. Here the step is css and path is css/all.css. The usemin plugin will use concat to merge all css files to css/all.css and cssmin plugin to minify it.
Use <!-- build:js path --> to optimize your JS. The js step will do concat and ugilfy in defult.
Use <!-- build:copy --> to only concat file without ugilfy. Because i want config file could be modified manually. Obfuscated file makes hard to modify.
If you want to overwrite the default cssmin and concat settings you could just add the block in config. I overwrite the original concat process function. Because the css url path will be wrong if your source css and merged css are in different folder. See my comment inside function.
Gruntfile.js
1234567891011121314151617181920212223242526272829
cssmin:{options:{noAdvanced:true}},concat:{options:{separator:'\n',process:function(src,filepath){varcssPatt=newRegExp('(\/.*\/).*\.css$');varfile=cssPatt.exec(filepath);if(file){// rewrite the url in css file.// url(../../img/bluebg.jpg) -> url(../img/bluebg.jpg)// url(../../../img/bluebg2.jpg) -> url(../img/bluebg.2jpg)varurlPatt=/url\((\.\.\/){1,}(.*)\)/g;returnsrc.replace(urlPatt,function(match,p1,p2){varreplacedUrl='url(../'+p2+')';returnreplacedUrl;});}returnsrc;}}}
To prevent browser cache the js/css files need to change file name when the content are different. We use rev plugin to add md5 in file name. Don’t worry about the filename changed because usemin is smart enough to search files in assetsDirs: ['<%= gamenow.dist %>'].
In my module i also want to keep the originl .html file for debug. I add a task debugHtml to get it. I copy .html files to dist folder with -dev filename. Task nonOptimizeFiles is to copy the rest files.