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

de.acosix.maven.jshint.RhinoJSHinter Maven / Gradle / Ivy

Go to download

Maven plugin to run JSHint tests against project's JavaScripts sources with support of on-path .jshintrc and .jshintignore configurations

The newest version!
/*
 * Copyright 2016 Acosix GmbH
 *
 * Licensed 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.
 */
package de.acosix.maven.jshint;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.StringUtils;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.JavaScriptException;
import org.mozilla.javascript.NativeJavaObject;
import org.mozilla.javascript.Script;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.WrappedException;

/**
 * @author Axel Faust, Acosix GmbH
 */
public class RhinoJSHinter extends AbstractJSHinter
{

    protected final Object jshintScript;

    protected Scriptable scope;

    protected Script runnerScript;

    public RhinoJSHinter(final Log log, final String versionOrResourcePath, final boolean resourcePath)
    {
        super(log);

        if (StringUtils.isBlank(versionOrResourcePath))
        {
            throw new IllegalArgumentException("versionOrResourcePath not provided");
        }

        if (!resourcePath)
        {
            final String scriptName = "jshint-" + versionOrResourcePath + "-rhino.js";
            this.jshintScript = RhinoJSHinter.class.getResource(scriptName);

            if (this.jshintScript == null)
            {
                this.log.error("JSHint script could not be resolved for version " + versionOrResourcePath);
                throw new RuntimeException(new MojoExecutionException("Error resolving " + scriptName));
            }
        }
        else
        {
            this.jshintScript = RhinoJSHinter.class.getClassLoader().getResource(versionOrResourcePath);

            if (this.jshintScript == null)
            {
                this.log.error("JSHint script could not be resolved from resource path " + versionOrResourcePath);
                throw new RuntimeException(new MojoExecutionException("Error resolving " + versionOrResourcePath));
            }
        }
    }

    public RhinoJSHinter(final Log log, final File jshintScriptFile)
    {
        super(log);
        this.jshintScript = jshintScriptFile;
    }

    /**
     *
     * {@inheritDoc}
     */
    @Override
    protected List executeJSHintImpl(final File baseDirectory, final String path, final String effectiveJSHintConfigContent)
    {
        this.ensureEngineInitialisation();

        final List errors = new ArrayList<>();
        final Context cx = Context.enter();
        try
        {
            final List sourceLines = this.readSourceLines(baseDirectory, path);
            final Scriptable sourceLinesArr = cx.newArray(this.scope, sourceLines.toArray(new Object[0]));

            this.scope.put("errors", this.scope, errors);
            this.scope.put("sourceLines", this.scope, sourceLinesArr);
            this.scope.put("jshintConfig", this.scope, effectiveJSHintConfigContent);

            this.runnerScript.exec(cx, this.scope);
        }
        catch (final JavaScriptException jse)
        {
            // a Java exception triggered via JS code may be wrapped in this
            Object value = jse.getValue();
            if (value instanceof NativeJavaObject)
            {
                value = ((NativeJavaObject) value).unwrap();
            }

            if (value instanceof RuntimeException)
            {
                throw (RuntimeException) value;
            }
            throw jse;
        }
        catch (final WrappedException we)
        {
            final Throwable wrapped = we.getWrappedException();
            if (wrapped instanceof RuntimeException)
            {
                throw (RuntimeException) wrapped;
            }
            throw we;
        }
        finally
        {
            Context.exit();
        }
        return errors;
    }

    protected void ensureEngineInitialisation()
    {
        if (this.scope == null)
        {
            this.log.debug("Initialising Rhino context for JSHint");

            final Context cx = Context.enter();
            try
            {
                this.scope = cx.initStandardObjects(null, false);

                final Script jshintScript = this.compileJSHintScript(cx);
                // execute once to actually load JSHINT
                jshintScript.exec(cx, this.scope);

                // the runner script is our wrapper for repeated execution
                this.runnerScript = this.compileInternalScriptScript(cx, "jshint-rhino-runner.js");
            }
            catch (final IOException ioex)
            {
                this.log.error("Error initialising Rhino context for JSHint");
                throw new RuntimeException(new MojoExecutionException("Error initialising Rhino context for JSHint", ioex));
            }
            finally
            {
                Context.exit();
            }
        }
    }

    protected Script compileJSHintScript(final Context cx) throws IOException
    {
        Script jshintScript;
        if (this.jshintScript instanceof URL)
        {
            final URL jshintScriptURL = (URL) this.jshintScript;
            final InputStream scriptInputStream = jshintScriptURL.openStream();
            try
            {
                jshintScript = this.compileScript(cx, jshintScriptURL.toExternalForm(), scriptInputStream);
            }
            finally
            {
                IOUtil.close(scriptInputStream);
            }
        }
        else if (this.jshintScript instanceof File)
        {
            final InputStream scriptInputStream = new FileInputStream((File) this.jshintScript);
            try
            {
                jshintScript = this.compileScript(cx, ((File) this.jshintScript).getAbsolutePath(), scriptInputStream);
            }
            finally
            {
                IOUtil.close(scriptInputStream);
            }
        }
        else
        {
            throw new RuntimeException(new MojoExecutionException("JSHint script has not been resolved"));
        }

        return jshintScript;
    }

    protected Script compileInternalScriptScript(final Context cx, final String scriptName) throws IOException
    {
        final InputStream scriptInputStream = RhinoJSHinter.class.getResourceAsStream(scriptName);
        try
        {
            final Script script = this.compileScript(cx, scriptName, scriptInputStream);
            return script;
        }
        finally
        {
            IOUtil.close(scriptInputStream);
        }
    }

    protected Script compileScript(final Context cx, final String name, final InputStream is) throws IOException
    {
        final Reader scriptReader = new InputStreamReader(is, StandardCharsets.UTF_8);
        final Script script = cx.compileReader(scriptReader, name, 1, null);
        return script;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy