diff --git a/src/main/java/com/samaxes/maven/minify/common/ClosureConfig.java b/src/main/java/com/samaxes/maven/minify/common/ClosureConfig.java index b12e557c..1749a2a8 100644 --- a/src/main/java/com/samaxes/maven/minify/common/ClosureConfig.java +++ b/src/main/java/com/samaxes/maven/minify/common/ClosureConfig.java @@ -46,6 +46,8 @@ public class ClosureConfig { private final Boolean angularPass; + private final Boolean mapToOriginalSourceFiles; + /** * Init Closure Compiler values. * @@ -56,9 +58,10 @@ public class ClosureConfig { * @param useDefaultExterns use default externs packed with the Closure Compiler * @param createSourceMap create a source map for the minifed/combined production files * @param angularPass use {@code @ngInject} annotation to generate Angular injections + * @param mapToOriginalSourceFiles if true, do not merge the source js files and create a link to each of them in the source map */ public ClosureConfig(LanguageMode language, CompilationLevel compilationLevel, DependencyOptions dependencyOptions, - List externs, boolean useDefaultExterns, boolean createSourceMap, boolean angularPass) { + List externs, boolean useDefaultExterns, boolean createSourceMap, boolean angularPass, boolean mapToOriginalSourceFiles) { this.language = language; this.compilationLevel = compilationLevel; this.dependencyOptions = dependencyOptions; @@ -66,6 +69,7 @@ public ClosureConfig(LanguageMode language, CompilationLevel compilationLevel, D this.useDefaultExterns = useDefaultExterns; this.sourceMapFormat = (createSourceMap) ? SourceMap.Format.V3 : null; this.angularPass = angularPass; + this.mapToOriginalSourceFiles = createSourceMap && mapToOriginalSourceFiles; } /** @@ -130,4 +134,14 @@ public Format getSourceMapFormat() { public Boolean getAngularPass() { return angularPass; } + + /** + * Gets the mapToOriginalSourceFiles + * + * @return the mapToOriginalSourceFiles + */ + public Boolean getMapToOriginalSourceFiles() { + return mapToOriginalSourceFiles; + } + } diff --git a/src/main/java/com/samaxes/maven/minify/plugin/MinifyMojo.java b/src/main/java/com/samaxes/maven/minify/plugin/MinifyMojo.java index bb85bcf4..68e6e61e 100644 --- a/src/main/java/com/samaxes/maven/minify/plugin/MinifyMojo.java +++ b/src/main/java/com/samaxes/maven/minify/plugin/MinifyMojo.java @@ -432,6 +432,17 @@ public static enum Engine { @Parameter(property = "closureCreateSourceMap", defaultValue = "false") private boolean closureCreateSourceMap; + /** + *

+ * If true, the source map created by the Closure compiler will have one link to each of the original JavaScript + * source files + *

+ * + * @since 1.7.5-SNAPSHOT + */ + @Parameter(property = "closureMapToOriginalSourceFiles", defaultValue = "false") + private boolean closureMapToOriginalSourceFiles; + /** *

* Enables or disables sorting mode for Closure Library dependencies. @@ -553,7 +564,7 @@ private ClosureConfig fillClosureConfig() { } return new ClosureConfig(closureLanguage, closureCompilationLevel, dependencyOptions, externs, - closureUseDefaultExterns, closureCreateSourceMap, closureAngularPass); + closureUseDefaultExterns, closureCreateSourceMap, closureAngularPass, closureMapToOriginalSourceFiles); } private Collection createTasks(YuiConfig yuiConfig, ClosureConfig closureConfig) diff --git a/src/main/java/com/samaxes/maven/minify/plugin/ProcessCSSFilesTask.java b/src/main/java/com/samaxes/maven/minify/plugin/ProcessCSSFilesTask.java index 57d0a6ce..1d51f17c 100644 --- a/src/main/java/com/samaxes/maven/minify/plugin/ProcessCSSFilesTask.java +++ b/src/main/java/com/samaxes/maven/minify/plugin/ProcessCSSFilesTask.java @@ -70,7 +70,7 @@ public ProcessCSSFilesTask(Log log, boolean verbose, Integer bufferSize, String String outputDir, String outputFilename, Engine engine, YuiConfig yuiConfig) throws FileNotFoundException { super(log, verbose, bufferSize, charset, suffix, nosuffix, skipMerge, skipMinify, webappSourceDir, webappTargetDir, inputDir, sourceFiles, sourceIncludes, sourceExcludes, outputDir, outputFilename, - engine, yuiConfig); + engine, yuiConfig, null); } /** @@ -110,4 +110,9 @@ protected void minify(File mergedFile, File minifiedFile) throws IOException { logCompressionGains(mergedFile, minifiedFile); } + + @Override + void minify(List srcFiles, File minifiedFile) throws IOException { + throw new RuntimeException("Compressing a list of css files is not supported by this version."); + } } diff --git a/src/main/java/com/samaxes/maven/minify/plugin/ProcessFilesTask.java b/src/main/java/com/samaxes/maven/minify/plugin/ProcessFilesTask.java index 8b9d4d62..cd8e3c29 100644 --- a/src/main/java/com/samaxes/maven/minify/plugin/ProcessFilesTask.java +++ b/src/main/java/com/samaxes/maven/minify/plugin/ProcessFilesTask.java @@ -40,6 +40,7 @@ import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.IOUtil; +import com.samaxes.maven.minify.common.ClosureConfig; import com.samaxes.maven.minify.common.SourceFilesEnumeration; import com.samaxes.maven.minify.common.YuiConfig; import com.samaxes.maven.minify.plugin.MinifyMojo.Engine; @@ -83,6 +84,8 @@ public abstract class ProcessFilesTask implements Callable { private final boolean sourceIncludesEmpty; + protected final ClosureConfig closureConfig; + /** * Task constructor. * @@ -105,12 +108,13 @@ public abstract class ProcessFilesTask implements Callable { * @param outputFilename the output file name * @param engine minify processor engine selected * @param yuiConfig YUI Compressor configuration + * @param closureConfig Google closure configuration * @throws FileNotFoundException when the given source file does not exist */ public ProcessFilesTask(Log log, boolean verbose, Integer bufferSize, String charset, String suffix, boolean nosuffix, boolean skipMerge, boolean skipMinify, String webappSourceDir, String webappTargetDir, String inputDir, List sourceFiles, List sourceIncludes, List sourceExcludes, - String outputDir, String outputFilename, Engine engine, YuiConfig yuiConfig) throws FileNotFoundException { + String outputDir, String outputFilename, Engine engine, YuiConfig yuiConfig, ClosureConfig closureConfig) throws FileNotFoundException { this.log = log; this.verbose = verbose; this.bufferSize = bufferSize; @@ -135,6 +139,7 @@ public ProcessFilesTask(Log log, boolean verbose, Integer bufferSize, String cha } this.sourceFilesEmpty = sourceFiles.isEmpty(); this.sourceIncludesEmpty = sourceIncludes.isEmpty(); + this.closureConfig = closureConfig; } /** @@ -149,7 +154,16 @@ public Object call() throws IOException { log.info("Starting " + fileType + " task:"); if (!files.isEmpty() && (targetDir.exists() || targetDir.mkdirs())) { - if (skipMerge) { + + if (fileType.equals("JavaScript") && this.engine == Engine.CLOSURE + && closureConfig.getMapToOriginalSourceFiles()) { + + File minifiedFile = new File(targetDir, (nosuffix) ? mergedFilename + : FileUtils.basename(mergedFilename) + suffix + FileUtils.getExtension(mergedFilename)); + + minify(files, minifiedFile); + + } else if (skipMerge) { log.info("Skipping the merge step..."); String sourceBasePath = sourceDir.getAbsolutePath(); @@ -223,6 +237,15 @@ protected void merge(File mergedFile) throws IOException { */ abstract void minify(File mergedFile, File minifiedFile) throws IOException; + /** + * Minifies a list of source files into a single file. Create missing parent directories if needed. + * + * @param srcFiles list of input files + * @param minifiedFile output file resulting from the minify step + * @throws IOException when the minify step fails + */ + abstract void minify(List srcFiles, File minifiedFile) throws IOException; + /** * Logs compression gains. * @@ -230,6 +253,18 @@ protected void merge(File mergedFile) throws IOException { * @param minifiedFile output file resulting from the minify step */ void logCompressionGains(File mergedFile, File minifiedFile) { + List srcFiles = new ArrayList(); + srcFiles.add(mergedFile); + logCompressionGains(srcFiles, minifiedFile); + } + + /** + * Logs compression gains. + * + * @param srcFiles list of input files to compress + * @param minifiedFile output file resulting from the minify step + */ + void logCompressionGains(List srcFiles, File minifiedFile) { try { File temp = File.createTempFile(minifiedFile.getName(), ".gz"); @@ -239,7 +274,14 @@ void logCompressionGains(File mergedFile, File minifiedFile) { IOUtil.copy(in, outGZIP, bufferSize); } - log.info("Uncompressed size: " + mergedFile.length() + " bytes."); + long uncompressedSize = 0; + if (srcFiles != null) { + for (File srcFile : srcFiles) { + uncompressedSize += srcFile.length(); + } + } + + log.info("Uncompressed size: " + uncompressedSize + " bytes."); log.info("Compressed size: " + minifiedFile.length() + " bytes minified (" + temp.length() + " bytes gzipped)."); diff --git a/src/main/java/com/samaxes/maven/minify/plugin/ProcessJSFilesTask.java b/src/main/java/com/samaxes/maven/minify/plugin/ProcessJSFilesTask.java index d31392dc..4da8bfa3 100644 --- a/src/main/java/com/samaxes/maven/minify/plugin/ProcessJSFilesTask.java +++ b/src/main/java/com/samaxes/maven/minify/plugin/ProcessJSFilesTask.java @@ -28,12 +28,12 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.util.ArrayList; import java.util.List; import org.apache.maven.plugin.logging.Log; import org.mozilla.javascript.EvaluatorException; -import com.google.common.collect.Lists; import com.google.javascript.jscomp.CommandLineRunner; import com.google.javascript.jscomp.Compiler; import com.google.javascript.jscomp.CompilerOptions; @@ -50,8 +50,6 @@ */ public class ProcessJSFilesTask extends ProcessFilesTask { - private final ClosureConfig closureConfig; - /** * Task constructor. * @@ -84,9 +82,7 @@ public ProcessJSFilesTask(Log log, boolean verbose, Integer bufferSize, String c throws FileNotFoundException { super(log, verbose, bufferSize, charset, suffix, nosuffix, skipMerge, skipMinify, webappSourceDir, webappTargetDir, inputDir, sourceFiles, sourceIncludes, sourceExcludes, outputDir, outputFilename, - engine, yuiConfig); - - this.closureConfig = closureConfig; + engine, yuiConfig, closureConfig); } /** @@ -98,81 +94,101 @@ public ProcessJSFilesTask(Log log, boolean verbose, Integer bufferSize, String c */ @Override protected void minify(File mergedFile, File minifiedFile) throws IOException { + List srcFiles = new ArrayList(); + srcFiles.add(mergedFile); + minify(srcFiles, minifiedFile); + } + + @Override + protected void minify(List srcFiles, File minifiedFile) throws IOException { minifiedFile.getParentFile().mkdirs(); - try (InputStream in = new FileInputStream(mergedFile); - OutputStream out = new FileOutputStream(minifiedFile); - InputStreamReader reader = new InputStreamReader(in, charset); + try (OutputStream out = new FileOutputStream(minifiedFile); OutputStreamWriter writer = new OutputStreamWriter(out, charset)) { log.info("Creating the minified file [" + ((verbose) ? minifiedFile.getPath() : minifiedFile.getName()) + "]."); switch (engine) { - case CLOSURE: - log.debug("Using Google Closure Compiler engine."); - - CompilerOptions options = new CompilerOptions(); - closureConfig.getCompilationLevel().setOptionsForCompilationLevel(options); - options.setOutputCharset(charset); - options.setLanguageIn(closureConfig.getLanguage()); - options.setAngularPass(closureConfig.getAngularPass()); - options.setDependencyOptions(closureConfig.getDependencyOptions()); - - File sourceMapResult = new File(minifiedFile.getPath() + ".map"); - if (closureConfig.getSourceMapFormat() != null) { - options.setSourceMapFormat(closureConfig.getSourceMapFormat()); - options.setSourceMapOutputPath(sourceMapResult.getPath()); - // options.setSourceMapLocationMappings(Lists.newArrayList(new - // SourceMap.LocationMapping(sourceDir.getPath() + File.separator, ""))); - } - - SourceFile input = SourceFile.fromInputStream(mergedFile.getName(), in); - List externs = closureConfig.getExterns(); - if (closureConfig.getUseDefaultExterns()) { - externs.addAll(CommandLineRunner.getDefaultExterns()); - } - - Compiler compiler = new Compiler(); - compiler.compile(externs, Lists.newArrayList(input), options); - - if (compiler.hasErrors()) { - throw new EvaluatorException(compiler.getErrors()[0].description); - } - - writer.append(compiler.toSource()); - - if (closureConfig.getSourceMapFormat() != null) { - log.info("Creating the minified file map [" - + ((verbose) ? sourceMapResult.getPath() : sourceMapResult.getName()) + "]."); - - sourceMapResult.createNewFile(); - flushSourceMap(sourceMapResult, minifiedFile.getName(), compiler.getSourceMap()); - - writer.append(System.getProperty("line.separator")); - writer.append("//# sourceMappingURL=" + sourceMapResult.getName()); - } - - break; - case YUI: + case CLOSURE: + log.debug("Using Google Closure Compiler engine."); + + List sourceFileList = new ArrayList(); + for (File srcFile : srcFiles) { + InputStream in = new FileInputStream(srcFile); + SourceFile input = SourceFile.fromInputStream(srcFile.getName(), in); + sourceFileList.add(input); + } + + CompilerOptions options = new CompilerOptions(); + closureConfig.getCompilationLevel().setOptionsForCompilationLevel(options); + options.setOutputCharset(charset); + options.setLanguageIn(closureConfig.getLanguage()); + options.setAngularPass(closureConfig.getAngularPass()); + options.setDependencyOptions(closureConfig.getDependencyOptions()); + + File sourceMapResult = new File(minifiedFile.getPath() + ".map"); + if (closureConfig.getSourceMapFormat() != null) { + options.setSourceMapFormat(closureConfig.getSourceMapFormat()); + options.setSourceMapOutputPath(sourceMapResult.getPath()); + // options.setSourceMapLocationMappings(Lists.newArrayList(new + // SourceMap.LocationMapping(sourceDir.getPath() + File.separator, ""))); + } + + List externs = closureConfig.getExterns(); + if (closureConfig.getUseDefaultExterns()) { + externs.addAll(CommandLineRunner.getDefaultExterns()); + } + + Compiler compiler = new Compiler(); + compiler.compile(externs, sourceFileList, options); + + if (compiler.hasErrors()) { + throw new EvaluatorException(compiler.getErrors()[0].description); + } + + writer.append(compiler.toSource()); + + if (closureConfig.getSourceMapFormat() != null) { + log.info("Creating the minified files map [" + + ((verbose) ? sourceMapResult.getPath() : sourceMapResult.getName()) + "]."); + + sourceMapResult.createNewFile(); + flushSourceMap(sourceMapResult, minifiedFile.getName(), compiler.getSourceMap()); + + writer.append(System.getProperty("line.separator")); + writer.append("//# sourceMappingURL=" + sourceMapResult.getName()); + } + + break; + + case YUI: + + if (srcFiles.size() == 1) { log.debug("Using YUI Compressor engine."); + InputStream in = new FileInputStream(srcFiles.get(0)); + InputStreamReader reader = new InputStreamReader(in, charset); + JavaScriptCompressor compressor = new JavaScriptCompressor(reader, new JavaScriptErrorReporter(log, - mergedFile.getName())); + srcFiles.get(0).getName())); compressor.compress(writer, yuiConfig.getLineBreak(), yuiConfig.isMunge(), verbose, yuiConfig.isPreserveSemicolons(), yuiConfig.isDisableOptimizations()); - break; - default: - log.warn("JavaScript engine not supported."); - break; + + } else { + log.warn("JavaScript engine not supported. " + + "Only the CLOSURE engine is supported with the closureMapToOriginalSourceFiles option."); + } + break; + default: + log.warn("JavaScript engine not supported."); + break; } } catch (IOException e) { - log.error( - "Failed to compress the JavaScript file [" - + ((verbose) ? mergedFile.getPath() : mergedFile.getName()) + "].", e); + log.error("Failed to compress the JavaScript file", e); throw e; } - logCompressionGains(mergedFile, minifiedFile); + logCompressionGains(srcFiles, minifiedFile); } private void flushSourceMap(File sourceMapOutputFile, String minifyFileName, SourceMap sourceMap) { @@ -183,4 +199,5 @@ private void flushSourceMap(File sourceMapOutputFile, String minifyFileName, Sou + ((verbose) ? sourceMapOutputFile.getPath() : sourceMapOutputFile.getName()) + "].", e); } } + }