function CLIEngine(options) {
options = Object.assign(
Object.create(null),
defaultOptions,
{ cwd: process.cwd() },
options
);
/**
* Stored options for this instance
* @type {Object}
*/
this.options = options;
const cacheFile = getCacheFile(this.options.cacheLocation || this.options.cacheFile, this.options.cwd);
/**
* Cache used to avoid operating on files that haven't changed since the
* last successful execution (e.g., file passed linting with no errors and
* no warnings).
* @type {Object}
*/
this._fileCache = fileEntryCache.create(cacheFile);
// load in additional rules
if (this.options.rulePaths) {
const cwd = this.options.cwd;
this.options.rulePaths.forEach(rulesdir => {
debug(`Loading rules from ${rulesdir}`);
rules.load(rulesdir, cwd);
});
}
Object.keys(this.options.rules || {}).forEach(name => {
validator.validateRuleOptions(name, this.options.rules[name], "CLI");
});
}n/a
function RuleTester(testerConfig) {
/**
* The configuration to use for this tester. Combination of the tester
* configuration and the default configuration.
* @type {Object}
*/
this.testerConfig = lodash.merge(
// we have to clone because merge uses the first argument for recipient
lodash.cloneDeep(defaultConfig),
testerConfig
);
/**
* Rule definitions to define before tests.
* @type {Object}
*/
this.rules = {};
}n/a
function SourceCode(text, ast) {
validate(ast);
/**
* The flag to indicate that the source code has Unicode BOM.
* @type boolean
*/
this.hasBOM = (text.charCodeAt(0) === 0xFEFF);
/**
* The original text source code.
* BOM was stripped from this text.
* @type string
*/
this.text = (this.hasBOM ? text.slice(1) : text);
/**
* The parsed AST for the source code.
* @type ASTNode
*/
this.ast = ast;
/**
* The source code split into lines according to ECMA-262 specification.
* This is done to avoid each rule needing to do so separately.
* @type string[]
*/
this.lines = [];
this.lineStartIndices = [0];
const lineEndingPattern = astUtils.createGlobalLinebreakMatcher();
let match;
/*
* Previously, this was implemented using a regex that
* matched a sequence of non-linebreak characters followed by a
* linebreak, then adding the lengths of the matches. However,
* this caused a catastrophic backtracking issue when the end
* of a file contained a large number of non-newline characters.
* To avoid this, the current implementation just matches newlines
* and uses match.index to get the correct line start indices.
*/
while ((match = lineEndingPattern.exec(this.text))) {
this.lines.push(this.text.slice(this.lineStartIndices[this.lineStartIndices.length - 1], match.index));
this.lineStartIndices.push(match.index + match[0].length);
}
this.lines.push(this.text.slice(this.lineStartIndices[this.lineStartIndices.length - 1]));
this.tokensAndComments = sortedMerge(ast.tokens, ast.comments);
// create token store methods
const tokenStore = new TokenStore(ast.tokens, ast.comments);
for (const methodName of TokenStore.PUBLIC_METHODS) {
this[methodName] = tokenStore[methodName].bind(tokenStore);
}
// don't allow modification of this object
Object.freeze(this);
Object.freeze(this.lines);
}n/a
class RuleContext {
/**
* @param {string} ruleId The ID of the rule using this object.
* @param {eslint} eslint The eslint object.
* @param {number} severity The configured severity level of the rule.
* @param {Array} options The configuration information to be added to the rule.
* @param {Object} settings The configuration settings passed from the config file.
* @param {Object} parserOptions The parserOptions settings passed from the config file.
* @param {Object} parserPath The parser setting passed from the config file.
* @param {Object} meta The metadata of the rule
* @param {Object} parserServices The parser services for the rule.
*/
constructor(ruleId, eslint, severity, options, settings, parserOptions, parserPath, meta, parserServices) {
// public.
this.id = ruleId;
this.options = options;
this.settings = settings;
this.parserOptions = parserOptions;
this.parserPath = parserPath;
this.meta = meta;
// create a separate copy and freeze it (it's not nice to freeze other people's objects)
this.parserServices = Object.freeze(Object.assign({}, parserServices));
// private.
this.eslint = eslint;
this.severity = severity;
Object.freeze(this);
}
/**
* Passthrough to eslint.getSourceCode().
* @returns {SourceCode} The SourceCode object for the code.
*/
getSourceCode() {
return this.eslint.getSourceCode();
}
/**
* Passthrough to eslint.report() that automatically assigns the rule ID and severity.
* @param {ASTNode|MessageDescriptor} nodeOrDescriptor The AST node related to the message or a message
* descriptor.
* @param {Object=} location The location of the error.
* @param {string} message The message to display to the user.
* @param {Object} opts Optional template data which produces a formatted message
* with symbols being replaced by this object's values.
* @returns {void}
*/
report(nodeOrDescriptor, location, message, opts) {
// check to see if it's a new style call
if (arguments.length === 1) {
const descriptor = nodeOrDescriptor;
let fix = null;
// if there's a fix specified, get it
if (typeof descriptor.fix === "function") {
fix = descriptor.fix(ruleFixer);
}
this.eslint.report(
this.id,
this.severity,
descriptor.node,
descriptor.loc || descriptor.node.loc.start,
descriptor.message,
descriptor.data,
fix,
this.meta
);
return;
}
// old style call
this.eslint.report(
this.id,
this.severity,
nodeOrDescriptor,
location,
message,
opts,
this.meta
);
}
}n/a
function CLIEngine(options) {
options = Object.assign(
Object.create(null),
defaultOptions,
{ cwd: process.cwd() },
options
);
/**
* Stored options for this instance
* @type {Object}
*/
this.options = options;
const cacheFile = getCacheFile(this.options.cacheLocation || this.options.cacheFile, this.options.cwd);
/**
* Cache used to avoid operating on files that haven't changed since the
* last successful execution (e.g., file passed linting with no errors and
* no warnings).
* @type {Object}
*/
this._fileCache = fileEntryCache.create(cacheFile);
// load in additional rules
if (this.options.rulePaths) {
const cwd = this.options.cwd;
this.options.rulePaths.forEach(rulesdir => {
debug(`Loading rules from ${rulesdir}`);
rules.load(rulesdir, cwd);
});
}
Object.keys(this.options.rules || {}).forEach(name => {
validator.validateRuleOptions(name, this.options.rules[name], "CLI");
});
}n/a
getErrorResults = function (results) {
const filtered = [];
results.forEach(result => {
const filteredMessages = result.messages.filter(isErrorMessage);
if (filteredMessages.length > 0) {
filtered.push(
Object.assign(result, {
messages: filteredMessages,
errorCount: filteredMessages.length,
warningCount: 0
})
);
}
});
return filtered;
}n/a
getFormatter = function (format) {
let formatterPath;
// default is stylish
format = format || "stylish";
// only strings are valid formatters
if (typeof format === "string") {
// replace \ with / for Windows compatibility
format = format.replace(/\\/g, "/");
// if there's a slash, then it's a file
if (format.indexOf("/") > -1) {
const cwd = this.options ? this.options.cwd : process.cwd();
formatterPath = path.resolve(cwd, format);
} else {
formatterPath = `./formatters/${format}`;
}
try {
return require(formatterPath);
} catch (ex) {
ex.message = `There was a problem loading formatter: ${formatterPath}\nError: ${ex.message}`;
throw ex;
}
} else {
return null;
}
}n/a
outputFixes = function (report) {
report.results.filter(result => result.hasOwnProperty("output")).forEach(result => {
fs.writeFileSync(result.filePath, result.output);
});
}n/a
addPlugin(name, pluginobject) {
Plugins.define(name, pluginobject);
}n/a
function CLIEngine(options) {
options = Object.assign(
Object.create(null),
defaultOptions,
{ cwd: process.cwd() },
options
);
/**
* Stored options for this instance
* @type {Object}
*/
this.options = options;
const cacheFile = getCacheFile(this.options.cacheLocation || this.options.cacheFile, this.options.cwd);
/**
* Cache used to avoid operating on files that haven't changed since the
* last successful execution (e.g., file passed linting with no errors and
* no warnings).
* @type {Object}
*/
this._fileCache = fileEntryCache.create(cacheFile);
// load in additional rules
if (this.options.rulePaths) {
const cwd = this.options.cwd;
this.options.rulePaths.forEach(rulesdir => {
debug(`Loading rules from ${rulesdir}`);
rules.load(rulesdir, cwd);
});
}
Object.keys(this.options.rules || {}).forEach(name => {
validator.validateRuleOptions(name, this.options.rules[name], "CLI");
});
}n/a
executeOnFiles(patterns) {
const results = [],
options = this.options,
fileCache = this._fileCache,
configHelper = new Config(options);
let prevConfig; // the previous configuration used
/**
* Calculates the hash of the config file used to validate a given file
* @param {string} filename The path of the file to retrieve a config object for to calculate the hash
* @returns {string} the hash of the config
*/
function hashOfConfigFor(filename) {
const config = configHelper.getConfig(filename);
if (!prevConfig) {
prevConfig = {};
}
// reuse the previously hashed config if the config hasn't changed
if (prevConfig.config !== config) {
/*
* config changed so we need to calculate the hash of the config
* and the hash of the plugins being used
*/
prevConfig.config = config;
const eslintVersion = pkg.version;
prevConfig.hash = hash(`${eslintVersion}_${stringify(config)}`);
}
return prevConfig.hash;
}
/**
* Executes the linter on a file defined by the `filename`. Skips
* unsupported file extensions and any files that are already linted.
* @param {string} filename The resolved filename of the file to be linted
* @param {boolean} warnIgnored always warn when a file is ignored
* @returns {void}
*/
function executeOnFile(filename, warnIgnored) {
let hashOfConfig,
descriptor;
if (warnIgnored) {
results.push(createIgnoreResult(filename, options.cwd));
return;
}
if (options.cache) {
/*
* get the descriptor for this file
* with the metadata and the flag that determines if
* the file has changed
*/
descriptor = fileCache.getFileDescriptor(filename);
const meta = descriptor.meta || {};
hashOfConfig = hashOfConfigFor(filename);
const changed = descriptor.changed || meta.hashOfConfig !== hashOfConfig;
if (!changed) {
debug(`Skipping file since hasn't changed: ${filename}`);
/*
* Add the the cached results (always will be 0 error and
* 0 warnings). We should not cache results for files that
* failed, in order to guarantee that next execution will
* process those files as well.
*/
results.push(descriptor.meta.results);
// move to the next file
return;
}
} else {
fileCache.destroy();
}
debug(`Processing ${filename}`);
const res = processFile(filename, configHelper, options);
if (options.cache) {
/*
* if a file contains errors or warnings we don't want to
* store the file in the cache so we can guarantee that
* next execution will also operate on this file
*/
if (res.errorCount > 0 || res.warningCount > 0) {
debug(`File has problems, skipping it: ${filename}`);
// remove the entry from the cache
fileCache.removeEntry(filename);
} else {
/*
* since the file passed we store the result here
* TODO: check this as we might not need to store the
* successful runs as it will always should be 0 errors and
* 0 warnings.
*/
descriptor.meta.hashOfConfig = hashOfConfig;
descriptor.meta.results = res;
}
}
results.push(res);
}
const startTime = Date.now();
patterns = this.resolveFileGlobPatterns(patterns);
const fileList = globUtil.listFilesToProcess(patterns, options);
fileList.forEach(fileInfo => {
executeOnFile(fileInfo.filename, fileInfo ...n/a
executeOnText(text, filename, warnIgnored) {
const results = [],
options = this.options,
configHelper = new Config(options),
ignoredPaths = new IgnoredPaths(options);
// resolve filename based on options.cwd (for reporting, ignoredPaths also resolves)
if (filename && !path.isAbsolute(filename)) {
filename = path.resolve(options.cwd, filename);
}
if (filename && ignoredPaths.contains(filename)) {
if (warnIgnored) {
results.push(createIgnoreResult(filename, options.cwd));
}
} else {
results.push(processText(text, configHelper, filename, options.fix, options.allowInlineConfig));
}
const stats = calculateStatsPerRun(results);
return {
results,
errorCount: stats.errorCount,
warningCount: stats.warningCount
};
}n/a
getConfigForFile(filePath) {
const configHelper = new Config(this.options);
return configHelper.getConfig(filePath);
}n/a
getFormatter = function (format) {
let formatterPath;
// default is stylish
format = format || "stylish";
// only strings are valid formatters
if (typeof format === "string") {
// replace \ with / for Windows compatibility
format = format.replace(/\\/g, "/");
// if there's a slash, then it's a file
if (format.indexOf("/") > -1) {
const cwd = this.options ? this.options.cwd : process.cwd();
formatterPath = path.resolve(cwd, format);
} else {
formatterPath = `./formatters/${format}`;
}
try {
return require(formatterPath);
} catch (ex) {
ex.message = `There was a problem loading formatter: ${formatterPath}\nError: ${ex.message}`;
throw ex;
}
} else {
return null;
}
}n/a
isPathIgnored(filePath) {
const resolvedPath = path.resolve(this.options.cwd, filePath);
const ignoredPaths = new IgnoredPaths(this.options);
return ignoredPaths.contains(resolvedPath);
}n/a
resolveFileGlobPatterns(patterns) {
return globUtil.resolveFileGlobPatterns(patterns, this.options);
}n/a
function RuleTester(testerConfig) {
/**
* The configuration to use for this tester. Combination of the tester
* configuration and the default configuration.
* @type {Object}
*/
this.testerConfig = lodash.merge(
// we have to clone because merge uses the first argument for recipient
lodash.cloneDeep(defaultConfig),
testerConfig
);
/**
* Rule definitions to define before tests.
* @type {Object}
*/
this.rules = {};
}n/a
function defaultHandler(text, method) {
return method.apply(this);
}n/a
getDefaultConfig = function () {
return defaultConfig;
}n/a
function defaultHandler(text, method) {
return method.apply(this);
}n/a
resetDefaultConfig = function () {
defaultConfig = lodash.cloneDeep(testerDefaultConfig);
}n/a
setDefaultConfig = function (config) {
if (typeof config !== "object") {
throw new Error("RuleTester.setDefaultConfig: config must be an object");
}
defaultConfig = config;
// Make sure the rules object exists since it is assumed to exist later
defaultConfig.rules = defaultConfig.rules || {};
}n/a
defineRule(name, rule) {
this.rules[name] = rule;
}n/a
run(ruleName, rule, test) {
const testerConfig = this.testerConfig,
requiredScenarios = ["valid", "invalid"],
scenarioErrors = [],
result = {};
if (lodash.isNil(test) || typeof test !== "object") {
throw new Error(`Test Scenarios for rule ${ruleName} : Could not find test scenario object`);
}
requiredScenarios.forEach(scenarioType => {
if (lodash.isNil(test[scenarioType])) {
scenarioErrors.push(`Could not find any ${scenarioType} test scenarios`);
}
});
if (scenarioErrors.length > 0) {
throw new Error([
`Test Scenarios for rule ${ruleName} is invalid:`
].concat(scenarioErrors).join("\n"));
}
/* eslint-disable no-shadow */
/**
* Run the rule for the given item
* @param {string} ruleName name of the rule
* @param {string|Object} item Item to run the rule against
* @returns {Object} Eslint run result
* @private
*/
function runRuleForItem(ruleName, item) {
let config = lodash.cloneDeep(testerConfig),
code, filename, beforeAST, afterAST;
if (typeof item === "string") {
code = item;
} else {
code = item.code;
// Assumes everything on the item is a config except for the
// parameters used by this tester
const itemConfig = lodash.omit(item, RuleTesterParameters);
// Create the config object from the tester config and this item
// specific configurations.
config = lodash.merge(
config,
itemConfig
);
}
if (item.filename) {
filename = item.filename;
}
if (item.options) {
const options = item.options.concat();
options.unshift(1);
config.rules[ruleName] = options;
} else {
config.rules[ruleName] = 1;
}
eslint.defineRule(ruleName, rule);
const schema = validator.getRuleOptionsSchema(ruleName);
if (schema) {
validateSchema(schema);
if (validateSchema.errors) {
throw new Error([
`Schema for rule ${ruleName} is invalid:`
].concat(validateSchema.errors.map(error => `\t${error.field}: ${error.message}`)).join("\n"));
}
}
validator.validate(config, "rule-tester");
/*
* Setup AST getters.
* The goal is to check whether or not AST was modified when
* running the rule under test.
*/
eslint.reset();
eslint.on("Program", node => {
beforeAST = cloneDeeplyExcludesParent(node);
});
eslint.on("Program:exit", node => {
afterAST = node;
});
// Freezes rule-context properties.
const originalGet = rules.get;
try {
rules.get = function(ruleId) {
const rule = originalGet(ruleId);
if (typeof rule === "function") {
return function(context) {
Object.freeze(context);
freezeDeeply(context.options);
freezeDeeply(context.settings);
freezeDeeply(context.parserOptions);
return rule(context);
};
}
return {
meta: rule.meta,
create(context) {
Object.freeze(context);
freezeDeeply(context.options);
freezeDeeply(context.settings);
freezeDeeply(context.parserOptions);
return rule.create(context);
}
};
};
return {
messages: eslint.verify(code, config, filename, true),
beforeAST,
afterAST: cloneDeeplyExcludesParent(afterAST)
};
} finally { ...n/a
function SourceCode(text, ast) {
validate(ast);
/**
* The flag to indicate that the source code has Unicode BOM.
* @type boolean
*/
this.hasBOM = (text.charCodeAt(0) === 0xFEFF);
/**
* The original text source code.
* BOM was stripped from this text.
* @type string
*/
this.text = (this.hasBOM ? text.slice(1) : text);
/**
* The parsed AST for the source code.
* @type ASTNode
*/
this.ast = ast;
/**
* The source code split into lines according to ECMA-262 specification.
* This is done to avoid each rule needing to do so separately.
* @type string[]
*/
this.lines = [];
this.lineStartIndices = [0];
const lineEndingPattern = astUtils.createGlobalLinebreakMatcher();
let match;
/*
* Previously, this was implemented using a regex that
* matched a sequence of non-linebreak characters followed by a
* linebreak, then adding the lengths of the matches. However,
* this caused a catastrophic backtracking issue when the end
* of a file contained a large number of non-newline characters.
* To avoid this, the current implementation just matches newlines
* and uses match.index to get the correct line start indices.
*/
while ((match = lineEndingPattern.exec(this.text))) {
this.lines.push(this.text.slice(this.lineStartIndices[this.lineStartIndices.length - 1], match.index));
this.lineStartIndices.push(match.index + match[0].length);
}
this.lines.push(this.text.slice(this.lineStartIndices[this.lineStartIndices.length - 1]));
this.tokensAndComments = sortedMerge(ast.tokens, ast.comments);
// create token store methods
const tokenStore = new TokenStore(ast.tokens, ast.comments);
for (const methodName of TokenStore.PUBLIC_METHODS) {
this[methodName] = tokenStore[methodName].bind(tokenStore);
}
// don't allow modification of this object
Object.freeze(this);
Object.freeze(this.lines);
}n/a
splitLines = function (text) {
return text.split(astUtils.createGlobalLinebreakMatcher());
}n/a
function SourceCode(text, ast) {
validate(ast);
/**
* The flag to indicate that the source code has Unicode BOM.
* @type boolean
*/
this.hasBOM = (text.charCodeAt(0) === 0xFEFF);
/**
* The original text source code.
* BOM was stripped from this text.
* @type string
*/
this.text = (this.hasBOM ? text.slice(1) : text);
/**
* The parsed AST for the source code.
* @type ASTNode
*/
this.ast = ast;
/**
* The source code split into lines according to ECMA-262 specification.
* This is done to avoid each rule needing to do so separately.
* @type string[]
*/
this.lines = [];
this.lineStartIndices = [0];
const lineEndingPattern = astUtils.createGlobalLinebreakMatcher();
let match;
/*
* Previously, this was implemented using a regex that
* matched a sequence of non-linebreak characters followed by a
* linebreak, then adding the lengths of the matches. However,
* this caused a catastrophic backtracking issue when the end
* of a file contained a large number of non-newline characters.
* To avoid this, the current implementation just matches newlines
* and uses match.index to get the correct line start indices.
*/
while ((match = lineEndingPattern.exec(this.text))) {
this.lines.push(this.text.slice(this.lineStartIndices[this.lineStartIndices.length - 1], match.index));
this.lineStartIndices.push(match.index + match[0].length);
}
this.lines.push(this.text.slice(this.lineStartIndices[this.lineStartIndices.length - 1]));
this.tokensAndComments = sortedMerge(ast.tokens, ast.comments);
// create token store methods
const tokenStore = new TokenStore(ast.tokens, ast.comments);
for (const methodName of TokenStore.PUBLIC_METHODS) {
this[methodName] = tokenStore[methodName].bind(tokenStore);
}
// don't allow modification of this object
Object.freeze(this);
Object.freeze(this.lines);
}n/a
getAllComments() {
return this.ast.comments;
}n/a
getComments(node) {
let leadingComments = node.leadingComments || [];
const trailingComments = node.trailingComments || [];
/*
* espree adds a "comments" array on Program nodes rather than
* leadingComments/trailingComments. Comments are only left in the
* Program node comments array if there is no executable code.
*/
if (node.type === "Program") {
if (node.body.length === 0) {
leadingComments = node.comments;
}
}
return {
leading: leadingComments,
trailing: trailingComments
};
}...
return true;
}
// Checks `@this` in its leading comments for callbacks,
// because callbacks don't have its JSDoc comment.
// e.g.
// sinon.test(/* @this sinon.Sandbox */function() { this.spy(); });
return sourceCode.getComments(node).leading.some(comment => thisTagPattern.test
(comment.value));
}
/**
* Determines if a node is surrounded by parentheses.
* @param {SourceCode} sourceCode The ESLint source code object
* @param {ASTNode} node The node to be checked.
* @returns {boolean} True if the node is parenthesised.
...getIndexFromLoc(loc) {
if (typeof loc !== "object" || typeof loc.line !== "number" || typeof loc.column !== "number") {
throw new TypeError("Expected `loc` to be an object with numeric `line` and `column` properties.");
}
if (loc.line <= 0) {
throw new RangeError(`Line number out of range (line ${loc.line} requested). Line numbers should be 1-based.`);
}
if (loc.line > this.lineStartIndices.length) {
throw new RangeError(`Line number out of range (line ${loc.line} requested, but only ${this.lineStartIndices.length} lines
present).`);
}
const lineStartIndex = this.lineStartIndices[loc.line - 1];
const lineEndIndex = loc.line === this.lineStartIndices.length ? this.text.length : this.lineStartIndices[loc.line];
const positionIndex = lineStartIndex + loc.column;
/*
* By design, getIndexFromLoc({ line: lineNum, column: 0 }) should return the start index of
* the given line, provided that the line number is valid element of this.lines. Since the
* last element of this.lines is an empty string for files with trailing newlines, add a
* special case where getting the index for the first location after the end of the file
* will return the length of the file, rather than throwing an error. This allows rules to
* use getIndexFromLoc consistently without worrying about edge cases at the end of a file.
*/
if (
loc.line === this.lineStartIndices.length && positionIndex > lineEndIndex ||
loc.line < this.lineStartIndices.length && positionIndex >= lineEndIndex
) {
throw new RangeError(`Column number out of range (column ${loc.column} requested, but the length of line ${loc.line} is ${
lineEndIndex - lineStartIndex}).`);
}
return positionIndex;
}n/a
getJSDocComment(node) {
let parent = node.parent;
switch (node.type) {
case "ClassDeclaration":
case "FunctionDeclaration":
if (looksLikeExport(parent)) {
return findJSDocComment(parent.leadingComments, parent.loc.start.line);
}
return findJSDocComment(node.leadingComments, node.loc.start.line);
case "ClassExpression":
return findJSDocComment(parent.parent.leadingComments, parent.parent.loc.start.line);
case "ArrowFunctionExpression":
case "FunctionExpression":
if (parent.type !== "CallExpression" && parent.type !== "NewExpression") {
while (parent && !parent.leadingComments && !/Function/.test(parent.type) && parent.type !== "MethodDefinition" &&
parent.type !== "Property") {
parent = parent.parent;
}
return parent && (parent.type !== "FunctionDeclaration") ? findJSDocComment(parent.leadingComments, parent.loc.start
.line) : null;
} else if (node.leadingComments) {
return findJSDocComment(node.leadingComments, node.loc.start.line);
}
// falls through
default:
return null;
}
}...
/**
* Checks whether or not a node has a `@this` tag in its comments.
* @param {ASTNode} node - A node to check.
* @param {SourceCode} sourceCode - A SourceCode instance to get comments.
* @returns {boolean} Whether or not the node has a `@this` tag in its comments.
*/
function hasJSDocThisTag(node, sourceCode) {
const jsdocComment = sourceCode.getJSDocComment(node);
if (jsdocComment && thisTagPattern.test(jsdocComment.value)) {
return true;
}
// Checks `@this` in its leading comments for callbacks,
// because callbacks don't have its JSDoc comment.
...getLines() {
return this.lines;
}n/a
getLocFromIndex(index) {
if (typeof index !== "number") {
throw new TypeError("Expected `index` to be a number.");
}
if (index < 0 || index > this.text.length) {
throw new RangeError(`Index out of range (requested index ${index}, but source text has length ${this.text.length}).`);
}
/*
* For an argument of this.text.length, return the location one "spot" past the last character
* of the file. If the last character is a linebreak, the location will be column 0 of the next
* line; otherwise, the location will be in the next column on the same line.
*
* See getIndexFromLoc for the motivation for this special case.
*/
if (index === this.text.length) {
return { line: this.lines.length, column: this.lines[this.lines.length - 1].length };
}
/*
* To figure out which line rangeIndex is on, determine the last index at which rangeIndex could
* be inserted into lineIndices to keep the list sorted.
*/
const lineNumber = lodash.sortedLastIndex(this.lineStartIndices, index);
return { line: lineNumber, column: index - this.lineStartIndices[lineNumber - 1] };
}n/a
getNodeByRangeIndex(index) {
let result = null,
resultParent = null;
const traverser = new Traverser();
traverser.traverse(this.ast, {
enter(node, parent) {
if (node.range[0] <= index && index < node.range[1]) {
result = node;
resultParent = parent;
} else {
this.skip();
}
},
leave(node) {
if (node === result) {
this.break();
}
}
});
return result ? Object.assign({ parent: resultParent }, result) : null;
}n/a
getText(node, beforeCount, afterCount) {
if (node) {
return this.text.slice(Math.max(node.range[0] - (beforeCount || 0), 0),
node.range[1] + (afterCount || 0));
}
return this.text;
}...
return {
start: Object.assign({}, start),
end: Object.assign({}, end)
};
},
/**
* Gets the parenthesized text of a node. This is similar to sourceCode.getText(node),
but it also includes any parentheses
* surrounding the node.
* @param {SourceCode} sourceCode The source code object
* @param {ASTNode} node An expression node
* @returns {string} The text representing the node, with all surrounding parentheses included
*/
getParenthesisedText(sourceCode, node) {
let leftToken = sourceCode.getFirstToken(node);
...isSpaceBetweenTokens(first, second) {
const text = this.text.slice(first.range[1], second.range[0]);
return /\s/.test(text.replace(/\/\*.*?\*\//g, ""));
}n/a
function CLIEngine(options) {
options = Object.assign(
Object.create(null),
defaultOptions,
{ cwd: process.cwd() },
options
);
/**
* Stored options for this instance
* @type {Object}
*/
this.options = options;
const cacheFile = getCacheFile(this.options.cacheLocation || this.options.cacheFile, this.options.cwd);
/**
* Cache used to avoid operating on files that haven't changed since the
* last successful execution (e.g., file passed linting with no errors and
* no warnings).
* @type {Object}
*/
this._fileCache = fileEntryCache.create(cacheFile);
// load in additional rules
if (this.options.rulePaths) {
const cwd = this.options.cwd;
this.options.rulePaths.forEach(rulesdir => {
debug(`Loading rules from ${rulesdir}`);
rules.load(rulesdir, cwd);
});
}
Object.keys(this.options.rules || {}).forEach(name => {
validator.validateRuleOptions(name, this.options.rules[name], "CLI");
});
}n/a
function RuleTester(testerConfig) {
/**
* The configuration to use for this tester. Combination of the tester
* configuration and the default configuration.
* @type {Object}
*/
this.testerConfig = lodash.merge(
// we have to clone because merge uses the first argument for recipient
lodash.cloneDeep(defaultConfig),
testerConfig
);
/**
* Rule definitions to define before tests.
* @type {Object}
*/
this.rules = {};
}n/a
function SourceCode(text, ast) {
validate(ast);
/**
* The flag to indicate that the source code has Unicode BOM.
* @type boolean
*/
this.hasBOM = (text.charCodeAt(0) === 0xFEFF);
/**
* The original text source code.
* BOM was stripped from this text.
* @type string
*/
this.text = (this.hasBOM ? text.slice(1) : text);
/**
* The parsed AST for the source code.
* @type ASTNode
*/
this.ast = ast;
/**
* The source code split into lines according to ECMA-262 specification.
* This is done to avoid each rule needing to do so separately.
* @type string[]
*/
this.lines = [];
this.lineStartIndices = [0];
const lineEndingPattern = astUtils.createGlobalLinebreakMatcher();
let match;
/*
* Previously, this was implemented using a regex that
* matched a sequence of non-linebreak characters followed by a
* linebreak, then adding the lengths of the matches. However,
* this caused a catastrophic backtracking issue when the end
* of a file contained a large number of non-newline characters.
* To avoid this, the current implementation just matches newlines
* and uses match.index to get the correct line start indices.
*/
while ((match = lineEndingPattern.exec(this.text))) {
this.lines.push(this.text.slice(this.lineStartIndices[this.lineStartIndices.length - 1], match.index));
this.lineStartIndices.push(match.index + match[0].length);
}
this.lines.push(this.text.slice(this.lineStartIndices[this.lineStartIndices.length - 1]));
this.tokensAndComments = sortedMerge(ast.tokens, ast.comments);
// create token store methods
const tokenStore = new TokenStore(ast.tokens, ast.comments);
for (const methodName of TokenStore.PUBLIC_METHODS) {
this[methodName] = tokenStore[methodName].bind(tokenStore);
}
// don't allow modification of this object
Object.freeze(this);
Object.freeze(this.lines);
}n/a
couldBeError(node) {
switch (node.type) {
case "Identifier":
case "CallExpression":
case "NewExpression":
case "MemberExpression":
case "TaggedTemplateExpression":
case "YieldExpression":
case "AwaitExpression":
return true; // possibly an error object.
case "AssignmentExpression":
return module.exports.couldBeError(node.right);
case "SequenceExpression": {
const exprs = node.expressions;
return exprs.length !== 0 && module.exports.couldBeError(exprs[exprs.length - 1]);
}
case "LogicalExpression":
return module.exports.couldBeError(node.left) || module.exports.couldBeError(node.right);
case "ConditionalExpression":
return module.exports.couldBeError(node.consequent) || module.exports.couldBeError(node.alternate);
default:
return false;
}
}...
case "MemberExpression":
case "TaggedTemplateExpression":
case "YieldExpression":
case "AwaitExpression":
return true; // possibly an error object.
case "AssignmentExpression":
return module.exports.couldBeError(node.right);
case "SequenceExpression": {
const exprs = node.expressions;
return exprs.length !== 0 && module.exports.couldBeError(exprs[exprs.length - 1]);
}
...function createGlobalLinebreakMatcher() {
return new RegExp(LINEBREAK_MATCHER.source, "g");
}n/a
getDirectivePrologue(node) {
const directives = [];
// Directive prologues only occur at the top of files or functions.
if (
node.type === "Program" ||
node.type === "FunctionDeclaration" ||
node.type === "FunctionExpression" ||
// Do not check arrow functions with implicit return.
// `() => "use strict";` returns the string `"use strict"`.
(node.type === "ArrowFunctionExpression" && node.body.type === "BlockStatement")
) {
const statements = node.type === "Program" ? node.body : node.body.body;
for (const statement of statements) {
if (
statement.type === "ExpressionStatement" &&
statement.expression.type === "Literal"
) {
directives.push(statement);
} else {
break;
}
}
}
return directives;
}n/a
getFunctionHeadLoc(node, sourceCode) {
const parent = node.parent;
let start = null;
let end = null;
if (node.type === "ArrowFunctionExpression") {
const arrowToken = sourceCode.getTokenBefore(node.body, isArrowToken);
start = arrowToken.loc.start;
end = arrowToken.loc.end;
} else if (parent.type === "Property" || parent.type === "MethodDefinition") {
start = parent.loc.start;
end = getOpeningParenOfParams(node, sourceCode).loc.start;
} else {
start = node.loc.start;
end = getOpeningParenOfParams(node, sourceCode).loc.start;
}
return {
start: Object.assign({}, start),
end: Object.assign({}, end)
};
}n/a
getFunctionNameWithKind(node) {
const parent = node.parent;
const tokens = [];
if (parent.type === "MethodDefinition" && parent.static) {
tokens.push("static");
}
if (node.async) {
tokens.push("async");
}
if (node.generator) {
tokens.push("generator");
}
if (node.type === "ArrowFunctionExpression") {
tokens.push("arrow", "function");
} else if (parent.type === "Property" || parent.type === "MethodDefinition") {
if (parent.kind === "constructor") {
return "constructor";
} else if (parent.kind === "get") {
tokens.push("getter");
} else if (parent.kind === "set") {
tokens.push("setter");
} else {
tokens.push("method");
}
} else {
tokens.push("function");
}
if (node.id) {
tokens.push(`'${node.id.name}'`);
} else {
const name = module.exports.getStaticPropertyName(parent);
if (name) {
tokens.push(`'${name}'`);
}
}
return tokens.join(" ");
}n/a
getLabel(node) {
if (node.parent.type === "LabeledStatement") {
return node.parent.label.name;
}
return null;
}n/a
getModifyingReferences(references) {
return references.filter(isModifyingReference);
}n/a
getParenthesisedText(sourceCode, node) {
let leftToken = sourceCode.getFirstToken(node);
let rightToken = sourceCode.getLastToken(node);
while (
sourceCode.getTokenBefore(leftToken) &&
sourceCode.getTokenBefore(leftToken).type === "Punctuator" &&
sourceCode.getTokenBefore(leftToken).value === "(" &&
sourceCode.getTokenAfter(rightToken) &&
sourceCode.getTokenAfter(rightToken).type === "Punctuator" &&
sourceCode.getTokenAfter(rightToken).value === ")"
) {
leftToken = sourceCode.getTokenBefore(leftToken);
rightToken = sourceCode.getTokenAfter(rightToken);
}
return sourceCode.getText().slice(leftToken.range[0], rightToken.range[1]);
}n/a
getPrecedence(node) {
switch (node.type) {
case "SequenceExpression":
return 0;
case "AssignmentExpression":
case "ArrowFunctionExpression":
case "YieldExpression":
return 1;
case "ConditionalExpression":
return 3;
case "LogicalExpression":
switch (node.operator) {
case "||":
return 4;
case "&&":
return 5;
// no default
}
/* falls through */
case "BinaryExpression":
switch (node.operator) {
case "|":
return 6;
case "^":
return 7;
case "&":
return 8;
case "==":
case "!=":
case "===":
case "!==":
return 9;
case "<":
case "<=":
case ">":
case ">=":
case "in":
case "instanceof":
return 10;
case "<<":
case ">>":
case ">>>":
return 11;
case "+":
case "-":
return 12;
case "*":
case "/":
case "%":
return 13;
case "**":
return 15;
// no default
}
/* falls through */
case "UnaryExpression":
case "AwaitExpression":
return 16;
case "UpdateExpression":
return 17;
case "CallExpression":
// IIFE is allowed to have parens in any position (#655)
if (node.callee.type === "FunctionExpression") {
return -1;
}
return 18;
case "NewExpression":
return 19;
// no default
}
return 20;
}n/a
getStaticPropertyName(node) {
let prop;
switch (node && node.type) {
case "Property":
case "MethodDefinition":
prop = node.key;
break;
case "MemberExpression":
prop = node.property;
break;
// no default
}
switch (prop && prop.type) {
case "Literal":
return String(prop.value);
case "TemplateLiteral":
if (prop.expressions.length === 0 && prop.quasis.length === 1) {
return prop.quasis[0].value.cooked;
}
break;
case "Identifier":
if (!node.computed) {
return prop.name;
}
break;
// no default
}
return null;
}...
} else {
tokens.push("function");
}
if (node.id) {
tokens.push(`'${node.id.name}'`);
} else {
const name = module.exports.getStaticPropertyName(parent);
if (name) {
tokens.push(`'${name}'`);
}
}
return tokens.join(" ");
...function trailingStatement(node) {
switch (node.type) {
case 'IfStatement':
if (node.alternate != null) {
return node.alternate;
}
return node.consequent;
case 'LabeledStatement':
case 'ForStatement':
case 'ForInStatement':
case 'WhileStatement':
case 'WithStatement':
return node.body;
}
return null;
}n/a
function getUpperFunction(node) {
while (node) {
if (anyFunctionPattern.test(node.type)) {
return node;
}
node = node.parent;
}
return null;
}n/a
getVariableByName(initScope, name) {
let scope = initScope;
while (scope) {
const variable = scope.set.get(name);
if (variable) {
return variable;
}
scope = scope.upper;
}
return null;
}n/a
function isArrayFromMethod(node) {
return (
node.type === "MemberExpression" &&
node.object.type === "Identifier" &&
arrayOrTypedArrayPattern.test(node.object.name) &&
node.property.type === "Identifier" &&
node.property.name === "from" &&
node.computed === false
);
}n/a
function isArrowToken(token) {
return token.value === "=>" && token.type === "Punctuator";
}n/a
isBreakableStatement(node) {
return breakableTypePattern.test(node.type);
}n/a
function isCallee(node) {
return node.parent.type === "CallExpression" && node.parent.callee === node;
}n/a
function isClosingBraceToken(token) {
return token.value === "}" && token.type === "Punctuator";
}n/a
function isClosingBracketToken(token) {
return token.value === "]" && token.type === "Punctuator";
}n/a
function isClosingParenToken(token) {
return token.value === ")" && token.type === "Punctuator";
}n/a
function isColonToken(token) {
return token.value === ":" && token.type === "Punctuator";
}n/a
function isCommaToken(token) {
return token.value === "," && token.type === "Punctuator";
}n/a
function isCommentToken(token) {
return token.type === "Line" || token.type === "Block" || token.type === "Shebang";
}n/a
isDecimalInteger(node) {
return node.type === "Literal" && typeof node.value === "number" && /^(0|[1-9]\d*)$/.test(node.raw);
}n/a
isDefaultThisBinding(node, sourceCode) {
if (isES5Constructor(node) || hasJSDocThisTag(node, sourceCode)) {
return false;
}
const isAnonymous = node.id === null;
while (node) {
const parent = node.parent;
switch (parent.type) {
/*
* Looks up the destination.
* e.g., obj.foo = nativeFoo || function foo() { ... };
*/
case "LogicalExpression":
case "ConditionalExpression":
node = parent;
break;
// If the upper function is IIFE, checks the destination of the return value.
// e.g.
// obj.foo = (function() {
// // setup...
// return function foo() { ... };
// })();
case "ReturnStatement": {
const func = getUpperFunction(parent);
if (func === null || !isCallee(func)) {
return true;
}
node = func.parent;
break;
}
// e.g.
// var obj = { foo() { ... } };
// var obj = { foo: function() { ... } };
// class A { constructor() { ... } }
// class A { foo() { ... } }
// class A { get foo() { ... } }
// class A { set foo() { ... } }
// class A { static foo() { ... } }
case "Property":
case "MethodDefinition":
return parent.value !== node;
// e.g.
// obj.foo = function foo() { ... };
// Foo = function() { ... };
// [obj.foo = function foo() { ... }] = a;
// [Foo = function() { ... }] = a;
case "AssignmentExpression":
case "AssignmentPattern":
if (parent.right === node) {
if (parent.left.type === "MemberExpression") {
return false;
}
if (isAnonymous &&
parent.left.type === "Identifier" &&
startsWithUpperCase(parent.left.name)
) {
return false;
}
}
return true;
// e.g.
// var Foo = function() { ... };
case "VariableDeclarator":
return !(
isAnonymous &&
parent.init === node &&
parent.id.type === "Identifier" &&
startsWithUpperCase(parent.id.name)
);
// e.g.
// var foo = function foo() { ... }.bind(obj);
// (function foo() { ... }).call(obj);
// (function foo() { ... }).apply(obj, []);
case "MemberExpression":
return (
parent.object !== node ||
parent.property.type !== "Identifier" ||
!bindOrCallOrApplyPattern.test(parent.property.name) ||
!isCallee(parent) ||
parent.parent.arguments.length === 0 ||
isNullOrUndefined(parent.parent.arguments[0])
);
// e.g.
// Reflect.apply(function() {}, obj, []);
// Array.from([], function() {}, obj);
// list.forEach(function() {}, obj);
case "CallExpression":
if (isReflectApply(parent.callee)) {
return (
parent.arguments.length !== 3 ||
parent.arguments[0] !== node ||
isNullOrUndefined(parent.arguments[1])
);
}
if (isArrayFromMethod(parent.callee)) {
return (
parent.arguments.length !== 3 ||
parent.arguments[1] !== node ||
isNullOrUndefined(parent.arguments[2])
);
} ...n/a
isDirectiveComment(node) {
const comment = node.value.trim();
return (
node.type === "Line" && comment.indexOf("eslint-") === 0 ||
node.type === "Block" && (
comment.indexOf("global ") === 0 ||
comment.indexOf("eslint ") === 0 ||
comment.indexOf("eslint-") === 0
)
);
}n/a
function isES5Constructor(node) {
return (node.id && startsWithUpperCase(node.id.name));
}n/a
isEmptyBlock(node) {
return Boolean(node && node.type === "BlockStatement" && node.body.length === 0);
}...
/**
* Checks whether the given node is an empty function node or not.
*
* @param {ASTNode|null} node - The node to check.
* @returns {boolean} `true` if the node is an empty function.
*/
isEmptyFunction(node) {
return isFunction(node) && module.exports.isEmptyBlock(node.body);
},
/**
* Gets the property name of a given node.
* The node can be a MemberExpression, a Property, or a MethodDefinition.
*
* If the name is dynamic, this returns `null`.
...isEmptyFunction(node) {
return isFunction(node) && module.exports.isEmptyBlock(node.body);
}n/a
function isFunction(node) {
return Boolean(node && anyFunctionPattern.test(node.type));
}n/a
function isInLoop(node) {
while (node && !isFunction(node)) {
if (isLoop(node)) {
return true;
}
node = node.parent;
}
return false;
}n/a
function isKeywordToken(token) {
return token.type === "Keyword";
}n/a
function isLoop(node) {
return Boolean(node && anyLoopPattern.test(node.type));
}n/a
token => !f(token)
n/a
token => !f(token)
n/a
token => !f(token)
n/a
token => !f(token)
n/a
token => !f(token)
n/a
token => !f(token)
n/a
token => !f(token)
n/a
token => !f(token)
n/a
token => !f(token)
n/a
isNullLiteral(node) {
/*
* Checking `node.value === null` does not guarantee that a literal is a null literal.
* When parsing values that cannot be represented in the current environment (e.g. unicode
* regexes in Node 4), `node.value` is set to `null` because it wouldn't be possible to
* set `node.value` to a unicode regex. To make sure a literal is actually `null`, check
* `node.regex` instead. Also see: https://github.com/eslint/eslint/issues/8020
*/
return node.type === "Literal" && node.value === null && !node.regex;
}...
* Checks whether or not a node is `null` or `undefined`.
* @param {ASTNode} node - A node to check.
* @returns {boolean} Whether or not the node is a `null` or `undefined`.
* @public
*/
function isNullOrUndefined(node) {
return (
module.exports.isNullLiteral(node) ||
(node.type === "Identifier" && node.name === "undefined") ||
(node.type === "UnaryExpression" && node.operator === "void")
);
}
/**
* Checks whether or not a node is callee.
...function isNullOrUndefined(node) {
return (
module.exports.isNullLiteral(node) ||
(node.type === "Identifier" && node.name === "undefined") ||
(node.type === "UnaryExpression" && node.operator === "void")
);
}n/a
function isOpeningBraceToken(token) {
return token.value === "{" && token.type === "Punctuator";
}n/a
function isOpeningBracketToken(token) {
return token.value === "[" && token.type === "Punctuator";
}n/a
function isOpeningParenToken(token) {
return token.value === "(" && token.type === "Punctuator";
}n/a
function isParenthesised(sourceCode, node) {
const previousToken = sourceCode.getTokenBefore(node),
nextToken = sourceCode.getTokenAfter(node);
return Boolean(previousToken && nextToken) &&
previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
nextToken.value === ")" && nextToken.range[0] >= node.range[1];
}n/a
function isSemicolonToken(token) {
return token.value === ";" && token.type === "Punctuator";
}n/a
isStringLiteral(node) {
return (
(node.type === "Literal" && typeof node.value === "string") ||
node.type === "TemplateLiteral"
);
}n/a
isSurroundedBy(val, character) {
return val[0] === character && val[val.length - 1] === character;
}n/a
isTokenOnSameLine(left, right) {
return left.loc.end.line === right.loc.start.line;
}n/a
defaults = function () {
return require("../conf/eslint-recommended");
}n/a
defineRule = function (ruleId, ruleModule) {
rules.define(ruleId, ruleModule);
}n/a
defineRules = function (rulesToDefine) {
Object.getOwnPropertyNames(rulesToDefine).forEach(ruleId => {
defineRule(ruleId, rulesToDefine[ruleId]);
});
}n/a
getAllComments = function (a, b, c, d, e) {
if (sourceCode) {
return sourceCode[exMethodName](a, b, c, d, e);
}
return null;
}n/a
getAncestors = function () {
return traverser.parents();
}n/a
getComments = function (a, b, c, d, e) {
if (sourceCode) {
return sourceCode[exMethodName](a, b, c, d, e);
}
return null;
}...
return true;
}
// Checks `@this` in its leading comments for callbacks,
// because callbacks don't have its JSDoc comment.
// e.g.
// sinon.test(/* @this sinon.Sandbox */function() { this.spy(); });
return sourceCode.getComments(node).leading.some(comment => thisTagPattern.test
(comment.value));
}
/**
* Determines if a node is surrounded by parentheses.
* @param {SourceCode} sourceCode The ESLint source code object
* @param {ASTNode} node The node to be checked.
* @returns {boolean} True if the node is parenthesised.
...getDeclaredVariables = function (node) {
return (scopeManager && scopeManager.getDeclaredVariables(node)) || [];
}n/a
getFilename = function () {
if (typeof currentFilename === "string") {
return currentFilename;
}
return "<input>";
}n/a
getFirstToken = function (a, b, c, d, e) {
if (sourceCode) {
return sourceCode[exMethodName](a, b, c, d, e);
}
return null;
}...
* @param {ASTNode} node - The function node to get.
* @param {SourceCode} sourceCode - The source code object to get tokens.
* @returns {Token} `(` token.
*/
function getOpeningParenOfParams(node, sourceCode) {
return node.id
? sourceCode.getTokenAfter(node.id, isOpeningParenToken)
: sourceCode.getFirstToken(node, isOpeningParenToken);
}
/**
* Creates a version of the LINEBREAK_MATCHER regex with the global flag.
* Global regexes are mutable, so this needs to be a function instead of a constant.
* @returns {RegExp} A global regular expression that matches line terminators
*/
...getFirstTokens = function (a, b, c, d, e) {
if (sourceCode) {
return sourceCode[exMethodName](a, b, c, d, e);
}
return null;
}n/a
getJSDocComment = function (a, b, c, d, e) {
if (sourceCode) {
return sourceCode[exMethodName](a, b, c, d, e);
}
return null;
}...
/**
* Checks whether or not a node has a `@this` tag in its comments.
* @param {ASTNode} node - A node to check.
* @param {SourceCode} sourceCode - A SourceCode instance to get comments.
* @returns {boolean} Whether or not the node has a `@this` tag in its comments.
*/
function hasJSDocThisTag(node, sourceCode) {
const jsdocComment = sourceCode.getJSDocComment(node);
if (jsdocComment && thisTagPattern.test(jsdocComment.value)) {
return true;
}
// Checks `@this` in its leading comments for callbacks,
// because callbacks don't have its JSDoc comment.
...getLastToken = function (a, b, c, d, e) {
if (sourceCode) {
return sourceCode[exMethodName](a, b, c, d, e);
}
return null;
}...
* surrounding the node.
* @param {SourceCode} sourceCode The source code object
* @param {ASTNode} node An expression node
* @returns {string} The text representing the node, with all surrounding parentheses included
*/
getParenthesisedText(sourceCode, node) {
let leftToken = sourceCode.getFirstToken(node);
let rightToken = sourceCode.getLastToken(node);
while (
sourceCode.getTokenBefore(leftToken) &&
sourceCode.getTokenBefore(leftToken).type === "Punctuator" &&
sourceCode.getTokenBefore(leftToken).value === "(" &&
sourceCode.getTokenAfter(rightToken) &&
sourceCode.getTokenAfter(rightToken).type === "Punctuator" &&
...getLastTokens = function (a, b, c, d, e) {
if (sourceCode) {
return sourceCode[exMethodName](a, b, c, d, e);
}
return null;
}n/a
getNodeByRangeIndex = function (a, b, c, d, e) {
if (sourceCode) {
return sourceCode[exMethodName](a, b, c, d, e);
}
return null;
}n/a
getRules = function () {
return rules.getAllLoadedRules();
}n/a
getScope = function () {
const parents = traverser.parents();
// Don't do this for Program nodes - they have no parents
if (parents.length) {
// if current node introduces a scope, add it to the list
const current = traverser.current();
if (currentConfig.parserOptions.ecmaVersion >= 6) {
if (["BlockStatement", "SwitchStatement", "CatchClause", "FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression
"].indexOf(current.type) >= 0) {
parents.push(current);
}
} else {
if (["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"].indexOf(current.type) >= 0) {
parents.push(current);
}
}
// Ascend the current node's parents
for (let i = parents.length - 1; i >= 0; --i) {
// Get the innermost scope
const scope = scopeManager.acquire(parents[i], true);
if (scope) {
if (scope.type === "function-expression-name") {
return scope.childScopes[0];
}
return scope;
}
}
}
return currentScopes[0];
}n/a
getSource = function (a, b, c, d, e) {
if (sourceCode) {
return sourceCode[exMethodName](a, b, c, d, e);
}
return null;
}n/a
getSourceCode = function () {
return sourceCode;
}...
this.eslint = eslint;
this.severity = severity;
Object.freeze(this);
}
/**
* Passthrough to eslint.getSourceCode().
* @returns {SourceCode} The SourceCode object for the code.
*/
getSourceCode() {
return this.eslint.getSourceCode();
}
/**
...getSourceLines = function (a, b, c, d, e) {
if (sourceCode) {
return sourceCode[exMethodName](a, b, c, d, e);
}
return null;
}n/a
getTokenAfter = function (a, b, c, d, e) {
if (sourceCode) {
return sourceCode[exMethodName](a, b, c, d, e);
}
return null;
}...
* @param {SourceCode} sourceCode The ESLint source code object
* @param {ASTNode} node The node to be checked.
* @returns {boolean} True if the node is parenthesised.
* @private
*/
function isParenthesised(sourceCode, node) {
const previousToken = sourceCode.getTokenBefore(node),
nextToken = sourceCode.getTokenAfter(node);
return Boolean(previousToken && nextToken) &&
previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
nextToken.value === ")" && nextToken.range[0] >= node.range[1];
}
/**
...getTokenBefore = function (a, b, c, d, e) {
if (sourceCode) {
return sourceCode[exMethodName](a, b, c, d, e);
}
return null;
}...
* Determines if a node is surrounded by parentheses.
* @param {SourceCode} sourceCode The ESLint source code object
* @param {ASTNode} node The node to be checked.
* @returns {boolean} True if the node is parenthesised.
* @private
*/
function isParenthesised(sourceCode, node) {
const previousToken = sourceCode.getTokenBefore(node),
nextToken = sourceCode.getTokenAfter(node);
return Boolean(previousToken && nextToken) &&
previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
nextToken.value === ")" && nextToken.range[0] >= node.range[1];
}
...getTokenByRangeStart = function (a, b, c, d, e) {
if (sourceCode) {
return sourceCode[exMethodName](a, b, c, d, e);
}
return null;
}n/a
getTokens = function (a, b, c, d, e) {
if (sourceCode) {
return sourceCode[exMethodName](a, b, c, d, e);
}
return null;
}n/a
getTokensAfter = function (a, b, c, d, e) {
if (sourceCode) {
return sourceCode[exMethodName](a, b, c, d, e);
}
return null;
}n/a
getTokensBefore = function (a, b, c, d, e) {
if (sourceCode) {
return sourceCode[exMethodName](a, b, c, d, e);
}
return null;
}n/a
getTokensBetween = function (a, b, c, d, e) {
if (sourceCode) {
return sourceCode[exMethodName](a, b, c, d, e);
}
return null;
}n/a
markVariableAsUsed = function (name) {
const hasGlobalReturn = currentConfig.parserOptions.ecmaFeatures && currentConfig.parserOptions.ecmaFeatures.globalReturn,
specialScope = hasGlobalReturn || currentConfig.parserOptions.sourceType === "module";
let scope = this.getScope(),
i,
len;
// Special Node.js scope means we need to start one level deeper
if (scope.type === "global" && specialScope) {
scope = scope.childScopes[0];
}
do {
const variables = scope.variables;
for (i = 0, len = variables.length; i < len; i++) {
if (variables[i].name === name) {
variables[i].eslintUsed = true;
return true;
}
}
} while ((scope = scope.upper));
return false;
}n/a
report = function (ruleId, severity, node, location, message, opts, fix, meta) {
if (node) {
assert.strictEqual(typeof node, "object", "Node must be an object");
}
if (typeof location === "string") {
assert.ok(node, "Node must be provided when reporting error if location is not provided");
meta = fix;
fix = opts;
opts = message;
message = location;
location = node.loc.start;
}
// Store end location.
const endLocation = location.end;
location = location.start || location;
if (isDisabledByReportingConfig(reportingConfig, ruleId, location)) {
return;
}
if (opts) {
message = message.replace(/\{\{\s*([^{}]+?)\s*\}\}/g, (fullMatch, term) => {
if (term in opts) {
return opts[term];
}
// Preserve old behavior: If parameter name not provided, don't replace it.
return fullMatch;
});
}
const problem = {
ruleId,
severity,
message,
line: location.line,
column: location.column + 1, // switch to 1-base instead of 0-base
nodeType: node && node.type,
source: sourceCode.lines[location.line - 1] || ""
};
// Define endLine and endColumn if exists.
if (endLocation) {
problem.endLine = endLocation.line;
problem.endColumn = endLocation.column + 1; // switch to 1-base instead of 0-base
}
// ensure there's range and text properties, otherwise it's not a valid fix
if (fix && Array.isArray(fix.range) && (typeof fix.text === "string")) {
// If rule uses fix, has metadata, but has no metadata.fixable, we should throw
if (meta && !meta.fixable) {
throw new Error("Fixable rules should export a `meta.fixable` property.");
}
problem.fix = fix;
}
messages.push(problem);
}...
* @returns {SourceCode} The SourceCode object for the code.
*/
getSourceCode() {
return this.eslint.getSourceCode();
}
/**
* Passthrough to eslint.report() that automatically assigns the rule ID and severity
.
* @param {ASTNode|MessageDescriptor} nodeOrDescriptor The AST node related to the message or a message
* descriptor.
* @param {Object=} location The location of the error.
* @param {string} message The message to display to the user.
* @param {Object} opts Optional template data which produces a formatted message
* with symbols being replaced by this object's values.
* @returns {void}
...reset = function () {
this.removeAllListeners();
messages = [];
currentConfig = null;
currentScopes = null;
scopeManager = null;
traverser = null;
reportingConfig = [];
sourceCode = null;
}n/a
verify = function (textOrSourceCode, config, filenameOrOptions, saveState) {
const text = (typeof textOrSourceCode === "string") ? textOrSourceCode : null;
let ast,
parseResult,
shebang,
allowInlineConfig;
// evaluate arguments
if (typeof filenameOrOptions === "object") {
currentFilename = filenameOrOptions.filename;
allowInlineConfig = filenameOrOptions.allowInlineConfig;
saveState = filenameOrOptions.saveState;
} else {
currentFilename = filenameOrOptions;
}
if (!saveState) {
this.reset();
}
// search and apply "eslint-env *".
const envInFile = findEslintEnv(text || textOrSourceCode.text);
config = Object.assign({}, config);
if (envInFile) {
if (config.env) {
config.env = Object.assign({}, config.env, envInFile);
} else {
config.env = envInFile;
}
}
// process initial config to make it safe to extend
config = prepareConfig(config);
// only do this for text
if (text !== null) {
// there's no input, just exit here
if (text.trim().length === 0) {
sourceCode = new SourceCode(text, blankScriptAST);
return messages;
}
parseResult = parse(
stripUnicodeBOM(text).replace(/^#!([^\r\n]+)/, (match, captured) => {
shebang = captured;
return `//${captured}`;
}),
config,
currentFilename
);
// if this result is from a parseForESLint() method, normalize
if (parseResult && parseResult.ast) {
ast = parseResult.ast;
} else {
ast = parseResult;
parseResult = null;
}
if (ast) {
sourceCode = new SourceCode(text, ast);
}
} else {
sourceCode = textOrSourceCode;
ast = sourceCode.ast;
}
// if espree failed to parse the file, there's no sense in setting up rules
if (ast) {
// parse global comments and modify config
if (allowInlineConfig !== false) {
config = modifyConfigsFromComments(currentFilename, ast, config, reportingConfig, messages);
}
// ensure that severities are normalized in the config
ConfigOps.normalize(config);
// enable appropriate rules
Object.keys(config.rules).filter(key => getRuleSeverity(config.rules[key]) > 0).forEach(key => {
let ruleCreator;
ruleCreator = rules.get(key);
if (!ruleCreator) {
const replacementMsg = getRuleReplacementMessage(key);
if (replacementMsg) {
ruleCreator = createStubRule(replacementMsg);
} else {
ruleCreator = createStubRule(`Definition for rule '${key}' was not found`);
}
rules.define(key, ruleCreator);
}
const severity = getRuleSeverity(config.rules[key]);
const options = getRuleOptions(config.rules[key]);
try {
const ruleContext = new RuleContext(
key, api, severity, options,
config.settings, config.parserOptions, config.parser,
ruleCreator.meta,
(parseResult && parseResult.services ? parseResult.services : {})
);
const rule = ruleCreator.create ? ruleCreator.create(ruleContext)
: ruleCreator(ruleContext);
// add all the selectors from the rule as listeners
Object.keys(rule).forEach(selector => {
api.on(selector, timing.enabled
? timing.time(key, rule[selector])
: rule[selector]
);
});
} catch (ex) {
ex.message = `Error while loading rule '${key}': ${ex.message}`;
throw ex;
}
});
// save config so rules can access as necess ...n/a
error() {
console.error.apply(console, Array.prototype.slice.call(arguments));
}n/a
info() {
console.log.apply(console, Array.prototype.slice.call(arguments));
}n/a
generateHelp = function (arg$){
var ref$, showHidden, interpolate, maxWidth, output, out, data, optionCount, totalPreLen, preLens, i$, len$, item, that, pre,
descParts, desc, preLen, sortedPreLens, maxPreLen, preLenMean, x, padAmount, descSepLen, fullWrapCount, partialWrapCount, descLen
, totalLen, initialSpace, wrapAllFull, i, wrap;
ref$ = arg$ != null
? arg$
: {}, showHidden = ref$.showHidden, interpolate = ref$.interpolate;
maxWidth = stdout != null && stdout.isTTY ? stdout.columns - 1 : null;
output = [];
out = function(it){
return output.push(it != null ? it : '');
};
if (prepend) {
out(interpolate ? interp(prepend, interpolate) : prepend);
out();
}
data = [];
optionCount = 0;
totalPreLen = 0;
preLens = [];
for (i$ = 0, len$ = (ref$ = options).length; i$ < len$; ++i$) {
item = ref$[i$];
if (showHidden || !item.hidden) {
if (that = item.heading) {
data.push({
type: 'heading',
value: that
});
} else {
pre = getPreText(item, helpStyle, maxWidth);
descParts = [];
if ((that = item.description) != null) {
descParts.push(that);
}
if (that = item['enum']) {
descParts.push("either: " + naturalJoin(that));
}
if (item['default'] && !item.negateName) {
descParts.push("default: " + item['default']);
}
desc = descParts.join(' - ');
data.push({
type: 'option',
pre: pre,
desc: desc,
descLen: desc.length
});
preLen = pre.length;
optionCount++;
totalPreLen += preLen;
preLens.push(preLen);
}
}
}
sortedPreLens = sort(preLens);
maxPreLen = sortedPreLens[sortedPreLens.length - 1];
preLenMean = initialIndent + totalPreLen / optionCount;
x = optionCount > 2 ? min(preLenMean * maxPadFactor, maxPreLen) : maxPreLen;
for (i$ = sortedPreLens.length - 1; i$ >= 0; --i$) {
preLen = sortedPreLens[i$];
if (preLen <= x) {
padAmount = preLen;
break;
}
}
descSepLen = descriptionSeparator.length;
if (maxWidth != null) {
fullWrapCount = 0;
partialWrapCount = 0;
for (i$ = 0, len$ = data.length; i$ < len$; ++i$) {
item = data[i$];
if (item.type === 'option') {
pre = item.pre, desc = item.desc, descLen = item.descLen;
if (descLen === 0) {
item.wrap = 'none';
} else {
preLen = max(padAmount, pre.length) + initialIndent + descSepLen;
totalLen = preLen + descLen;
if (totalLen > maxWidth) {
if (descLen / 2.5 > maxWidth - preLen) {
fullWrapCount++;
item.wrap = 'full';
} else {
partialWrapCount++;
item.wrap = 'partial';
}
} else {
item.wrap = 'none';
}
}
}
}
}
initialSpace = repeatString$(' ', initialIndent);
wrapAllFull = optionCount > 1 && fullWrapCount + partialWrapCount * 0.5 > optionCount * 0.5;
for (i$ = 0, len$ = data.length; i$ < len$; ++i$) {
i = i$;
item = data[i$];
if (item.type === 'heading') {
if (i !== 0) {
out();
}
out(item.value + ":");
} else {
pre = item.pre, desc = item.desc, descLen = item.descLen, wrap = item.wrap;
if (maxWidth != null) {
if (wrapAllFull || wrap === 'full') {
wrap = wordwrap(initialIndent + secondaryIndent, maxWidth);
out(initialSpace + "" + pre + "\n" + wrap(desc));
continue;
} else if (wrap === 'partial') {
wrap = wordwrap(initialIndent + descSepLen + max(padAmount, pre.length), maxWidth);
out(initialSpace + "" + pad(pre, padAmount) + descriptionSeparator + wrap(desc).replace(/^\s+/, ''));
continue;
}
}
if (descLen === 0) {
out(initialSpace + "" + pre);
} else {
out(initialSpace + "" + pad(pre, padAmount) + descriptionSeparator + desc);
}
}
}
if (append) {
out();
out(interpola ...n/a
generateHelpForOption = function (optionName){
var maxWidth, wrap, option, e, pre, defaultString, restPositionalString, description, fullDescription, that, preDescription, descriptionString
, exampleString, examples, seperator;
maxWidth = stdout != null && stdout.isTTY ? stdout.columns - 1 : null;
wrap = maxWidth ? wordwrap(maxWidth) : id;
try {
option = getOption(dasherize(optionName));
} catch (e$) {
e = e$;
return e.message;
}
pre = getPreText(option, helpStyle);
defaultString = option['default'] && !option.negateName ? "\ndefault: " + option['default'] : '';
restPositionalString = option.restPositional ? 'Everything after this option is considered a positional argument, even if it looks
like an option.' : '';
description = option.longDescription || option.description && sentencize(option.description);
fullDescription = description && restPositionalString
? description + " " + restPositionalString
: (that = description || restPositionalString) ? that : '';
preDescription = 'description:';
descriptionString = !fullDescription
? ''
: maxWidth && fullDescription.length - 1 - preDescription.length > maxWidth
? "\n" + preDescription + "\n" + wrap(fullDescription)
: "\n" + preDescription + " " + fullDescription;
exampleString = (that = option.example) ? (examples = [].concat(that), examples.length > 1
? "\nexamples:\n" + unlines(examples)
: "\nexample: " + examples[0]) : '';
seperator = defaultString || descriptionString || exampleString ? "\n" + repeatString$('=', pre.length) : '';
return pre + "" + seperator + defaultString + descriptionString + exampleString;
}n/a
parse = function (input, arg$){
var slice, obj, positional, restPositional, overrideRequired, prop, setValue, setDefaults, checkRequired, mutuallyExclusiveError
, checkMutuallyExclusive, checkDependency, checkDependencies, checkProp, args, key, value, option, ref$, i$, len$, arg, that, result
, short, argName, usingAssign, val, flags, len, j$, len1$, i, flag, opt, name, valPrime, negated, noedName;
slice = (arg$ != null
? arg$
: {}).slice;
obj = {};
positional = [];
restPositional = false;
overrideRequired = false;
prop = null;
setValue = function(name, value){
var opt, val, cra, e, currentType;
opt = getOption(name);
if (opt.boolean) {
val = value;
} else {
try {
cra = opt.concatRepeatedArrays;
if (cra != null && cra[0] && cra[1].oneValuePerFlag && opt.parsedType.length === 1 && opt.parsedType[0].structure === 'array
') {
val = [parseLevn(opt.parsedType[0].of, value)];
} else {
val = parseLevn(opt.parsedType, value);
}
} catch (e$) {
e = e$;
throw new Error("Invalid value for option '" + name + "' - expected type " + opt.type + ", received value: " + value + ".");
}
if (opt['enum'] && !any(function(it){
return deepIs(it, val);
}, opt.parsedPossibilities)) {
throw new Error("Option " + name + ": '" + val + "' not one of " + naturalJoin(opt['enum']) + ".");
}
}
currentType = toString$.call(obj[name]).slice(8, -1);
if (obj[name] != null) {
if (opt.concatRepeatedArrays != null && opt.concatRepeatedArrays[0] && currentType === 'Array') {
obj[name] = obj[name].concat(val);
} else if (opt.mergeRepeatedObjects && currentType === 'Object') {
import$(obj[name], val);
} else {
obj[name] = val;
}
} else {
obj[name] = val;
}
if (opt.restPositional) {
restPositional = true;
}
if (opt.overrideRequired) {
overrideRequired = true;
}
};
setDefaults = function(){
var name, ref$, value;
for (name in ref$ = defaults) {
value = ref$[name];
if (obj[name] == null) {
obj[name] = value;
}
}
};
checkRequired = function(){
var i$, ref$, len$, name;
if (overrideRequired) {
return;
}
for (i$ = 0, len$ = (ref$ = required).length; i$ < len$; ++i$) {
name = ref$[i$];
if (!obj[name]) {
throw new Error("Option " + nameToRaw(name) + " is required.");
}
}
};
mutuallyExclusiveError = function(first, second){
throw new Error("The options " + nameToRaw(first) + " and " + nameToRaw(second) + " are mutually exclusive - you cannot use
them at the same time.");
};
checkMutuallyExclusive = function(){
var rules, i$, len$, rule, present, j$, len1$, element, k$, len2$, opt;
rules = libOptions.mutuallyExclusive;
if (!rules) {
return;
}
for (i$ = 0, len$ = rules.length; i$ < len$; ++i$) {
rule = rules[i$];
present = null;
for (j$ = 0, len1$ = rule.length; j$ < len1$; ++j$) {
element = rule[j$];
if (toString$.call(element).slice(8, -1) === 'Array') {
for (k$ = 0, len2$ = element.length; k$ < len2$; ++k$) {
opt = element[k$];
if (opt in obj) {
if (present != null) {
mutuallyExclusiveError(present, opt);
} else {
present = opt;
break;
}
}
}
} else {
if (element in obj) {
if (present != null) {
mutuallyExclusiveError(present, element);
} else {
present = element;
}
}
}
}
}
};
checkDependency = function(option){
var dependsOn, type, targetOptionNames, i$, len$, targetOptionName, targetOption;
dependsOn = option.dependsOn;
if (!dependsOn || option.dependenciesMet) {
return true;
}
type = dependsOn[0], targetOptionNames = slice$.call(dependsOn, 1);
for (i$ = 0, len$ = ...n/a
parseArgv = function (it){
return parse(it, {
slice: 2
});
}n/a
class RuleContext {
/**
* @param {string} ruleId The ID of the rule using this object.
* @param {eslint} eslint The eslint object.
* @param {number} severity The configured severity level of the rule.
* @param {Array} options The configuration information to be added to the rule.
* @param {Object} settings The configuration settings passed from the config file.
* @param {Object} parserOptions The parserOptions settings passed from the config file.
* @param {Object} parserPath The parser setting passed from the config file.
* @param {Object} meta The metadata of the rule
* @param {Object} parserServices The parser services for the rule.
*/
constructor(ruleId, eslint, severity, options, settings, parserOptions, parserPath, meta, parserServices) {
// public.
this.id = ruleId;
this.options = options;
this.settings = settings;
this.parserOptions = parserOptions;
this.parserPath = parserPath;
this.meta = meta;
// create a separate copy and freeze it (it's not nice to freeze other people's objects)
this.parserServices = Object.freeze(Object.assign({}, parserServices));
// private.
this.eslint = eslint;
this.severity = severity;
Object.freeze(this);
}
/**
* Passthrough to eslint.getSourceCode().
* @returns {SourceCode} The SourceCode object for the code.
*/
getSourceCode() {
return this.eslint.getSourceCode();
}
/**
* Passthrough to eslint.report() that automatically assigns the rule ID and severity.
* @param {ASTNode|MessageDescriptor} nodeOrDescriptor The AST node related to the message or a message
* descriptor.
* @param {Object=} location The location of the error.
* @param {string} message The message to display to the user.
* @param {Object} opts Optional template data which produces a formatted message
* with symbols being replaced by this object's values.
* @returns {void}
*/
report(nodeOrDescriptor, location, message, opts) {
// check to see if it's a new style call
if (arguments.length === 1) {
const descriptor = nodeOrDescriptor;
let fix = null;
// if there's a fix specified, get it
if (typeof descriptor.fix === "function") {
fix = descriptor.fix(ruleFixer);
}
this.eslint.report(
this.id,
this.severity,
descriptor.node,
descriptor.loc || descriptor.node.loc.start,
descriptor.message,
descriptor.data,
fix,
this.meta
);
return;
}
// old style call
this.eslint.report(
this.id,
this.severity,
nodeOrDescriptor,
location,
message,
opts,
this.meta
);
}
}n/a
getAllComments = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}n/a
getAncestors = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}n/a
getComments = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}...
return true;
}
// Checks `@this` in its leading comments for callbacks,
// because callbacks don't have its JSDoc comment.
// e.g.
// sinon.test(/* @this sinon.Sandbox */function() { this.spy(); });
return sourceCode.getComments(node).leading.some(comment => thisTagPattern.test
(comment.value));
}
/**
* Determines if a node is surrounded by parentheses.
* @param {SourceCode} sourceCode The ESLint source code object
* @param {ASTNode} node The node to be checked.
* @returns {boolean} True if the node is parenthesised.
...getDeclaredVariables = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}n/a
getFilename = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}n/a
getFirstToken = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}...
* @param {ASTNode} node - The function node to get.
* @param {SourceCode} sourceCode - The source code object to get tokens.
* @returns {Token} `(` token.
*/
function getOpeningParenOfParams(node, sourceCode) {
return node.id
? sourceCode.getTokenAfter(node.id, isOpeningParenToken)
: sourceCode.getFirstToken(node, isOpeningParenToken);
}
/**
* Creates a version of the LINEBREAK_MATCHER regex with the global flag.
* Global regexes are mutable, so this needs to be a function instead of a constant.
* @returns {RegExp} A global regular expression that matches line terminators
*/
...getFirstTokens = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}n/a
getJSDocComment = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}...
/**
* Checks whether or not a node has a `@this` tag in its comments.
* @param {ASTNode} node - A node to check.
* @param {SourceCode} sourceCode - A SourceCode instance to get comments.
* @returns {boolean} Whether or not the node has a `@this` tag in its comments.
*/
function hasJSDocThisTag(node, sourceCode) {
const jsdocComment = sourceCode.getJSDocComment(node);
if (jsdocComment && thisTagPattern.test(jsdocComment.value)) {
return true;
}
// Checks `@this` in its leading comments for callbacks,
// because callbacks don't have its JSDoc comment.
...getLastToken = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}...
* surrounding the node.
* @param {SourceCode} sourceCode The source code object
* @param {ASTNode} node An expression node
* @returns {string} The text representing the node, with all surrounding parentheses included
*/
getParenthesisedText(sourceCode, node) {
let leftToken = sourceCode.getFirstToken(node);
let rightToken = sourceCode.getLastToken(node);
while (
sourceCode.getTokenBefore(leftToken) &&
sourceCode.getTokenBefore(leftToken).type === "Punctuator" &&
sourceCode.getTokenBefore(leftToken).value === "(" &&
sourceCode.getTokenAfter(rightToken) &&
sourceCode.getTokenAfter(rightToken).type === "Punctuator" &&
...getLastTokens = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}n/a
getNodeByRangeIndex = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}n/a
getScope = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}n/a
getSource = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}n/a
getSourceLines = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}n/a
getTokenAfter = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}...
* @param {SourceCode} sourceCode The ESLint source code object
* @param {ASTNode} node The node to be checked.
* @returns {boolean} True if the node is parenthesised.
* @private
*/
function isParenthesised(sourceCode, node) {
const previousToken = sourceCode.getTokenBefore(node),
nextToken = sourceCode.getTokenAfter(node);
return Boolean(previousToken && nextToken) &&
previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
nextToken.value === ")" && nextToken.range[0] >= node.range[1];
}
/**
...getTokenBefore = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}...
* Determines if a node is surrounded by parentheses.
* @param {SourceCode} sourceCode The ESLint source code object
* @param {ASTNode} node The node to be checked.
* @returns {boolean} True if the node is parenthesised.
* @private
*/
function isParenthesised(sourceCode, node) {
const previousToken = sourceCode.getTokenBefore(node),
nextToken = sourceCode.getTokenAfter(node);
return Boolean(previousToken && nextToken) &&
previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
nextToken.value === ")" && nextToken.range[0] >= node.range[1];
}
...getTokenByRangeStart = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}n/a
getTokens = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}n/a
getTokensAfter = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}n/a
getTokensBefore = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}n/a
getTokensBetween = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}n/a
markVariableAsUsed = function (a, b, c, d, e) {
return this.eslint[name](a, b, c, d, e);
}n/a
function define(ruleId, ruleModule) {
rules[ruleId] = ruleModule;
}n/a
function getHandler(ruleId) {
if (typeof rules[ruleId] === "string") {
return require(rules[ruleId]);
}
return rules[ruleId];
}...
rules = require("../lib/rules");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
const enabledRules = Object.keys(load()).reduce((result, ruleId) => {
if (!rules.get(ruleId).meta.deprecated) {
result[ruleId] = "error";
}
return result;
}, {});
//------------------------------------------------------------------------------
// Public Interface
...function getAllLoadedRules() {
const allRules = new Map();
Object.keys(rules).forEach(name => {
const rule = getHandler(name);
allRules.set(name, rule);
});
return allRules;
}n/a
function importPlugin(plugin, pluginName) {
if (plugin.rules) {
Object.keys(plugin.rules).forEach(ruleId => {
const qualifiedRuleId = `${pluginName}/${ruleId}`,
rule = plugin.rules[ruleId];
define(qualifiedRuleId, rule);
});
}
}n/a
function load(rulesDir, cwd) {
const newRules = loadRules(rulesDir, cwd);
Object.keys(newRules).forEach(ruleId => {
define(ruleId, newRules[ruleId]);
});
}...
config = configToLoad;
if (config.extends) {
config = ConfigFile.applyExtends(config, filePath);
}
} else {
filePath = configToLoad;
config = ConfigFile.load(filePath);
}
}
return config;
}
...function testClear() {
rules = Object.create(null);
}n/a
testReset() {
testClear();
load();
}n/a
function time(key, fn) {
if (typeof data[key] === "undefined") {
data[key] = 0;
}
return function() {
let t = process.hrtime();
fn.apply(null, Array.prototype.slice.call(arguments));
t = process.hrtime(t);
data[key] += t[0] * 1e3 + t[1] / 1e6;
};
}n/a