tools/lint/eslint/__init__.py
author Mark Banner <standard8@mozilla.com>
Tue, 25 Sep 2018 18:15:51 +0000
changeset 438163 951f04d1bb518e722e13b2a15580921590eb2b44
parent 402445 a03df902d42e411c2e082980cdcd376963643eb2
child 439394 c5b037dfba7e9863efea887e6e9bb970ce3102e3
permissions -rw-r--r--
Bug 1482435 - Separate out nodejs finding logic from configure and use it for ESLint. r=firefox-build-system-reviewers,gps This extracts the current logic for finding nodejs into its own module in mozbuild. Configure and ESLint then use it. For ESLint, this will change the first location it looks for nodejs to be the .mozbuild directory. Differential Revision: https://phabricator.services.mozilla.com/D6430

# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import json
import os
import signal
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), "eslint"))
import setup_helper
from mozbuild.nodeutil import find_node_executable

from mozprocess import ProcessHandler

from mozlint import result

ESLINT_ERROR_MESSAGE = """
An error occurred running eslint. Please check the following error messages:

{}
""".strip()

ESLINT_NOT_FOUND_MESSAGE = """
Could not find eslint!  We looked at the --binary option, at the ESLINT
environment variable, and then at your local node_modules path. Please Install
eslint and needed plugins with:

mach eslint --setup

and try again.
""".strip()


def setup(root):
    setup_helper.set_project_root(root)

    if not setup_helper.check_node_executables_valid():
        return 1

    return setup_helper.eslint_maybe_setup()


def lint(paths, config, binary=None, fix=None, setup=None, **lintargs):
    """Run eslint."""
    setup_helper.set_project_root(lintargs['root'])
    module_path = setup_helper.get_project_root()

    # Valid binaries are:
    #  - Any provided by the binary argument.
    #  - Any pointed at by the ESLINT environmental variable.
    #  - Those provided by |mach lint --setup|.

    if not binary:
        binary, _ = find_node_executable()

    if not binary:
        print(ESLINT_NOT_FOUND_MESSAGE)
        return 1

    extra_args = lintargs.get('extra_args') or []
    cmd_args = [binary,
                os.path.join(module_path, "node_modules", "eslint", "bin", "eslint.js"),
                # Enable the HTML plugin.
                # We can't currently enable this in the global config file
                # because it has bad interactions with the SublimeText
                # ESLint plugin (bug 1229874).
                '--plugin', 'html',
                # This keeps ext as a single argument.
                '--ext', '[{}]'.format(','.join(config['extensions'])),
                '--format', 'json',
                ] + extra_args + paths

    # eslint requires that --fix be set before the --ext argument.
    if fix:
        cmd_args.insert(1, '--fix')

    shell = False
    if os.environ.get('MSYSTEM') in ('MINGW32', 'MINGW64'):
        # The eslint binary needs to be run from a shell with msys
        shell = True

    orig = signal.signal(signal.SIGINT, signal.SIG_IGN)
    proc = ProcessHandler(cmd_args, env=os.environ, stream=None, shell=shell)
    proc.run()
    signal.signal(signal.SIGINT, orig)

    try:
        proc.wait()
    except KeyboardInterrupt:
        proc.kill()
        return []

    if not proc.output:
        return []  # no output means success

    try:
        jsonresult = json.loads(proc.output[0])
    except ValueError:
        print(ESLINT_ERROR_MESSAGE.format("\n".join(proc.output)))
        return 1

    results = []
    for obj in jsonresult:
        errors = obj['messages']

        for err in errors:
            err.update({
                'hint': err.get('fix'),
                'level': 'error' if err['severity'] == 2 else 'warning',
                'lineno': err.get('line'),
                'path': obj['filePath'],
                'rule': err.get('ruleId'),
            })
            results.append(result.from_config(config, **err))

    return results