com.cj.jshintmojo.Mojo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jshint-maven-plugin Show documentation
Show all versions of jshint-maven-plugin Show documentation
a maven mojo that integrates the 'jshint' javascript preprocessor
package com.cj.jshintmojo;
import static com.cj.jshintmojo.util.Util.mkdirs;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.io.FileUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.codehaus.plexus.util.StringUtils;
import com.cj.jshintmojo.cache.Cache;
import com.cj.jshintmojo.cache.Result;
import com.cj.jshintmojo.jshint.FunctionalJava;
import com.cj.jshintmojo.jshint.JSHint;
import com.cj.jshintmojo.jshint.FunctionalJava.Fn;
import com.cj.jshintmojo.jshint.JSHint.Error;
import com.cj.jshintmojo.util.OptionsParser;
import com.cj.jshintmojo.util.Util;
/**
* @goal lint
* @phase compile
*/
public class Mojo extends AbstractMojo {
/**
* @parameter property="directories"
*/
private final List directories = new ArrayList();
/**
* @parameter property="excludes"
*/
private final List excludes = new ArrayList();
/**
* @parameter property="options"
*/
private String options = "";
/**
* @parameter
*/
private String globals = "";
/**
* @parameter property="configFile"
*/
private String configFile = "";
/**
* @parameter
*/
private Boolean failOnError = true;
/**
* @parameter default-value="${basedir}
* @readonly
* @required
*/
private File basedir;
public Mojo() {}
public Mojo(String options, String globals, File basedir, List directories, List excludes, boolean failOnError, String configFile) {
super();
this.options = options;
this.globals = globals;
this.basedir = basedir;
this.directories.addAll(directories);
this.excludes.addAll(excludes);
this.failOnError = failOnError;
this.configFile = configFile;
}
/*
* TODO:
* 1) Add a way to skip (i.e. 'mvn install -Dlint.skip=true')
* 2) Make it parallelizable: i.e. 'mvn install -Dlint.threads=4'
*/
public void execute() throws MojoExecutionException, MojoFailureException {
if (StringUtils.isNotBlank(this.configFile)) {
getLog().info("Reading JSHint settings from configuration file: " + this.configFile);
processConfigFile();
}
if(directories.isEmpty()){
directories.add("src");
}
getLog().debug("Globals are : " + globals);
try {
final File targetPath = new File(basedir, "target");
mkdirs(targetPath);
final File cachePath = new File(targetPath, "lint.cache");
final Cache cache = readCache(cachePath, new Cache(this.options, this.globals));
if(!nullSafeEquals(options, cache.options)){
getLog().warn("Options changed ... clearing cache");
cache.previousResults.clear();
}
if(!nullSafeEquals(globals, cache.globals)){
getLog().warn("Globals changed ... clearing cache");
cache.previousResults.clear();
}
List javascriptFiles = new ArrayList();
for(String next: directories){
File path = new File(basedir, next);
if(!path.exists() && !path.isDirectory()){
getLog().warn("You told me to find tests in " + next + ", but there is nothing there (" + path.getAbsolutePath() + ")");
}else{
collect(path, javascriptFiles);
}
}
List matches = FunctionalJava.filter(javascriptFiles, new Fn(){
public Boolean apply(File i) {
for(String exclude : excludes){
File e = new File(basedir, exclude);
if(i.getAbsolutePath().startsWith(e.getAbsolutePath())){
getLog().warn("Excluding " + i);
return Boolean.FALSE;
}
}
return Boolean.TRUE;
}
});
JSHint jshint = new JSHint();
final Map currentResults = new HashMap();
for(File file : matches){
Result previousResult = cache.previousResults.get(file.getAbsolutePath());
Result theResult;
if(previousResult==null || (previousResult.lastModified.longValue()!=file.lastModified())){
getLog().info(" " + file );
List errors = jshint.run(new FileInputStream(file), options, globals);
theResult = new Result(file.getAbsolutePath(), file.lastModified(), errors);
}else{
getLog().info(" " + file + " [no change]");
theResult = previousResult;
}
if(theResult!=null){
currentResults.put(theResult.path, theResult);
Result r = theResult;
currentResults.put(r.path, r);
for(Error error: r.errors){
getLog().error(" " + error.line.intValue() + "," + error.character.intValue() + ": " + error.reason);
}
}
}
Util.writeObject(new Cache(options, this.globals, currentResults), cachePath);
int numProblematicFiles = 0;
for(Result r : currentResults.values()){
if(!r.errors.isEmpty()){
numProblematicFiles ++;
}
}
if(numProblematicFiles > 0) {
String errorMessage = "JSHint found problems with " + numProblematicFiles + " file";
// pluralise
if (numProblematicFiles > 1) {
errorMessage += "s";
}
if (failOnError) {
throw new MojoExecutionException(errorMessage);
} else {
getLog().info(errorMessage);
}
}
} catch (FileNotFoundException e) {
throw new MojoExecutionException("Something bad happened", e);
}
}
private boolean nullSafeEquals(String a, String b) {
if(a==null && b==null) return true;
else if(a==null || b==null) return false;
else return a.equals(b);
}
private Cache readCache(File path, Cache defaultCache){
try {
if(path.exists()){
return Util.readObject(path);
}
} catch (Throwable e) {
super.getLog().warn("I was unable to read the cache. This may be because of an upgrade to the plugin.");
}
return defaultCache;
}
private void collect(File directory, List files) {
for(File next : directory.listFiles()){
if(next.isDirectory()){
collect(next, files);
}else if(next.getName().endsWith(".js")){
files.add(next);
}
}
}
/**
* Read contents of the specified config file and use the values defined there instead of the ones defined directly in pom.xml config.
*
* @throws MojoExecutionException if the specified file cannot be processed
*/
private void processConfigFile() throws MojoExecutionException {
byte[] configFileContents;
try {
configFileContents = FileUtils.readFileToByteArray(new File(configFile));
} catch (IOException e) {
throw new MojoExecutionException("Unable to read config file located in " + configFile);
}
Set globalsSet = OptionsParser.extractGlobals(configFileContents);
Set optionsSet = OptionsParser.extractOptions(configFileContents);
if (globalsSet.size() > 0) {
this.globals = StringUtils.join(globalsSet.iterator(), ",");
}
if (optionsSet.size() > 0) {
this.options = StringUtils.join(optionsSet.iterator(), ",");
}
}
}