From 6757ddc676add185bd7f77630760f28252c7ccae Mon Sep 17 00:00:00 2001 From: Svetlin Minchev Date: Fri, 17 Apr 2015 10:03:36 +0300 Subject: [PATCH] JavaScript Source map generated by Closure compiler now can have links to each source file instead of one link to the merged file. This may be useful when debugging many JavaScript source files. This is configured with the option closureMapToOriginalSourceFiles = true --- .../maven/minify/common/ClosureConfig.java | 16 +- .../maven/minify/plugin/MinifyMojo.java | 13 +- .../minify/plugin/ProcessCSSFilesTask.java | 7 +- .../maven/minify/plugin/ProcessFilesTask.java | 48 +++++- .../minify/plugin/ProcessJSFilesTask.java | 145 ++++++++++-------- 5 files changed, 159 insertions(+), 70 deletions(-) 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); } } + }