/*
* @package jscodesniffer
* @author sheiko
* @license MIT
* @copyright (c) Dmitry Sheiko http://www.dsheiko.com
* jscs standard:Jquery
* jshint unused:false
* Code style: http://docs.jquery.com/JQuery_Core_Style_Guidelines
*/
/**
* @module lib/Sniffer
* @requires module:lib/SourceCode
* @requires module:lib/SyntaxAnalizer
* @requires module:lib/Mediator
* @requires module:lib/TokenIterator
* @requires module:lib/Utils
* @requires module:lib/Logger
*/
// UMD boilerplate according to https://github.com/umdjs/umd
if ( typeof module === "object" && typeof define !== "function" ) {
/**
* Override AMD `define` function for RequireJS
* @param {function( function, Object, Object )} factory
*/
var define = function ( factory ) {
module.exports = factory( require, exports, module );
},
/**
* Trick RequireJS that tries to resolve `require` calls with no regard to conditions
* @param {string} module
* @returns {*}
*/
load = function( module ) {
return require( module );
};
}
/**
* @param {function} require
*/
define(function( require ) {
"use strict";
var
/** @type {module:lib/SourceCode} */
SourceCode = require( "./SourceCode" ),
/** @type {module:lib/SyntaxAnalizer} */
SyntaxAnalizer = require( "./SyntaxAnalizer" ),
/** @type {module:lib/Mediator} */
Mediator = require( "./Mediator" ),
/** @type {module:lib/TokenIterator} */
TokenIterator = require( "./TokenIterator" ),
/** @type {module:lib/utils} */
utils = require( "./Utils" ),
/** @type {module:lib/Logger} */
Logger = require( "./Logger" );
/**
* Workaround for http://requirejs.org/docs/errors.html#notloaded
*/
if ( typeof requirejs === "function" ) {
require( "../standard/Jquery" );
require( "../standard/Idiomatic" );
}
/**
* @constructor
* @alias module:lib/Sniffer
* @param {function} esprima - for dependency injection
*/
return function( esprima ) {
esprima = esprima || load( "esprima" );
/** @lends module:lib/Sniffer.prototype */
return {
/**
* Lint source code and get results wrapped in a Logger instance
* @access public
* @param {string} text
* @param {Object} options - reference
* @param {Object} [rulesetOverrides={}]
* @param {Object} [injectedSyntaxTree]
* @returns {Object}
*/
getTestResults: function( text, options, rulesetOverrides, injectedSyntaxTree ) {
/** @type {SyntaxAnalizer} */
var analizer,
/** @type {Object} */
syntaxTree,
/** @type {Object} */
standard,
/** @type {RegExp} */
re = /\r/gm,
/** @type {Logger} */
logger = new Logger(),
/** @type {SourceCode} */
sourceCode,
/** @type {Mediator} */
mediator = new Mediator();
// Normalize source code
text = text.replace( re, "" );
sourceCode = new SourceCode( text );
if ( !sourceCode.length ) {
throw new TypeError("Please pass in your source code");
}
rulesetOverrides = rulesetOverrides || {};
syntaxTree = injectedSyntaxTree || this.getSyntaxTree( text, options.src );
options.standard = this.findJscsConfigInComments( syntaxTree.comments ) || options.standard;
if ( options.standard ) {
standard = this.loadStandard ( options.standard );
utils.extend( standard, rulesetOverrides );
} else {
standard = rulesetOverrides;
}
if ( !standard ) {
throw new TypeError( "Cannot infer coding standard" );
}
analizer = new SyntaxAnalizer( standard );
mediator.subscribe( "violation", function( sniff, errorCode, range, loc, payload ){
logger.log( sniff, errorCode, range, loc, payload );
});
analizer.loadSniffs( sourceCode, mediator, new TokenIterator( syntaxTree.tokens ) );
analizer.validateStandard();
analizer.applySourceCodeSniffs();
analizer.applyTokenSniffs( syntaxTree.tokens );
analizer.markNestings( syntaxTree );
analizer.applySyntaxTreeSniffs( syntaxTree );
return logger;
},
/**
* Get Esprima syntax tree
* @access public
* @param {string} srcCode
* @param {string|undefined} [fileInfo]
* @returns {Object}
*/
getSyntaxTree: function( srcCode, fileInfo ) {
var syntaxTree;
try {
syntaxTree = esprima.parse( srcCode, {
comment: true,
range: true,
tokens: true,
loc: true
});
} catch( e ) {
throw new SyntaxError( "Apparently your source code " +
( fileInfo ? "('" + fileInfo + "') " : "" ) +
"isn't valid EcmaScript (" + e.message + "). " );
}
return syntaxTree;
},
/**
* Try to make instance of a given standard
* @access public
* @param {string} standardName
* @returns {Object}
*/
loadStandard: function( standardName ) {
var standard;
try {
standard = require( "../standard/" + standardName );
if ( !standard ) {
throw new ReferenceError( "Cannot find " + standardName + " standard definition " );
}
} catch( e ) {
throw new ReferenceError( "Cannot find " + standardName + " standard definition " );
}
return standard;
},
/**
* Parse source code block comments for @jscs option instructions
* @access public
* @param {Object[]} comments
* @returns {string}
*/
findJscsConfigInComments: function( comments ) {
var testRe = /@?jscs\s+standard:/ig,
matchRe = /@?jscs\s+standard:\s*([a-zA-Z0-9_]+)/i,
standardName;
comments.forEach(function( c ){
var matches;
if ( c.type === "Block" && testRe.test( c.value ) ) {
matches = c.value.match( matchRe );
standardName = (matches && matches[ 1 ]) || null;
}
});
return standardName;
}
};
};
});