it.anyplace.web.JavascriptProcessorMojo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of a-web-maven-plugin Show documentation
Show all versions of a-web-maven-plugin Show documentation
A web resource compiler/preprocessor, Maven Plugin
The newest version!
/**
* Copyright 2013 Davide Imbriaco
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see .
*
*/
package it.anyplace.web;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParser;
import com.google.javascript.jscomp.CompilerOptions;
import com.google.javascript.jscomp.SourceFile;
import com.google.javascript.jscomp.Compiler;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.StringReader;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.maven.plugin.logging.Log;
import org.yaml.snakeyaml.Yaml;
/**
*/
@Mojo(name = "javascript", defaultPhase = LifecyclePhase.PACKAGE)
public class JavascriptProcessorMojo extends AbstractMojo {
private final Log logger = getLog();
@Parameter(required = true, property = "sourceJsDirectory", defaultValue = "${project.basedir}/src/main/javascript")
private File sourceJsDirectory;
@Parameter(required = true, property = "targetJsDirectory", defaultValue = "${project.build.directory}/${project.build.finalName}/js")
private File targetJsDirectory;
@Parameter(property = "defaultPrefix", defaultValue = "")
private String defaultPrefix;
@Parameter(property = "compressJs", defaultValue = "false")
private boolean compressJs;
@Parameter(property = "validateJson", defaultValue = "true")
private boolean validateJson;
public void execute() throws MojoExecutionException {
try {
logger.info("executing a-web js processor");
targetJsDirectory.mkdirs();
for (File directory : Iterables.concat(Collections.singleton(sourceJsDirectory), Arrays.asList(sourceJsDirectory.listFiles(new FileFilter() {
public boolean accept(File file) {
return file.isDirectory();
}
})))) {
logger.debug("processing js dir = " + directory);
Collection files = FileUtils.listFiles(directory, TrueFileFilter.INSTANCE, directory.equals(sourceJsDirectory) ? null : TrueFileFilter.INSTANCE);
if (files.isEmpty()) {
continue;
}
final Set includedFilesAsCanPath = getIncludedFilesAsCanPath(files);
files = Lists.newArrayList(Iterables.filter(files, new Predicate() {
public boolean apply(File input) {
try {
return !includedFilesAsCanPath.contains(input.getCanonicalPath());
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}));
Multimap filesByPrefix = Multimaps.index(files, new Function() {
public String apply(File file) {
return file.getName().replaceFirst("^([a-z0-9-]*)(_.*)?[.](js)$", "$1");
}
});
String prefix;
if (filesByPrefix.keySet().size() == 1 && !filesByPrefix.keys().iterator().next().isEmpty()) {
prefix = filesByPrefix.keys().iterator().next().replaceFirst("_$", "");
} else if (directory.equals(sourceJsDirectory)) {
prefix = defaultPrefix;
} else {
prefix = defaultPrefix + "-" + directory.getName();
}
prefix = prefix.replaceFirst("[.].*$", "");
processJsFiles(prefix, files);
}
logger.info("executed a-web js processor");
} catch (IOException ex) {
throw new MojoExecutionException("IO error", ex);
}
}
private final Pattern pattern = Pattern.compile("anyplacetools.importFile\\(['\"]([^'\"]+)['\"]\\)", Pattern.DOTALL);
private final Yaml yaml = new Yaml();
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
private Set getIncludedFilesAsCanPath(Iterable files) throws IOException {
Set res = Sets.newHashSet();
for (File file : files) {
Matcher matcher = pattern.matcher(FileUtils.readFileToString(file));
while (matcher.find()) {
String relativePath = Strings.emptyToNull(matcher.group(1));
Preconditions.checkNotNull(relativePath);
res.add(new File(file.getParentFile(), relativePath).getCanonicalPath());
}
}
return res;
}
private static boolean isJson(File file) {
return file.getName().endsWith(".json");
}
private static boolean isYaml(File file) {
return file.getName().endsWith(".yml") || file.getName().endsWith(".yaml");
}
private String readFile(File file) throws IOException {
String data = FileUtils.readFileToString(file);
{
Matcher matcher = pattern.matcher(data);
StringBuffer stringBuffer = new StringBuffer();
while (matcher.find()) {
String relativePath = Strings.emptyToNull(matcher.group(1));
Preconditions.checkNotNull(relativePath);
File importFile = new File(file.getParentFile(), relativePath);
Preconditions.checkArgument(importFile.exists() && importFile.canRead(), "file not fount = %s", importFile);
logger.info("importing " + importFile + " from " + file);
String importData = readFile(importFile);
matcher.appendReplacement(stringBuffer, importData.replace("\\", "\\\\").replace("$", "\\$"));
}
matcher.appendTail(stringBuffer);
data = stringBuffer.toString();
}
if (isJson(file) && validateJson) {
logger.debug("validating json");
JsonParser jsonParser = new JsonParser();
jsonParser.parse(new StringReader(data));
} else if (isYaml(file)) {
Object yamlBean = yaml.load(data);
data = gson.toJson(yamlBean);
}
return data;
}
private void processJsFiles(String prefix, Collection files) throws IOException {
logger.info("processing js = " + prefix);
files = Ordering.usingToString().sortedCopy(files);
String targetFileName = prefix + ".js";
File destFile = new File(targetJsDirectory, targetFileName);
logger.debug("target file = " + destFile);
ByteArrayOutputStream out = new ByteArrayOutputStream();
boolean isLastFileAJson = false;
for (File srcFile : files) {
logger.debug("including file = " + srcFile);
if ((isJson(srcFile) || isYaml(srcFile)) && isLastFileAJson) {
IOUtils.write(",", out);
}
String data = readFile(srcFile);
IOUtils.write("\n\n/** BEGIN FILE " + srcFile.getName() + " **/\n\n", out);
IOUtils.write(data, out);
IOUtils.write("\n\n/** END FILE " + srcFile.getName() + " **/\n\n", out);
isLastFileAJson = (isJson(srcFile) || isYaml(srcFile));
}
out.close();
byte[] jsData = out.toByteArray();
if (compressJs) {
logger.debug("compressing with closure-compiler");
Compiler compiler = new Compiler();
CompilerOptions options = new CompilerOptions();
SourceFile inputFile = SourceFile.fromInputStream(targetFileName, new ByteArrayInputStream(jsData));
compiler.compile(SourceFile.fromCode("external.js", ""), inputFile, options);
jsData = compiler.toSource().getBytes();
}
FileUtils.writeByteArrayToFile(destFile, jsData);
}
}