package org.codehaus.mojo.jslint; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.codehaus.plexus.util.Scanner; import org.sonatype.plexus.build.incremental.BuildContext; import com.googlecode.jslint4java.Issue; import com.googlecode.jslint4java.JSLint; import com.googlecode.jslint4java.JSLintBuilder; import com.googlecode.jslint4java.JSLintResult; import com.googlecode.jslint4java.Option; /** * Mojo to invoke JSLint verification on JS sources. */ /** * @author huntc */ public abstract class AbstractJSLintMojo extends AbstractMojo { /** * The current project scope. */ protected enum Scope { /** */ COMPILE, /** */ TEST }; /** * @parameter default-value="**\/*.js" * @required */ private String jsFileExtensions; /** * Stop the build if things go wrong according to JSLint. * * @parameter default-value="true" * @required */ private boolean failOnIssues; /** * true if ADsafe rules should be enforced. @see http://www.ADsafe.org/. * * @parameter default-value="false" * @required */ private boolean adsafe; /** * true if bitwise operators should not be allowed. @see http://www.jslint.com/lint.html#bitwise * * @parameter default-value="true" * @required */ private boolean disallowBitwiseOperators; /** * true if the standard browser globals should be predefined. @see http://www.jslint.com/lint.html#browser * * @parameter default-value="true" * @required */ private boolean assumeABrowser; /** * true if Initial Caps must be used with constructor functions. @see http://www.jslint.com/lint.html#new * * @parameter default-value="true" * @required */ private boolean requireInitialCapsForConstructors; /** * true if CSS workarounds should be tolerated. @see http://www.jslint.com/lint.html#css * * @parameter default-value="false" * @required */ private boolean tolerateCSSWorkarounds; /** * true if debugger statements should be allowed. Set this option to false before going into production. * * @parameter default-value="false" * @required */ private boolean tolerateDebuggerStatements; /** * true if browser globals that are useful in development should be predefined. @see * morehttp://www.jslint.com/lint.html#devel * * @parameter default-value="false" * @required */ private boolean assumeConsoleAlertEtc; /** * true if === and !== should be required. @see http://www.jslint.com/lint.html#equal * * @parameter default-value="true" * @required */ private boolean disallowEQNE; /** * true if ES5 syntax should be allowed. * * @parameter default-value="false" * @required */ private boolean tolerateES5Syntax; /** * true if eval should be allowed. @see http://www.jslint.com/lint.html#evil * * @parameter default-value="false" * @required */ private boolean tolerateEval; /** * true if unfiltered for in statements should be allowed. @see http://www.jslint.com/lint.html#forin * * @parameter default-value="true" * @required */ private boolean tolerateUnfilteredForIn; /** * true if HTML fragments should be allowed. @see http://www.jslint.com/lint.html#html * * @parameter default-value="false" * @required */ private boolean tolerateHTMLFragments; /** * true if immediate function invocations must be wrapped in parens. * * @parameter default-value="true" * @required */ private boolean requireParensAroundImmediateInvocations; /** * true if the ES5 "use strict"; pragma is required. Do not use this option carelessly. * * @parameter default-value="false" * @required */ private boolean strictWhiteSpace; /** * The number of spaces used for indentation. * * @parameter default-value="4" * @required */ private Integer strictWhiteSpaceIndentation; /** * true if statement breaks should not be checked. @see http://www.jslint.com/lint.html#breaking * * @parameter default-value="false" * @required */ private boolean tolerateSloppyLineBreaking; /** * The maximum number of warnings reported. * * @parameter default-value="50" * @required */ private Integer maximumNumberOfErrors; /** * true if upper case HTML should be allowed. * * @parameter default-value="false" * @required */ private boolean tolerateHTMLCase; /** * true if names should be checked for initial or trailing underbars. * * @parameter default-value="true" * @required */ private boolean disallowDanglingUnderbarInIdentifiers; /** * true if HTML event handlers should be allowed. @see http://www.jslint.com/lint.html#html * * @parameter default-value="false" * @required */ private boolean tolerateHTMLEventHandlers; /** * true if only one var statement per function should be allowed. @see http://www.jslint.com/lint.html#scope * * @parameter default-value="true" * @required */ private boolean allowOneVarStatementPerFunction; /** * true if the scan should stop on first error. * * @parameter default-value="false" * @required */ private boolean stopOnFirstError; /** * true if ++ and -- should not be allowed. @see http://www.jslint.com/lint.html#inc * * @parameter default-value="false" * @required */ private boolean disallowIncrAndDecr; /** * An array of strings, the names of predefined global variables. predef is used with the option object, but not * with the jslint comment. Use the var statement to declare global variables in a script file. * * @parameter default-value="false" * @required */ private String predefinedVars; /** * true if . and [^...] should not be allowed in RegExp literals. These forms should not be used when validating in * secure applications. * * @parameter default-value="true" * @required */ private boolean disallowInsecureCharsInRegExp; /** * true if the Rhino environment globals should be predefined. @see http://www.jslint.com/lint.html#rhino * * @parameter default-value="false" * @required */ private boolean assumeRhino; /** * true if the safe subset rules are enforced. These rules are used by <a href="http://www.ADsafe.org/">ADsafe</a>. * It enforces the safe subset rules but not the widget structure rules. * * @parameter default-value="false" * @required */ private boolean safeSubset; /** * true if the ES5 "use strict"; pragma is required. Do not use this option carelessly. * * @parameter default-value="false" * @required */ private boolean requireUseStrict; /** * true if subscript notation may be used for expressions better expressed in dot notation. * * @parameter default-value="false" * @required */ private boolean tolerateInefficientSubscripting; /** * true if variables must be declared before used. @see http://www.jslint.com/lint.html#undefined * * @parameter default-value="true" * @required */ private boolean disallowUndefinedVariables; /** * true if the <a href="http://widgets.yahoo.com/gallery/view.php?widget=37484">Yahoo Widgets</a> globals should be * predefined. @see http://www.jslint.com/lint.html#widget * * @parameter default-value="false" * @required */ private boolean assumeAYahooWidget; /** * true if the Windows globals should be predefined. @see http://www.jslint.com/lint.html#windows * * @parameter default-value="false" * @required */ private boolean assumeWindows; /** * The linter to use. */ private JSLint jsLint; /** * The build context so that we can tell Maven certain files have changed if required. * * @component */ private BuildContext buildContext; /** * Perform the lint on files that have been modified. * * @param sourceFolder the root folder of source files. * @param scope the current mojo scope. * @throws MojoExecutionException if something goes wrong. */ public void doExecute( File sourceFolder, Scope scope ) throws MojoExecutionException { initJSLint(); List<Issue> projectIssues = new ArrayList<Issue>(); Scanner scanner = buildContext.newScanner( sourceFolder ); scanner.setIncludes( jsFileExtensions.split( "," ) ); scanner.scan(); String[] sources = scanner.getIncludedFiles(); for ( String source : sources ) { File sourceFile = new File( sourceFolder, source ); getLog().info( "Parsing: " + source ); try { FileReader fileReader = new FileReader( sourceFile ); try { BufferedReader bufferedFileReader = new BufferedReader( fileReader ); try { JSLintResult result = jsLint.lint( source, bufferedFileReader ); List<Issue> issues = result.getIssues(); projectIssues.addAll( issues ); } finally { bufferedFileReader.close(); } } finally { fileReader.close(); } } catch ( IOException e ) { throw new MojoExecutionException( "Problem while parsing file: " + source, e ); } } if ( projectIssues.size() > 0 ) { for ( Issue issue : projectIssues ) { if ( failOnIssues ) { getLog().error( issue.toString() ); } else { getLog().warn( issue.toString() ); } } if ( failOnIssues ) { throw new MojoExecutionException( "Issues found in project." ); } } } /** * @return property. */ public BuildContext getBuildContext() { return buildContext; } /** * @return property. */ public String getJsFileExtensions() { return jsFileExtensions; } /** * @return property. */ public JSLint getJsLint() { return jsLint; } /** * @return property. */ public Integer getMaximumNumberOfErrors() { return maximumNumberOfErrors; } /** * @return property. */ public String getPredefinedVars() { return predefinedVars; } /** * @return property. */ public int getStrictWhiteSpaceIndentation() { return strictWhiteSpaceIndentation; } /** * Initialise the JSLint environment. * * @throws MojoExecutionException if the init fails. */ protected void initJSLint() throws MojoExecutionException { try { jsLint = new JSLintBuilder().fromDefault(); // Set up options. if ( adsafe ) { jsLint.addOption( Option.ADSAFE ); } if ( disallowBitwiseOperators ) { jsLint.addOption( Option.BITWISE ); } if ( assumeABrowser ) { jsLint.addOption( Option.BROWSER ); } if ( tolerateHTMLCase ) { jsLint.addOption( Option.CAP ); } if ( tolerateCSSWorkarounds ) { jsLint.addOption( Option.CSS ); } if ( tolerateDebuggerStatements ) { jsLint.addOption( Option.DEBUG ); } if ( assumeConsoleAlertEtc ) { jsLint.addOption( Option.DEVEL ); } if ( disallowEQNE ) { jsLint.addOption( Option.EQEQEQ ); } if ( tolerateES5Syntax ) { jsLint.addOption( Option.ES5 ); } if ( tolerateEval ) { jsLint.addOption( Option.EVIL ); } if ( tolerateUnfilteredForIn ) { jsLint.addOption( Option.FORIN ); } if ( tolerateHTMLFragments ) { jsLint.addOption( Option.FRAGMENT ); } if ( requireParensAroundImmediateInvocations ) { jsLint.addOption( Option.IMMED ); } if ( tolerateSloppyLineBreaking ) { jsLint.addOption( Option.LAXBREAK ); } jsLint.addOption( Option.MAXERR, maximumNumberOfErrors.toString() ); if ( requireInitialCapsForConstructors ) { jsLint.addOption( Option.NEWCAP ); } if ( disallowDanglingUnderbarInIdentifiers ) { jsLint.addOption( Option.NOMEN ); } if ( tolerateHTMLEventHandlers ) { jsLint.addOption( Option.ON ); } if ( allowOneVarStatementPerFunction ) { jsLint.addOption( Option.ONEVAR ); } if ( stopOnFirstError ) { jsLint.addOption( Option.PASSFAIL ); } if ( disallowIncrAndDecr ) { jsLint.addOption( Option.PLUSPLUS ); } jsLint.addOption( Option.PREDEF, predefinedVars ); if ( disallowInsecureCharsInRegExp ) { jsLint.addOption( Option.REGEXP ); } if ( assumeRhino ) { jsLint.addOption( Option.RHINO ); } if ( safeSubset ) { jsLint.addOption( Option.SAFE ); } if ( requireUseStrict ) { jsLint.addOption( Option.STRICT ); } if ( tolerateInefficientSubscripting ) { jsLint.addOption( Option.SUB ); } if ( disallowUndefinedVariables ) { jsLint.addOption( Option.UNDEF ); } if ( strictWhiteSpace ) { jsLint.addOption( Option.WHITE ); jsLint.addOption( Option.INDENT, strictWhiteSpaceIndentation.toString() ); } if ( assumeAYahooWidget ) { jsLint.addOption( Option.WIDGET ); } if ( assumeWindows ) { jsLint.addOption( Option.WINDOWS ); } } catch ( IOException e ) { throw new MojoExecutionException( "Problem while initialising JSLint", e ); } } /** * @return property. */ public boolean isADsafe() { return adsafe; } /** * @return property. */ public boolean isAllowOneVarStatementPerFunction() { return allowOneVarStatementPerFunction; } /** * @return property. */ public boolean isAssumeABrowser() { return assumeABrowser; } /** * @return property. */ public boolean isAssumeAYahooWidget() { return assumeAYahooWidget; } /** * @return property. */ public boolean isAssumeConsoleAlertEtc() { return assumeConsoleAlertEtc; } /** * @return property. */ public boolean isAssumeRhino() { return assumeRhino; } /** * @return property. */ public boolean isAssumeWindows() { return assumeWindows; } /** * @return property. */ public boolean isDisallowBitwiseOperators() { return disallowBitwiseOperators; } /** * @return property. */ public boolean isDisallowDanglingUnderbarInIdentifiers() { return disallowDanglingUnderbarInIdentifiers; } /** * @return property. */ public boolean isDisallowEQNE() { return disallowEQNE; } /** * @return property. */ public boolean isDisallowIncrAndDecr() { return disallowIncrAndDecr; } /** * @return property. */ public boolean isDisallowInsecureCharsInRegExp() { return disallowInsecureCharsInRegExp; } /** * @return property. */ public boolean isDisallowUndefinedVariables() { return disallowUndefinedVariables; } /** * @return property. */ public boolean isFailOnIssues() { return failOnIssues; } /** * @return property. */ public boolean isRequireInitialCapsForConstructors() { return requireInitialCapsForConstructors; } /** * @return property. */ public boolean isRequireParensAroundImmediateInvocations() { return requireParensAroundImmediateInvocations; } /** * @return property. */ public boolean isRequireUseStrict() { return requireUseStrict; } /** * @return property. */ public boolean isSafeSubset() { return safeSubset; } /** * @return property. */ public boolean isStopOnFirstError() { return stopOnFirstError; } /** * @return property. */ public boolean isStrictWhiteSpace() { return strictWhiteSpace; } /** * @return property. */ public boolean isTolerateCSSWorkarounds() { return tolerateCSSWorkarounds; } /** * @return property. */ public boolean isTolerateDebuggerStatements() { return tolerateDebuggerStatements; } /** * @return property. */ public boolean isTolerateES5Syntax() { return tolerateES5Syntax; } /** * @return property. */ public boolean isTolerateEval() { return tolerateEval; } /** * @return property. */ public boolean isTolerateHTMLCase() { return tolerateHTMLCase; } /** * @return property. */ public boolean isTolerateHTMLEventHandlers() { return tolerateHTMLEventHandlers; } /** * @return property. */ public boolean isTolerateHTMLFragments() { return tolerateHTMLFragments; } /** * @return property. */ public boolean isTolerateInefficientSubscripting() { return tolerateInefficientSubscripting; } /** * @return property. */ public boolean isTolerateSloppyLineBreaking() { return tolerateSloppyLineBreaking; } /** * @return property. */ public boolean isTolerateUnfilteredForIn() { return tolerateUnfilteredForIn; } /** * @param aDsafe set property. */ public void setADsafe( boolean aDsafe ) { adsafe = aDsafe; } /** * @param allowOneVarStatementPerFunction set property. */ public void setAllowOneVarStatementPerFunction( boolean allowOneVarStatementPerFunction ) { this.allowOneVarStatementPerFunction = allowOneVarStatementPerFunction; } /** * @param assumeABrowser set property. */ public void setAssumeABrowser( boolean assumeABrowser ) { this.assumeABrowser = assumeABrowser; } /** * @param assumeAYahooWidget set property. */ public void setAssumeAYahooWidget( boolean assumeAYahooWidget ) { this.assumeAYahooWidget = assumeAYahooWidget; } /** * @param assumeConsoleAlertEtc set property. */ public void setAssumeConsoleAlertEtc( boolean assumeConsoleAlertEtc ) { this.assumeConsoleAlertEtc = assumeConsoleAlertEtc; } /** * @param assumeRhino set property. */ public void setAssumeRhino( boolean assumeRhino ) { this.assumeRhino = assumeRhino; } /** * @param assumeWindows set property. */ public void setAssumeWindows( boolean assumeWindows ) { this.assumeWindows = assumeWindows; } /** * @param buildContext set property. */ public void setBuildContext( BuildContext buildContext ) { this.buildContext = buildContext; } /** * @param disallowBitwiseOperators set property. */ public void setDisallowBitwiseOperators( boolean disallowBitwiseOperators ) { this.disallowBitwiseOperators = disallowBitwiseOperators; } /** * @param disallowDanglingUnderbarInIdentifiers set property. */ public void setDisallowDanglingUnderbarInIdentifiers( boolean disallowDanglingUnderbarInIdentifiers ) { this.disallowDanglingUnderbarInIdentifiers = disallowDanglingUnderbarInIdentifiers; } /** * @param disallowEQNE set property. */ public void setDisallowEQNE( boolean disallowEQNE ) { this.disallowEQNE = disallowEQNE; } /** * @param disallowIncrAndDecr set property. */ public void setDisallowIncrAndDecr( boolean disallowIncrAndDecr ) { this.disallowIncrAndDecr = disallowIncrAndDecr; } /** * @param disallowInsecureCharsInRegExp set property. */ public void setDisallowInsecureCharsInRegExp( boolean disallowInsecureCharsInRegExp ) { this.disallowInsecureCharsInRegExp = disallowInsecureCharsInRegExp; } /** * @param disallowUndefinedVariables set property. */ public void setDisallowUndefinedVariables( boolean disallowUndefinedVariables ) { this.disallowUndefinedVariables = disallowUndefinedVariables; } /** * @param failOnIssues set property. */ public void setFailOnIssues( boolean failOnIssues ) { this.failOnIssues = failOnIssues; } /** * @param jsFileExtensions set property. */ public void setJsFileExtensions( String jsFileExtensions ) { this.jsFileExtensions = jsFileExtensions; } /** * @param maximumNumberOfErrors set property. */ public void setMaximumNumberOfErrors( Integer maximumNumberOfErrors ) { this.maximumNumberOfErrors = maximumNumberOfErrors; } /** * @param predefinedVars set property. */ public void setPredefinedVars( String predefinedVars ) { this.predefinedVars = predefinedVars; } /** * @param requireInitialCapsForConstructors set property. */ public void setRequireInitialCapsForConstructors( boolean requireInitialCapsForConstructors ) { this.requireInitialCapsForConstructors = requireInitialCapsForConstructors; } /** * @param requireParensAroundImmediateInvocations set property. */ public void setRequireParensAroundImmediateInvocations( boolean requireParensAroundImmediateInvocations ) { this.requireParensAroundImmediateInvocations = requireParensAroundImmediateInvocations; } /** * @param requireUseStrict set property. */ public void setRequireUseStrict( boolean requireUseStrict ) { this.requireUseStrict = requireUseStrict; } /** * @param safeSubset set property. */ public void setSafeSubset( boolean safeSubset ) { this.safeSubset = safeSubset; } /** * @param stopOnFirstError set property. */ public void setStopOnFirstError( boolean stopOnFirstError ) { this.stopOnFirstError = stopOnFirstError; } /** * @param strictWhiteSpace set property. */ public void setStrictWhiteSpace( boolean strictWhiteSpace ) { this.strictWhiteSpace = strictWhiteSpace; } /** * @param strictWhiteSpaceIndentation set property. */ public void setStrictWhiteSpaceIndentation( Integer strictWhiteSpaceIndentation ) { this.strictWhiteSpaceIndentation = strictWhiteSpaceIndentation; } /** * @param tolerateCSSWorkarounds set property. */ public void setTolerateCSSWorkarounds( boolean tolerateCSSWorkarounds ) { this.tolerateCSSWorkarounds = tolerateCSSWorkarounds; } /** * @param tolerateDebuggerStatements set property. */ public void setTolerateDebuggerStatements( boolean tolerateDebuggerStatements ) { this.tolerateDebuggerStatements = tolerateDebuggerStatements; } /** * @param tolerateES5Syntax set property. */ public void setTolerateES5Syntax( boolean tolerateES5Syntax ) { this.tolerateES5Syntax = tolerateES5Syntax; } /** * @param tolerateEval set property. */ public void setTolerateEval( boolean tolerateEval ) { this.tolerateEval = tolerateEval; } /** * @param tolerateHTMLCase set property. */ public void setTolerateHTMLCase( boolean tolerateHTMLCase ) { this.tolerateHTMLCase = tolerateHTMLCase; } /** * @param tolerateHTMLEventHandlers set property. */ public void setTolerateHTMLEventHandlers( boolean tolerateHTMLEventHandlers ) { this.tolerateHTMLEventHandlers = tolerateHTMLEventHandlers; } /** * @param tolerateHTMLFragments set property. */ public void setTolerateHTMLFragments( boolean tolerateHTMLFragments ) { this.tolerateHTMLFragments = tolerateHTMLFragments; } /** * @param tolerateInefficientSubscripting set property. */ public void setTolerateInefficientSubscripting( boolean tolerateInefficientSubscripting ) { this.tolerateInefficientSubscripting = tolerateInefficientSubscripting; } /** * @param tolerateSloppyLineBreaking set property. */ public void setTolerateSloppyLineBreaking( boolean tolerateSloppyLineBreaking ) { this.tolerateSloppyLineBreaking = tolerateSloppyLineBreaking; } /** * @param tolerateUnfilteredForIn set property. */ public void setTolerateUnfilteredForIn( boolean tolerateUnfilteredForIn ) { this.tolerateUnfilteredForIn = tolerateUnfilteredForIn; } }