View Javadoc

1   package net_alchim31_maven_yuicompressor;
2   
3   import java.io.File;
4   import java.io.FileInputStream;
5   import java.io.FileOutputStream;
6   import java.io.InputStreamReader;
7   import java.io.OutputStreamWriter;
8   import java.util.zip.GZIPOutputStream;
9   
10  import org.apache.maven.plugin.MojoExecutionException;
11  import org.codehaus.plexus.util.FileUtils;
12  import org.codehaus.plexus.util.IOUtil;
13  
14  import com.yahoo.platform.yui.compressor.CssCompressor;
15  import com.yahoo.platform.yui.compressor.JavaScriptCompressor;
16  
17  /**
18   * Apply compression on JS and CSS (using YUI Compressor).
19   *
20   * @goal compress
21   * @phase process-resources
22   *
23   * @author David Bernard
24   * @created 2007-08-28
25   */
26  // @SuppressWarnings("unchecked")
27  public class YuiCompressorMojo extends MojoSupport {
28  
29      /**
30       * Read the input file using "encoding".
31       *
32       * @parameter expression="${file.encoding}" default-value="UTF-8"
33       */
34      private String encoding;
35  
36      /**
37       * The output filename suffix.
38       *
39       * @parameter expression="${maven.yuicompressor.suffix}" default-value="-min"
40       */
41      private String suffix;
42  
43      /**
44       * If no "suffix" must be add to output filename (maven's configuration manage empty suffix like default).
45       *
46       * @parameter expression="${maven.yuicompressor.nosuffix}" default-value="false"
47       */
48      private boolean nosuffix;
49  
50      /**
51       * Insert line breaks in output after the specified column number.
52       *
53       * @parameter expression="${maven.yuicompressor.linebreakpos}" default-value="0"
54       */
55      private int linebreakpos;
56  
57      /**
58       * [js only] Minify only, do not obfuscate.
59       *
60       * @parameter expression="${maven.yuicompressor.nomunge}" default-value="false"
61       */
62      private boolean nomunge;
63  
64      /**
65       * [js only] Preserve unnecessary semicolons.
66       *
67       * @parameter expression="${maven.yuicompressor.preserveAllSemiColons}" default-value="false"
68       */
69      private boolean preserveAllSemiColons;
70  
71      /**
72       * [js only] disable all micro optimizations.
73       *
74       * @parameter expression="${maven.yuicompressor.disableOptimizations}" default-value="false"
75       */
76      private boolean disableOptimizations;
77  
78      /**
79       * force the compression of every files,
80       * else if compressed file already exists and is younger than source file, nothing is done.
81       *
82       * @parameter expression="${maven.yuicompressor.force}" default-value="false"
83       */
84      private boolean force;
85  
86      /**
87       * a list of aggregation/concatenation to do after processing,
88       * for example to create big js files that contain several small js files.
89       * Aggregation could be done on any type of file (js, css, ...).
90       *
91       * @parameter
92       */
93      private Aggregation[] aggregations;
94  
95      /**
96       * request to create a gzipped version of the yuicompressed/aggregation files.
97       *
98       * @parameter expression="${maven.yuicompressor.gzip}" default-value="false"
99       */
100     private boolean gzip;
101 
102     /**
103      * show statistics (compression ratio).
104      *
105      * @parameter expression="${maven.yuicompressor.statistics}" default-value="true"
106      */
107     private boolean statistics;
108 
109     private long inSizeTotal_;
110     private long outSizeTotal_;
111 
112     @Override
113     protected String[] getDefaultIncludes() throws Exception {
114         return new String[]{"**/*.css", "**/*.js"};
115     }
116 
117     @Override
118     public void beforeProcess() throws Exception {
119         if (nosuffix) {
120             suffix = "";
121         }
122     }
123 
124     @Override
125     protected void afterProcess() throws Exception {
126         if (statistics && (inSizeTotal_ > 0)) {
127             getLog().info(String.format("total input (%db) -> output (%db)[%d%%]", inSizeTotal_, outSizeTotal_, ((outSizeTotal_ * 100)/inSizeTotal_)));
128         }
129         if (aggregations != null) {
130             for(Aggregation aggregation : aggregations) {
131                 getLog().info("generate aggregation : " + aggregation.output);
132                 aggregation.run();
133                 File gzipped = gzipIfRequested(aggregation.output);
134                 if (statistics) {
135                     if (gzipped != null) {
136                         getLog().info(String.format("%s (%db) -> %s (%db)[%d%%]", aggregation.output.getName(), aggregation.output.length(), gzipped.getName(), gzipped.length(), ratioOfSize(aggregation.output, gzipped)));
137                     } else if (aggregation.output.exists()){
138                         getLog().info(String.format("%s (%db)", aggregation.output.getName(), aggregation.output.length()));
139                     } else {
140                         getLog().warn(String.format("%s not created", aggregation.output.getName()));
141                     }
142                 }
143             }
144         }
145     }
146 
147 
148     @Override
149     protected void processFile(SourceFile src) throws Exception {
150         if (getLog().isDebugEnabled()) {
151             getLog().debug("compress file :" + src.toFile()+ " to " + src.toDestFile(suffix));
152         }
153         File inFile = src.toFile();
154         File outFile = src.toDestFile(suffix);
155 
156         getLog().debug("only compress if input file is younger than existing output file");
157         if (!force && outFile.exists() && (outFile.lastModified() > inFile.lastModified())) {
158             if (getLog().isInfoEnabled()) {
159                 getLog().info("nothing to do, " + outFile + " is younger than original, use 'force' option or clean your target");
160             }
161             return;
162         }
163 
164         InputStreamReader in = null;
165         OutputStreamWriter out = null;
166         File outFileTmp = new File(outFile.getAbsolutePath() + ".tmp");
167         FileUtils.forceDelete(outFileTmp);
168         try {
169             in = new InputStreamReader(new FileInputStream(inFile), encoding);
170             if (!outFile.getParentFile().exists() && !outFile.getParentFile().mkdirs()) {
171                 throw new MojoExecutionException( "Cannot create resource output directory: " + outFile.getParentFile() );
172             }
173             getLog().debug("use a temporary outputfile (in case in == out)");
174 
175             getLog().debug("start compression");
176             out = new OutputStreamWriter(new FileOutputStream(outFileTmp), encoding);
177             if (".js".equalsIgnoreCase(src.getExtension())) {
178                 JavaScriptCompressor compressor = new JavaScriptCompressor(in, jsErrorReporter_);
179                 compressor.compress(out, linebreakpos, !nomunge, jswarn, preserveAllSemiColons, disableOptimizations);
180             } else if (".css".equalsIgnoreCase(src.getExtension())) {
181                 CssCompressor compressor = new CssCompressor(in);
182                 compressor.compress(out, linebreakpos);
183             }
184             getLog().debug("end compression");
185         } finally {
186             IOUtil.close(in);
187             IOUtil.close(out);
188         }
189         FileUtils.forceDelete(outFile);
190         FileUtils.rename(outFileTmp, outFile);
191         File gzipped = gzipIfRequested(outFile);
192         if (statistics) {
193             inSizeTotal_ += inFile.length();
194             outSizeTotal_ += outFile.length();
195             getLog().info(String.format("%s (%db) -> %s (%db)[%d%%]", inFile.getName(), inFile.length(), outFile.getName(), outFile.length(), ratioOfSize(inFile, outFile)));
196             if (gzipped != null) {
197                 getLog().info(String.format("%s (%db) -> %s (%db)[%d%%]", inFile.getName(), inFile.length(), gzipped.getName(), gzipped.length(), ratioOfSize(inFile, gzipped)));
198             }
199         }
200     }
201 
202     protected File gzipIfRequested(File file) throws Exception {
203         if (!gzip || (file == null) || (!file.exists())) {
204             return null;
205         }
206         if (".gz".equalsIgnoreCase(FileUtils.getExtension(file.getName()))) {
207             return null;
208         }
209         File gzipped = new File(file.getAbsolutePath()+".gz");
210         getLog().debug(String.format("create gzip version : %s", gzipped.getName()));
211         GZIPOutputStream out = null;
212         FileInputStream in = null;
213         try {
214             out = new GZIPOutputStream(new FileOutputStream(gzipped));
215             in = new FileInputStream(file);
216             IOUtil.copy(in, out);
217         } finally {
218             IOUtil.close(in);
219             IOUtil.close(out);
220         }
221         return gzipped;
222     }
223 
224     protected long ratioOfSize(File file100, File fileX) throws Exception {
225         long v100 = Math.max(file100.length(), 1);
226         long vX = Math.max(fileX.length(), 1);
227         return (vX * 100)/v100;
228     }
229 }