All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.cj.jshintmojo.Mojo Maven / Gradle / Ivy

There is a newer version: 1.6.0
Show newest version
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(), ",");
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy