Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript Source map generated by Closure compiler can have multiple links to the source files #97

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/main/java/com/samaxes/maven/minify/common/ClosureConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public class ClosureConfig {

private final Boolean angularPass;

private final Boolean mapToOriginalSourceFiles;

/**
* Init Closure Compiler values.
*
Expand All @@ -56,16 +58,18 @@ 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<SourceFile> externs, boolean useDefaultExterns, boolean createSourceMap, boolean angularPass) {
List<SourceFile> externs, boolean useDefaultExterns, boolean createSourceMap, boolean angularPass, boolean mapToOriginalSourceFiles) {
this.language = language;
this.compilationLevel = compilationLevel;
this.dependencyOptions = dependencyOptions;
this.externs = externs;
this.useDefaultExterns = useDefaultExterns;
this.sourceMapFormat = (createSourceMap) ? SourceMap.Format.V3 : null;
this.angularPass = angularPass;
this.mapToOriginalSourceFiles = createSourceMap && mapToOriginalSourceFiles;
}

/**
Expand Down Expand Up @@ -130,4 +134,14 @@ public Format getSourceMapFormat() {
public Boolean getAngularPass() {
return angularPass;
}

/**
* Gets the mapToOriginalSourceFiles
*
* @return the mapToOriginalSourceFiles
*/
public Boolean getMapToOriginalSourceFiles() {
return mapToOriginalSourceFiles;
}

}
13 changes: 12 additions & 1 deletion src/main/java/com/samaxes/maven/minify/plugin/MinifyMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,17 @@ public static enum Engine {
@Parameter(property = "closureCreateSourceMap", defaultValue = "false")
private boolean closureCreateSourceMap;

/**
* <p>
* If true, the source map created by the Closure compiler will have one link to each of the original JavaScript
* source files
* </p>
*
* @since 1.7.5-SNAPSHOT
*/
@Parameter(property = "closureMapToOriginalSourceFiles", defaultValue = "false")
private boolean closureMapToOriginalSourceFiles;

/**
* <p>
* Enables or disables sorting mode for Closure Library dependencies.
Expand Down Expand Up @@ -553,7 +564,7 @@ private ClosureConfig fillClosureConfig() {
}

return new ClosureConfig(closureLanguage, closureCompilationLevel, dependencyOptions, externs,
closureUseDefaultExterns, closureCreateSourceMap, closureAngularPass);
closureUseDefaultExterns, closureCreateSourceMap, closureAngularPass, closureMapToOriginalSourceFiles);
}

private Collection<ProcessFilesTask> createTasks(YuiConfig yuiConfig, ClosureConfig closureConfig)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -110,4 +110,9 @@ protected void minify(File mergedFile, File minifiedFile) throws IOException {

logCompressionGains(mergedFile, minifiedFile);
}

@Override
void minify(List<File> srcFiles, File minifiedFile) throws IOException {
throw new RuntimeException("Compressing a list of css files is not supported by this version.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -83,6 +84,8 @@ public abstract class ProcessFilesTask implements Callable<Object> {

private final boolean sourceIncludesEmpty;

protected final ClosureConfig closureConfig;

/**
* Task constructor.
*
Expand All @@ -105,12 +108,13 @@ public abstract class ProcessFilesTask implements Callable<Object> {
* @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<String> sourceFiles, List<String> sourceIncludes, List<String> 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;
Expand All @@ -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;
}

/**
Expand All @@ -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();

Expand Down Expand Up @@ -223,13 +237,34 @@ 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<File> srcFiles, File minifiedFile) throws IOException;

/**
* Logs compression gains.
*
* @param mergedFile input file resulting from the merged step
* @param minifiedFile output file resulting from the minify step
*/
void logCompressionGains(File mergedFile, File minifiedFile) {
List<File> srcFiles = new ArrayList<File>();
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<File> srcFiles, File minifiedFile) {
try {
File temp = File.createTempFile(minifiedFile.getName(), ".gz");

Expand All @@ -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).");

Expand Down
145 changes: 81 additions & 64 deletions src/main/java/com/samaxes/maven/minify/plugin/ProcessJSFilesTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -50,8 +50,6 @@
*/
public class ProcessJSFilesTask extends ProcessFilesTask {

private final ClosureConfig closureConfig;

/**
* Task constructor.
*
Expand Down Expand Up @@ -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);
}

/**
Expand All @@ -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<File> srcFiles = new ArrayList<File>();
srcFiles.add(mergedFile);
minify(srcFiles, minifiedFile);
}

@Override
protected void minify(List<File> 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<SourceFile> 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<SourceFile> sourceFileList = new ArrayList<SourceFile>();
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<SourceFile> 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) {
Expand All @@ -183,4 +199,5 @@ private void flushSourceMap(File sourceMapOutputFile, String minifyFileName, Sou
+ ((verbose) ? sourceMapOutputFile.getPath() : sourceMapOutputFile.getName()) + "].", e);
}
}

}