81165836aa4d5f617815e05d08da0b3e1569299e.svn-base 4.84 KB
var fs = require('fs'),
    path = require('path'),
    cli = require('cli').enable('glob', 'help'),
    hint = require('./hint');

function existsSync() {
    var obj = fs.existsSync ? fs : path;
    return obj.existsSync.apply(obj, arguments);
}

function _removeJsComments(str) {
    str = str || '';

    // replace everything between "/* */" in a non-greedy way
    // The English version of the regex is:
    //   match '/*'
    //   then match 0 or more instances of any character (including newlines)
    //     except for instances of '*/'
    //   then match '*/'
    str = str.replace(/\/\*(?:(?!\*\/)[\s\S])*\*\//g, '');

    str = str.replace(/\/\/[^\n\r]*/g, ''); //everything after "//"
    return str;
}

function _loadAndParseConfig(filePath) {
    return filePath && existsSync(filePath) ?
            JSON.parse(_removeJsComments(fs.readFileSync(filePath, "utf-8"))) : {};
}

/**
 * This function searches for a file with a specified name, it starts
 * with the dir passed, and traverses up the filesystem until it either
 * finds the file, or hits the root
 *
 * @param {String} name  Filename to search for (.jshintrc, .jshintignore)
 * @param {String} dir   Defaults to process.cwd()
 */
function _searchFile(name, dir) {
    dir = dir || process.cwd();

    var filename = path.normalize(path.join(dir, name)),
        parent = path.resolve(dir, "..");

    if (existsSync(filename)) {
        return filename;
    }

    return dir === parent ? null : _searchFile(name, parent);
}

function _findConfig() {
    var name = ".jshintrc",
        projectConfig = _searchFile(name),
        homeConfig = path.normalize(path.join(process.env.HOME, name));

    if (projectConfig) {
        return projectConfig;
    }

    // if no project config, check $HOME
    if (existsSync(homeConfig)) {
        return homeConfig;
    }

    return false;
}

function _print(results) {
    function exit() {
        process.exit(results.length > 0 ? 1 : 0);
    }

    // avoid stdout cutoff in node 0.4.x, also supports 0.5.x
    // see https://github.com/joyent/node/issues/1669
    try {
        if (!process.stdout.flush()) {
            process.stdout.once("drain", exit);
        } else {
            exit();
        }
    } catch (e) {
        exit();
    }
}

module.exports = {
    interpret: function (args) {
        var config, reporter, options,
            customConfig, customReporter,
            ignoreFile, ignores, extraExtensionList;

        cli.setArgv(args);
        cli.options = {};
        options = cli.parse({
            'version':  ['v', 'display package version', 'boolean', false],
            'config':   ['config', 'custom config file', 'string', false],
            'reporter': ['reporter', 'custom reporter', 'string', undefined],
            'jslint-reporter': ['jslint-reporter', 'use a jslint compatible xml reporter'],
            'checkstyle-reporter': ['checkstyle-reporter', 'use a CheckStyle compatible xml reporter'],
            'show-non-errors': ['show-non-errors', 'show additional data generated by jshint'],
            'extra-ext': ['extra-ext', 'comma-separated list of file extensions to use (.js is default)', 'string', '']
        });

        customConfig = options.config;
        customReporter = options.reporter ? path.resolve(process.cwd(), options.reporter) : null;
        extraExtensionList = options["extra-ext"];

        if (options.version) {
            cli.setApp(path.resolve(__dirname + "/../package.json"));
            process.stdout.write(cli.version + "\n");
            return;
        }

        if (options.help || !cli.args.length) {
            cli.getUsage();
            process.exit();
            return;
        }

        if (options['jslint-reporter']) {
            customReporter = "./reporters/jslint_xml.js";
        }

        if (options['checkstyle-reporter']) {
            customReporter = "./reporters/checkstyle.js";
        }

        if (options['show-non-errors']) {
            customReporter = "./reporters/non_error.js";
        }

        config = _loadAndParseConfig(customConfig ? customConfig : _findConfig());

        if (customReporter) {
            try {
                reporter = require(customReporter).reporter;
            } catch (r) {
                process.stdout.write("Error opening reporter file: " + customReporter);
                process.stdout.write(r + "\n");
                process.exit(1);
            }
        }

        ignoreFile = _searchFile(".jshintignore");

        if (ignoreFile) {
            ignores = fs.readFileSync(ignoreFile, "utf8").split("\n")
                .filter(function (line) {
                    return !!line.trim();
                })
                .map(function (line) {
                    return path.resolve(path.dirname(ignoreFile), line.trim());
                });
        }

        _print(hint.hint(cli.args, config, reporter, ignores, extraExtensionList));
    }
};