first commit
This commit is contained in:
1078
node_modules/eslint/lib/cli-engine/cli-engine.js
generated
vendored
Normal file
1078
node_modules/eslint/lib/cli-engine/cli-engine.js
generated
vendored
Normal file
@@ -0,0 +1,1078 @@
|
||||
/**
|
||||
* @fileoverview Main CLI object.
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
* The CLI object should *not* call process.exit() directly. It should only return
|
||||
* exit codes. This allows other programs to use the CLI object and still control
|
||||
* when the program exits.
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const defaultOptions = require("../../conf/default-cli-options");
|
||||
const pkg = require("../../package.json");
|
||||
|
||||
|
||||
const {
|
||||
Legacy: {
|
||||
ConfigOps,
|
||||
naming,
|
||||
CascadingConfigArrayFactory,
|
||||
IgnorePattern,
|
||||
getUsedExtractedConfigs,
|
||||
ModuleResolver
|
||||
}
|
||||
} = require("@eslint/eslintrc");
|
||||
|
||||
const { FileEnumerator } = require("./file-enumerator");
|
||||
|
||||
const { Linter } = require("../linter");
|
||||
const builtInRules = require("../rules");
|
||||
const loadRules = require("./load-rules");
|
||||
const hash = require("./hash");
|
||||
const LintResultCache = require("./lint-result-cache");
|
||||
|
||||
const debug = require("debug")("eslint:cli-engine");
|
||||
const validFixTypes = new Set(["directive", "problem", "suggestion", "layout"]);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Typedefs
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// For VSCode IntelliSense
|
||||
/** @typedef {import("../shared/types").ConfigData} ConfigData */
|
||||
/** @typedef {import("../shared/types").DeprecatedRuleInfo} DeprecatedRuleInfo */
|
||||
/** @typedef {import("../shared/types").LintMessage} LintMessage */
|
||||
/** @typedef {import("../shared/types").SuppressedLintMessage} SuppressedLintMessage */
|
||||
/** @typedef {import("../shared/types").ParserOptions} ParserOptions */
|
||||
/** @typedef {import("../shared/types").Plugin} Plugin */
|
||||
/** @typedef {import("../shared/types").RuleConf} RuleConf */
|
||||
/** @typedef {import("../shared/types").Rule} Rule */
|
||||
/** @typedef {import("../shared/types").FormatterFunction} FormatterFunction */
|
||||
/** @typedef {ReturnType<CascadingConfigArrayFactory.getConfigArrayForFile>} ConfigArray */
|
||||
/** @typedef {ReturnType<ConfigArray.extractConfig>} ExtractedConfig */
|
||||
|
||||
/**
|
||||
* The options to configure a CLI engine with.
|
||||
* @typedef {Object} CLIEngineOptions
|
||||
* @property {boolean} [allowInlineConfig] Enable or disable inline configuration comments.
|
||||
* @property {ConfigData} [baseConfig] Base config object, extended by all configs used with this CLIEngine instance
|
||||
* @property {boolean} [cache] Enable result caching.
|
||||
* @property {string} [cacheLocation] The cache file to use instead of .eslintcache.
|
||||
* @property {string} [configFile] The configuration file to use.
|
||||
* @property {string} [cwd] The value to use for the current working directory.
|
||||
* @property {string[]} [envs] An array of environments to load.
|
||||
* @property {string[]|null} [extensions] An array of file extensions to check.
|
||||
* @property {boolean|Function} [fix] Execute in autofix mode. If a function, should return a boolean.
|
||||
* @property {string[]} [fixTypes] Array of rule types to apply fixes for.
|
||||
* @property {string[]} [globals] An array of global variables to declare.
|
||||
* @property {boolean} [ignore] False disables use of .eslintignore.
|
||||
* @property {string} [ignorePath] The ignore file to use instead of .eslintignore.
|
||||
* @property {string|string[]} [ignorePattern] One or more glob patterns to ignore.
|
||||
* @property {boolean} [useEslintrc] False disables looking for .eslintrc
|
||||
* @property {string} [parser] The name of the parser to use.
|
||||
* @property {ParserOptions} [parserOptions] An object of parserOption settings to use.
|
||||
* @property {string[]} [plugins] An array of plugins to load.
|
||||
* @property {Record<string,RuleConf>} [rules] An object of rules to use.
|
||||
* @property {string[]} [rulePaths] An array of directories to load custom rules from.
|
||||
* @property {boolean|string} [reportUnusedDisableDirectives] `true`, `"error"` or '"warn"' adds reports for unused eslint-disable directives
|
||||
* @property {boolean} [globInputPaths] Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file.
|
||||
* @property {string} [resolvePluginsRelativeTo] The folder where plugins should be resolved from, defaulting to the CWD
|
||||
*/
|
||||
|
||||
/**
|
||||
* A linting result.
|
||||
* @typedef {Object} LintResult
|
||||
* @property {string} filePath The path to the file that was linted.
|
||||
* @property {LintMessage[]} messages All of the messages for the result.
|
||||
* @property {SuppressedLintMessage[]} suppressedMessages All of the suppressed messages for the result.
|
||||
* @property {number} errorCount Number of errors for the result.
|
||||
* @property {number} fatalErrorCount Number of fatal errors for the result.
|
||||
* @property {number} warningCount Number of warnings for the result.
|
||||
* @property {number} fixableErrorCount Number of fixable errors for the result.
|
||||
* @property {number} fixableWarningCount Number of fixable warnings for the result.
|
||||
* @property {string} [source] The source code of the file that was linted.
|
||||
* @property {string} [output] The source code of the file that was linted, with as many fixes applied as possible.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Linting results.
|
||||
* @typedef {Object} LintReport
|
||||
* @property {LintResult[]} results All of the result.
|
||||
* @property {number} errorCount Number of errors for the result.
|
||||
* @property {number} fatalErrorCount Number of fatal errors for the result.
|
||||
* @property {number} warningCount Number of warnings for the result.
|
||||
* @property {number} fixableErrorCount Number of fixable errors for the result.
|
||||
* @property {number} fixableWarningCount Number of fixable warnings for the result.
|
||||
* @property {DeprecatedRuleInfo[]} usedDeprecatedRules The list of used deprecated rules.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Private data for CLIEngine.
|
||||
* @typedef {Object} CLIEngineInternalSlots
|
||||
* @property {Map<string, Plugin>} additionalPluginPool The map for additional plugins.
|
||||
* @property {string} cacheFilePath The path to the cache of lint results.
|
||||
* @property {CascadingConfigArrayFactory} configArrayFactory The factory of configs.
|
||||
* @property {(filePath: string) => boolean} defaultIgnores The default predicate function to check if a file ignored or not.
|
||||
* @property {FileEnumerator} fileEnumerator The file enumerator.
|
||||
* @property {ConfigArray[]} lastConfigArrays The list of config arrays that the last `executeOnFiles` or `executeOnText` used.
|
||||
* @property {LintResultCache|null} lintResultCache The cache of lint results.
|
||||
* @property {Linter} linter The linter instance which has loaded rules.
|
||||
* @property {CLIEngineOptions} options The normalized options of this instance.
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {WeakMap<CLIEngine, CLIEngineInternalSlots>} */
|
||||
const internalSlotsMap = new WeakMap();
|
||||
|
||||
/**
|
||||
* Determines if each fix type in an array is supported by ESLint and throws
|
||||
* an error if not.
|
||||
* @param {string[]} fixTypes An array of fix types to check.
|
||||
* @returns {void}
|
||||
* @throws {Error} If an invalid fix type is found.
|
||||
*/
|
||||
function validateFixTypes(fixTypes) {
|
||||
for (const fixType of fixTypes) {
|
||||
if (!validFixTypes.has(fixType)) {
|
||||
throw new Error(`Invalid fix type "${fixType}" found.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It will calculate the error and warning count for collection of messages per file
|
||||
* @param {LintMessage[]} messages Collection of messages
|
||||
* @returns {Object} Contains the stats
|
||||
* @private
|
||||
*/
|
||||
function calculateStatsPerFile(messages) {
|
||||
const stat = {
|
||||
errorCount: 0,
|
||||
fatalErrorCount: 0,
|
||||
warningCount: 0,
|
||||
fixableErrorCount: 0,
|
||||
fixableWarningCount: 0
|
||||
};
|
||||
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
const message = messages[i];
|
||||
|
||||
if (message.fatal || message.severity === 2) {
|
||||
stat.errorCount++;
|
||||
if (message.fatal) {
|
||||
stat.fatalErrorCount++;
|
||||
}
|
||||
if (message.fix) {
|
||||
stat.fixableErrorCount++;
|
||||
}
|
||||
} else {
|
||||
stat.warningCount++;
|
||||
if (message.fix) {
|
||||
stat.fixableWarningCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
/**
|
||||
* It will calculate the error and warning count for collection of results from all files
|
||||
* @param {LintResult[]} results Collection of messages from all the files
|
||||
* @returns {Object} Contains the stats
|
||||
* @private
|
||||
*/
|
||||
function calculateStatsPerRun(results) {
|
||||
const stat = {
|
||||
errorCount: 0,
|
||||
fatalErrorCount: 0,
|
||||
warningCount: 0,
|
||||
fixableErrorCount: 0,
|
||||
fixableWarningCount: 0
|
||||
};
|
||||
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
const result = results[i];
|
||||
|
||||
stat.errorCount += result.errorCount;
|
||||
stat.fatalErrorCount += result.fatalErrorCount;
|
||||
stat.warningCount += result.warningCount;
|
||||
stat.fixableErrorCount += result.fixableErrorCount;
|
||||
stat.fixableWarningCount += result.fixableWarningCount;
|
||||
}
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an source code using ESLint.
|
||||
* @param {Object} config The config object.
|
||||
* @param {string} config.text The source code to verify.
|
||||
* @param {string} config.cwd The path to the current working directory.
|
||||
* @param {string|undefined} config.filePath The path to the file of `text`. If this is undefined, it uses `<text>`.
|
||||
* @param {ConfigArray} config.config The config.
|
||||
* @param {boolean} config.fix If `true` then it does fix.
|
||||
* @param {boolean} config.allowInlineConfig If `true` then it uses directive comments.
|
||||
* @param {boolean|string} config.reportUnusedDisableDirectives If `true`, `"error"` or '"warn"', then it reports unused `eslint-disable` comments.
|
||||
* @param {FileEnumerator} config.fileEnumerator The file enumerator to check if a path is a target or not.
|
||||
* @param {Linter} config.linter The linter instance to verify.
|
||||
* @returns {LintResult} The result of linting.
|
||||
* @private
|
||||
*/
|
||||
function verifyText({
|
||||
text,
|
||||
cwd,
|
||||
filePath: providedFilePath,
|
||||
config,
|
||||
fix,
|
||||
allowInlineConfig,
|
||||
reportUnusedDisableDirectives,
|
||||
fileEnumerator,
|
||||
linter
|
||||
}) {
|
||||
const filePath = providedFilePath || "<text>";
|
||||
|
||||
debug(`Lint ${filePath}`);
|
||||
|
||||
/*
|
||||
* Verify.
|
||||
* `config.extractConfig(filePath)` requires an absolute path, but `linter`
|
||||
* doesn't know CWD, so it gives `linter` an absolute path always.
|
||||
*/
|
||||
const filePathToVerify = filePath === "<text>" ? path.join(cwd, filePath) : filePath;
|
||||
const { fixed, messages, output } = linter.verifyAndFix(
|
||||
text,
|
||||
config,
|
||||
{
|
||||
allowInlineConfig,
|
||||
filename: filePathToVerify,
|
||||
fix,
|
||||
reportUnusedDisableDirectives,
|
||||
|
||||
/**
|
||||
* Check if the linter should adopt a given code block or not.
|
||||
* @param {string} blockFilename The virtual filename of a code block.
|
||||
* @returns {boolean} `true` if the linter should adopt the code block.
|
||||
*/
|
||||
filterCodeBlock(blockFilename) {
|
||||
return fileEnumerator.isTargetPath(blockFilename);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Tweak and return.
|
||||
const result = {
|
||||
filePath,
|
||||
messages,
|
||||
suppressedMessages: linter.getSuppressedMessages(),
|
||||
...calculateStatsPerFile(messages)
|
||||
};
|
||||
|
||||
if (fixed) {
|
||||
result.output = output;
|
||||
}
|
||||
if (
|
||||
result.errorCount + result.warningCount > 0 &&
|
||||
typeof result.output === "undefined"
|
||||
) {
|
||||
result.source = text;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns result with warning by ignore settings
|
||||
* @param {string} filePath File path of checked code
|
||||
* @param {string} baseDir Absolute path of base directory
|
||||
* @returns {LintResult} Result with single warning
|
||||
* @private
|
||||
*/
|
||||
function createIgnoreResult(filePath, baseDir) {
|
||||
let message;
|
||||
const isHidden = filePath.split(path.sep)
|
||||
.find(segment => /^\./u.test(segment));
|
||||
const isInNodeModules = baseDir && path.relative(baseDir, filePath).startsWith("node_modules");
|
||||
|
||||
if (isHidden) {
|
||||
message = "File ignored by default. Use a negated ignore pattern (like \"--ignore-pattern '!<relative/path/to/filename>'\") to override.";
|
||||
} else if (isInNodeModules) {
|
||||
message = "File ignored by default. Use \"--ignore-pattern '!node_modules/*'\" to override.";
|
||||
} else {
|
||||
message = "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to override.";
|
||||
}
|
||||
|
||||
return {
|
||||
filePath: path.resolve(filePath),
|
||||
messages: [
|
||||
{
|
||||
ruleId: null,
|
||||
fatal: false,
|
||||
severity: 1,
|
||||
message,
|
||||
nodeType: null
|
||||
}
|
||||
],
|
||||
suppressedMessages: [],
|
||||
errorCount: 0,
|
||||
fatalErrorCount: 0,
|
||||
warningCount: 1,
|
||||
fixableErrorCount: 0,
|
||||
fixableWarningCount: 0
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a rule.
|
||||
* @param {string} ruleId The rule ID to get.
|
||||
* @param {ConfigArray[]} configArrays The config arrays that have plugin rules.
|
||||
* @returns {Rule|null} The rule or null.
|
||||
*/
|
||||
function getRule(ruleId, configArrays) {
|
||||
for (const configArray of configArrays) {
|
||||
const rule = configArray.pluginRules.get(ruleId);
|
||||
|
||||
if (rule) {
|
||||
return rule;
|
||||
}
|
||||
}
|
||||
return builtInRules.get(ruleId) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a message's rule type should be fixed.
|
||||
* @param {LintMessage} message The message to check.
|
||||
* @param {ConfigArray[]} lastConfigArrays The list of config arrays that the last `executeOnFiles` or `executeOnText` used.
|
||||
* @param {string[]} fixTypes An array of fix types to check.
|
||||
* @returns {boolean} Whether the message should be fixed.
|
||||
*/
|
||||
function shouldMessageBeFixed(message, lastConfigArrays, fixTypes) {
|
||||
if (!message.ruleId) {
|
||||
return fixTypes.has("directive");
|
||||
}
|
||||
|
||||
const rule = message.ruleId && getRule(message.ruleId, lastConfigArrays);
|
||||
|
||||
return Boolean(rule && rule.meta && fixTypes.has(rule.meta.type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect used deprecated rules.
|
||||
* @param {ConfigArray[]} usedConfigArrays The config arrays which were used.
|
||||
* @returns {IterableIterator<DeprecatedRuleInfo>} Used deprecated rules.
|
||||
*/
|
||||
function *iterateRuleDeprecationWarnings(usedConfigArrays) {
|
||||
const processedRuleIds = new Set();
|
||||
|
||||
// Flatten used configs.
|
||||
/** @type {ExtractedConfig[]} */
|
||||
const configs = usedConfigArrays.flatMap(getUsedExtractedConfigs);
|
||||
|
||||
// Traverse rule configs.
|
||||
for (const config of configs) {
|
||||
for (const [ruleId, ruleConfig] of Object.entries(config.rules)) {
|
||||
|
||||
// Skip if it was processed.
|
||||
if (processedRuleIds.has(ruleId)) {
|
||||
continue;
|
||||
}
|
||||
processedRuleIds.add(ruleId);
|
||||
|
||||
// Skip if it's not used.
|
||||
if (!ConfigOps.getRuleSeverity(ruleConfig)) {
|
||||
continue;
|
||||
}
|
||||
const rule = getRule(ruleId, usedConfigArrays);
|
||||
|
||||
// Skip if it's not deprecated.
|
||||
if (!(rule && rule.meta && rule.meta.deprecated)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// This rule was used and deprecated.
|
||||
yield {
|
||||
ruleId,
|
||||
replacedBy: rule.meta.replacedBy || []
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given message is an error message.
|
||||
* @param {LintMessage} message The message to check.
|
||||
* @returns {boolean} Whether or not the message is an error message.
|
||||
* @private
|
||||
*/
|
||||
function isErrorMessage(message) {
|
||||
return message.severity === 2;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* return the cacheFile to be used by eslint, based on whether the provided parameter is
|
||||
* a directory or looks like a directory (ends in `path.sep`), in which case the file
|
||||
* name will be the `cacheFile/.cache_hashOfCWD`
|
||||
*
|
||||
* if cacheFile points to a file or looks like a file then it will just use that file
|
||||
* @param {string} cacheFile The name of file to be used to store the cache
|
||||
* @param {string} cwd Current working directory
|
||||
* @returns {string} the resolved path to the cache file
|
||||
*/
|
||||
function getCacheFile(cacheFile, cwd) {
|
||||
|
||||
/*
|
||||
* make sure the path separators are normalized for the environment/os
|
||||
* keeping the trailing path separator if present
|
||||
*/
|
||||
const normalizedCacheFile = path.normalize(cacheFile);
|
||||
|
||||
const resolvedCacheFile = path.resolve(cwd, normalizedCacheFile);
|
||||
const looksLikeADirectory = normalizedCacheFile.slice(-1) === path.sep;
|
||||
|
||||
/**
|
||||
* return the name for the cache file in case the provided parameter is a directory
|
||||
* @returns {string} the resolved path to the cacheFile
|
||||
*/
|
||||
function getCacheFileForDirectory() {
|
||||
return path.join(resolvedCacheFile, `.cache_${hash(cwd)}`);
|
||||
}
|
||||
|
||||
let fileStats;
|
||||
|
||||
try {
|
||||
fileStats = fs.lstatSync(resolvedCacheFile);
|
||||
} catch {
|
||||
fileStats = null;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* in case the file exists we need to verify if the provided path
|
||||
* is a directory or a file. If it is a directory we want to create a file
|
||||
* inside that directory
|
||||
*/
|
||||
if (fileStats) {
|
||||
|
||||
/*
|
||||
* is a directory or is a file, but the original file the user provided
|
||||
* looks like a directory but `path.resolve` removed the `last path.sep`
|
||||
* so we need to still treat this like a directory
|
||||
*/
|
||||
if (fileStats.isDirectory() || looksLikeADirectory) {
|
||||
return getCacheFileForDirectory();
|
||||
}
|
||||
|
||||
// is file so just use that file
|
||||
return resolvedCacheFile;
|
||||
}
|
||||
|
||||
/*
|
||||
* here we known the file or directory doesn't exist,
|
||||
* so we will try to infer if its a directory if it looks like a directory
|
||||
* for the current operating system.
|
||||
*/
|
||||
|
||||
// if the last character passed is a path separator we assume is a directory
|
||||
if (looksLikeADirectory) {
|
||||
return getCacheFileForDirectory();
|
||||
}
|
||||
|
||||
return resolvedCacheFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string array to a boolean map.
|
||||
* @param {string[]|null} keys The keys to assign true.
|
||||
* @param {boolean} defaultValue The default value for each property.
|
||||
* @param {string} displayName The property name which is used in error message.
|
||||
* @throws {Error} Requires array.
|
||||
* @returns {Record<string,boolean>} The boolean map.
|
||||
*/
|
||||
function toBooleanMap(keys, defaultValue, displayName) {
|
||||
if (keys && !Array.isArray(keys)) {
|
||||
throw new Error(`${displayName} must be an array.`);
|
||||
}
|
||||
if (keys && keys.length > 0) {
|
||||
return keys.reduce((map, def) => {
|
||||
const [key, value] = def.split(":");
|
||||
|
||||
if (key !== "__proto__") {
|
||||
map[key] = value === void 0
|
||||
? defaultValue
|
||||
: value === "true";
|
||||
}
|
||||
|
||||
return map;
|
||||
}, {});
|
||||
}
|
||||
return void 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a config data from CLI options.
|
||||
* @param {CLIEngineOptions} options The options
|
||||
* @returns {ConfigData|null} The created config data.
|
||||
*/
|
||||
function createConfigDataFromOptions(options) {
|
||||
const {
|
||||
ignorePattern,
|
||||
parser,
|
||||
parserOptions,
|
||||
plugins,
|
||||
rules
|
||||
} = options;
|
||||
const env = toBooleanMap(options.envs, true, "envs");
|
||||
const globals = toBooleanMap(options.globals, false, "globals");
|
||||
|
||||
if (
|
||||
env === void 0 &&
|
||||
globals === void 0 &&
|
||||
(ignorePattern === void 0 || ignorePattern.length === 0) &&
|
||||
parser === void 0 &&
|
||||
parserOptions === void 0 &&
|
||||
plugins === void 0 &&
|
||||
rules === void 0
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
env,
|
||||
globals,
|
||||
ignorePatterns: ignorePattern,
|
||||
parser,
|
||||
parserOptions,
|
||||
plugins,
|
||||
rules
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a directory exists at the given location
|
||||
* @param {string} resolvedPath A path from the CWD
|
||||
* @throws {Error} As thrown by `fs.statSync` or `fs.isDirectory`.
|
||||
* @returns {boolean} `true` if a directory exists
|
||||
*/
|
||||
function directoryExists(resolvedPath) {
|
||||
try {
|
||||
return fs.statSync(resolvedPath).isDirectory();
|
||||
} catch (error) {
|
||||
if (error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
|
||||
return false;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Core CLI.
|
||||
*/
|
||||
class CLIEngine {
|
||||
|
||||
/**
|
||||
* Creates a new instance of the core CLI engine.
|
||||
* @param {CLIEngineOptions} providedOptions The options for this instance.
|
||||
* @param {Object} [additionalData] Additional settings that are not CLIEngineOptions.
|
||||
* @param {Record<string,Plugin>|null} [additionalData.preloadedPlugins] Preloaded plugins.
|
||||
*/
|
||||
constructor(providedOptions, { preloadedPlugins } = {}) {
|
||||
const options = Object.assign(
|
||||
Object.create(null),
|
||||
defaultOptions,
|
||||
{ cwd: process.cwd() },
|
||||
providedOptions
|
||||
);
|
||||
|
||||
if (options.fix === void 0) {
|
||||
options.fix = false;
|
||||
}
|
||||
|
||||
const additionalPluginPool = new Map();
|
||||
|
||||
if (preloadedPlugins) {
|
||||
for (const [id, plugin] of Object.entries(preloadedPlugins)) {
|
||||
additionalPluginPool.set(id, plugin);
|
||||
}
|
||||
}
|
||||
|
||||
const cacheFilePath = getCacheFile(
|
||||
options.cacheLocation || options.cacheFile,
|
||||
options.cwd
|
||||
);
|
||||
const configArrayFactory = new CascadingConfigArrayFactory({
|
||||
additionalPluginPool,
|
||||
baseConfig: options.baseConfig || null,
|
||||
cliConfig: createConfigDataFromOptions(options),
|
||||
cwd: options.cwd,
|
||||
ignorePath: options.ignorePath,
|
||||
resolvePluginsRelativeTo: options.resolvePluginsRelativeTo,
|
||||
rulePaths: options.rulePaths,
|
||||
specificConfigPath: options.configFile,
|
||||
useEslintrc: options.useEslintrc,
|
||||
builtInRules,
|
||||
loadRules,
|
||||
getEslintRecommendedConfig: () => require("@eslint/js").configs.recommended,
|
||||
getEslintAllConfig: () => require("@eslint/js").configs.all
|
||||
});
|
||||
const fileEnumerator = new FileEnumerator({
|
||||
configArrayFactory,
|
||||
cwd: options.cwd,
|
||||
extensions: options.extensions,
|
||||
globInputPaths: options.globInputPaths,
|
||||
errorOnUnmatchedPattern: options.errorOnUnmatchedPattern,
|
||||
ignore: options.ignore
|
||||
});
|
||||
const lintResultCache =
|
||||
options.cache ? new LintResultCache(cacheFilePath, options.cacheStrategy) : null;
|
||||
const linter = new Linter({ cwd: options.cwd });
|
||||
|
||||
/** @type {ConfigArray[]} */
|
||||
const lastConfigArrays = [configArrayFactory.getConfigArrayForFile()];
|
||||
|
||||
// Store private data.
|
||||
internalSlotsMap.set(this, {
|
||||
additionalPluginPool,
|
||||
cacheFilePath,
|
||||
configArrayFactory,
|
||||
defaultIgnores: IgnorePattern.createDefaultIgnore(options.cwd),
|
||||
fileEnumerator,
|
||||
lastConfigArrays,
|
||||
lintResultCache,
|
||||
linter,
|
||||
options
|
||||
});
|
||||
|
||||
// setup special filter for fixes
|
||||
if (options.fix && options.fixTypes && options.fixTypes.length > 0) {
|
||||
debug(`Using fix types ${options.fixTypes}`);
|
||||
|
||||
// throw an error if any invalid fix types are found
|
||||
validateFixTypes(options.fixTypes);
|
||||
|
||||
// convert to Set for faster lookup
|
||||
const fixTypes = new Set(options.fixTypes);
|
||||
|
||||
// save original value of options.fix in case it's a function
|
||||
const originalFix = (typeof options.fix === "function")
|
||||
? options.fix : () => true;
|
||||
|
||||
options.fix = message => shouldMessageBeFixed(message, lastConfigArrays, fixTypes) && originalFix(message);
|
||||
}
|
||||
}
|
||||
|
||||
getRules() {
|
||||
const { lastConfigArrays } = internalSlotsMap.get(this);
|
||||
|
||||
return new Map(function *() {
|
||||
yield* builtInRules;
|
||||
|
||||
for (const configArray of lastConfigArrays) {
|
||||
yield* configArray.pluginRules;
|
||||
}
|
||||
}());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns results that only contains errors.
|
||||
* @param {LintResult[]} results The results to filter.
|
||||
* @returns {LintResult[]} The filtered results.
|
||||
*/
|
||||
static getErrorResults(results) {
|
||||
const filtered = [];
|
||||
|
||||
results.forEach(result => {
|
||||
const filteredMessages = result.messages.filter(isErrorMessage);
|
||||
const filteredSuppressedMessages = result.suppressedMessages.filter(isErrorMessage);
|
||||
|
||||
if (filteredMessages.length > 0) {
|
||||
filtered.push({
|
||||
...result,
|
||||
messages: filteredMessages,
|
||||
suppressedMessages: filteredSuppressedMessages,
|
||||
errorCount: filteredMessages.length,
|
||||
warningCount: 0,
|
||||
fixableErrorCount: result.fixableErrorCount,
|
||||
fixableWarningCount: 0
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs fixes from the given results to files.
|
||||
* @param {LintReport} report The report object created by CLIEngine.
|
||||
* @returns {void}
|
||||
*/
|
||||
static outputFixes(report) {
|
||||
report.results.filter(result => Object.prototype.hasOwnProperty.call(result, "output")).forEach(result => {
|
||||
fs.writeFileSync(result.filePath, result.output);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the patterns passed into executeOnFiles() into glob-based patterns
|
||||
* for easier handling.
|
||||
* @param {string[]} patterns The file patterns passed on the command line.
|
||||
* @returns {string[]} The equivalent glob patterns.
|
||||
*/
|
||||
resolveFileGlobPatterns(patterns) {
|
||||
const { options } = internalSlotsMap.get(this);
|
||||
|
||||
if (options.globInputPaths === false) {
|
||||
return patterns.filter(Boolean);
|
||||
}
|
||||
|
||||
const extensions = (options.extensions || [".js"]).map(ext => ext.replace(/^\./u, ""));
|
||||
const dirSuffix = `/**/*.{${extensions.join(",")}}`;
|
||||
|
||||
return patterns.filter(Boolean).map(pathname => {
|
||||
const resolvedPath = path.resolve(options.cwd, pathname);
|
||||
const newPath = directoryExists(resolvedPath)
|
||||
? pathname.replace(/[/\\]$/u, "") + dirSuffix
|
||||
: pathname;
|
||||
|
||||
return path.normalize(newPath).replace(/\\/gu, "/");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the current configuration on an array of file and directory names.
|
||||
* @param {string[]} patterns An array of file and directory names.
|
||||
* @throws {Error} As may be thrown by `fs.unlinkSync`.
|
||||
* @returns {LintReport} The results for all files that were linted.
|
||||
*/
|
||||
executeOnFiles(patterns) {
|
||||
const {
|
||||
cacheFilePath,
|
||||
fileEnumerator,
|
||||
lastConfigArrays,
|
||||
lintResultCache,
|
||||
linter,
|
||||
options: {
|
||||
allowInlineConfig,
|
||||
cache,
|
||||
cwd,
|
||||
fix,
|
||||
reportUnusedDisableDirectives
|
||||
}
|
||||
} = internalSlotsMap.get(this);
|
||||
const results = [];
|
||||
const startTime = Date.now();
|
||||
|
||||
// Clear the last used config arrays.
|
||||
lastConfigArrays.length = 0;
|
||||
|
||||
// Delete cache file; should this do here?
|
||||
if (!cache) {
|
||||
try {
|
||||
fs.unlinkSync(cacheFilePath);
|
||||
} catch (error) {
|
||||
const errorCode = error && error.code;
|
||||
|
||||
// Ignore errors when no such file exists or file system is read only (and cache file does not exist)
|
||||
if (errorCode !== "ENOENT" && !(errorCode === "EROFS" && !fs.existsSync(cacheFilePath))) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate source code files.
|
||||
for (const { config, filePath, ignored } of fileEnumerator.iterateFiles(patterns)) {
|
||||
if (ignored) {
|
||||
results.push(createIgnoreResult(filePath, cwd));
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Store used configs for:
|
||||
* - this method uses to collect used deprecated rules.
|
||||
* - `getRules()` method uses to collect all loaded rules.
|
||||
* - `--fix-type` option uses to get the loaded rule's meta data.
|
||||
*/
|
||||
if (!lastConfigArrays.includes(config)) {
|
||||
lastConfigArrays.push(config);
|
||||
}
|
||||
|
||||
// Skip if there is cached result.
|
||||
if (lintResultCache) {
|
||||
const cachedResult =
|
||||
lintResultCache.getCachedLintResults(filePath, config);
|
||||
|
||||
if (cachedResult) {
|
||||
const hadMessages =
|
||||
cachedResult.messages &&
|
||||
cachedResult.messages.length > 0;
|
||||
|
||||
if (hadMessages && fix) {
|
||||
debug(`Reprocessing cached file to allow autofix: ${filePath}`);
|
||||
} else {
|
||||
debug(`Skipping file since it hasn't changed: ${filePath}`);
|
||||
results.push(cachedResult);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do lint.
|
||||
const result = verifyText({
|
||||
text: fs.readFileSync(filePath, "utf8"),
|
||||
filePath,
|
||||
config,
|
||||
cwd,
|
||||
fix,
|
||||
allowInlineConfig,
|
||||
reportUnusedDisableDirectives,
|
||||
fileEnumerator,
|
||||
linter
|
||||
});
|
||||
|
||||
results.push(result);
|
||||
|
||||
/*
|
||||
* Store the lint result in the LintResultCache.
|
||||
* NOTE: The LintResultCache will remove the file source and any
|
||||
* other properties that are difficult to serialize, and will
|
||||
* hydrate those properties back in on future lint runs.
|
||||
*/
|
||||
if (lintResultCache) {
|
||||
lintResultCache.setCachedLintResults(filePath, config, result);
|
||||
}
|
||||
}
|
||||
|
||||
// Persist the cache to disk.
|
||||
if (lintResultCache) {
|
||||
lintResultCache.reconcile();
|
||||
}
|
||||
|
||||
debug(`Linting complete in: ${Date.now() - startTime}ms`);
|
||||
let usedDeprecatedRules;
|
||||
|
||||
return {
|
||||
results,
|
||||
...calculateStatsPerRun(results),
|
||||
|
||||
// Initialize it lazily because CLI and `ESLint` API don't use it.
|
||||
get usedDeprecatedRules() {
|
||||
if (!usedDeprecatedRules) {
|
||||
usedDeprecatedRules = Array.from(
|
||||
iterateRuleDeprecationWarnings(lastConfigArrays)
|
||||
);
|
||||
}
|
||||
return usedDeprecatedRules;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the current configuration on text.
|
||||
* @param {string} text A string of JavaScript code to lint.
|
||||
* @param {string} [filename] An optional string representing the texts filename.
|
||||
* @param {boolean} [warnIgnored] Always warn when a file is ignored
|
||||
* @returns {LintReport} The results for the linting.
|
||||
*/
|
||||
executeOnText(text, filename, warnIgnored) {
|
||||
const {
|
||||
configArrayFactory,
|
||||
fileEnumerator,
|
||||
lastConfigArrays,
|
||||
linter,
|
||||
options: {
|
||||
allowInlineConfig,
|
||||
cwd,
|
||||
fix,
|
||||
reportUnusedDisableDirectives
|
||||
}
|
||||
} = internalSlotsMap.get(this);
|
||||
const results = [];
|
||||
const startTime = Date.now();
|
||||
const resolvedFilename = filename && path.resolve(cwd, filename);
|
||||
|
||||
|
||||
// Clear the last used config arrays.
|
||||
lastConfigArrays.length = 0;
|
||||
if (resolvedFilename && this.isPathIgnored(resolvedFilename)) {
|
||||
if (warnIgnored) {
|
||||
results.push(createIgnoreResult(resolvedFilename, cwd));
|
||||
}
|
||||
} else {
|
||||
const config = configArrayFactory.getConfigArrayForFile(
|
||||
resolvedFilename || "__placeholder__.js"
|
||||
);
|
||||
|
||||
/*
|
||||
* Store used configs for:
|
||||
* - this method uses to collect used deprecated rules.
|
||||
* - `getRules()` method uses to collect all loaded rules.
|
||||
* - `--fix-type` option uses to get the loaded rule's meta data.
|
||||
*/
|
||||
lastConfigArrays.push(config);
|
||||
|
||||
// Do lint.
|
||||
results.push(verifyText({
|
||||
text,
|
||||
filePath: resolvedFilename,
|
||||
config,
|
||||
cwd,
|
||||
fix,
|
||||
allowInlineConfig,
|
||||
reportUnusedDisableDirectives,
|
||||
fileEnumerator,
|
||||
linter
|
||||
}));
|
||||
}
|
||||
|
||||
debug(`Linting complete in: ${Date.now() - startTime}ms`);
|
||||
let usedDeprecatedRules;
|
||||
|
||||
return {
|
||||
results,
|
||||
...calculateStatsPerRun(results),
|
||||
|
||||
// Initialize it lazily because CLI and `ESLint` API don't use it.
|
||||
get usedDeprecatedRules() {
|
||||
if (!usedDeprecatedRules) {
|
||||
usedDeprecatedRules = Array.from(
|
||||
iterateRuleDeprecationWarnings(lastConfigArrays)
|
||||
);
|
||||
}
|
||||
return usedDeprecatedRules;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a configuration object for the given file based on the CLI options.
|
||||
* This is the same logic used by the ESLint CLI executable to determine
|
||||
* configuration for each file it processes.
|
||||
* @param {string} filePath The path of the file to retrieve a config object for.
|
||||
* @throws {Error} If filepath a directory path.
|
||||
* @returns {ConfigData} A configuration object for the file.
|
||||
*/
|
||||
getConfigForFile(filePath) {
|
||||
const { configArrayFactory, options } = internalSlotsMap.get(this);
|
||||
const absolutePath = path.resolve(options.cwd, filePath);
|
||||
|
||||
if (directoryExists(absolutePath)) {
|
||||
throw Object.assign(
|
||||
new Error("'filePath' should not be a directory path."),
|
||||
{ messageTemplate: "print-config-with-directory-path" }
|
||||
);
|
||||
}
|
||||
|
||||
return configArrayFactory
|
||||
.getConfigArrayForFile(absolutePath)
|
||||
.extractConfig(absolutePath)
|
||||
.toCompatibleObjectAsConfigFileContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given path is ignored by ESLint.
|
||||
* @param {string} filePath The path of the file to check.
|
||||
* @returns {boolean} Whether or not the given path is ignored.
|
||||
*/
|
||||
isPathIgnored(filePath) {
|
||||
const {
|
||||
configArrayFactory,
|
||||
defaultIgnores,
|
||||
options: { cwd, ignore }
|
||||
} = internalSlotsMap.get(this);
|
||||
const absolutePath = path.resolve(cwd, filePath);
|
||||
|
||||
if (ignore) {
|
||||
const config = configArrayFactory
|
||||
.getConfigArrayForFile(absolutePath)
|
||||
.extractConfig(absolutePath);
|
||||
const ignores = config.ignores || defaultIgnores;
|
||||
|
||||
return ignores(absolutePath);
|
||||
}
|
||||
|
||||
return defaultIgnores(absolutePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the formatter representing the given format or null if the `format` is not a string.
|
||||
* @param {string} [format] The name of the format to load or the path to a
|
||||
* custom formatter.
|
||||
* @throws {any} As may be thrown by requiring of formatter
|
||||
* @returns {(FormatterFunction|null)} The formatter function or null if the `format` is not a string.
|
||||
*/
|
||||
getFormatter(format) {
|
||||
|
||||
// default is stylish
|
||||
const resolvedFormatName = format || "stylish";
|
||||
|
||||
// only strings are valid formatters
|
||||
if (typeof resolvedFormatName === "string") {
|
||||
|
||||
// replace \ with / for Windows compatibility
|
||||
const normalizedFormatName = resolvedFormatName.replace(/\\/gu, "/");
|
||||
|
||||
const slots = internalSlotsMap.get(this);
|
||||
const cwd = slots ? slots.options.cwd : process.cwd();
|
||||
const namespace = naming.getNamespaceFromTerm(normalizedFormatName);
|
||||
|
||||
let formatterPath;
|
||||
|
||||
// if there's a slash, then it's a file (TODO: this check seems dubious for scoped npm packages)
|
||||
if (!namespace && normalizedFormatName.includes("/")) {
|
||||
formatterPath = path.resolve(cwd, normalizedFormatName);
|
||||
} else {
|
||||
try {
|
||||
const npmFormat = naming.normalizePackageName(normalizedFormatName, "eslint-formatter");
|
||||
|
||||
formatterPath = ModuleResolver.resolve(npmFormat, path.join(cwd, "__placeholder__.js"));
|
||||
} catch {
|
||||
formatterPath = path.resolve(__dirname, "formatters", normalizedFormatName);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return require(formatterPath);
|
||||
} catch (ex) {
|
||||
if (format === "table" || format === "codeframe") {
|
||||
ex.message = `The ${format} formatter is no longer part of core ESLint. Install it manually with \`npm install -D eslint-formatter-${format}\``;
|
||||
} else {
|
||||
ex.message = `There was a problem loading formatter: ${formatterPath}\nError: ${ex.message}`;
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CLIEngine.version = pkg.version;
|
||||
CLIEngine.getFormatter = CLIEngine.prototype.getFormatter;
|
||||
|
||||
module.exports = {
|
||||
CLIEngine,
|
||||
|
||||
/**
|
||||
* Get the internal slots of a given CLIEngine instance for tests.
|
||||
* @param {CLIEngine} instance The CLIEngine instance to get.
|
||||
* @returns {CLIEngineInternalSlots} The internal slots.
|
||||
*/
|
||||
getCLIEngineInternalSlots(instance) {
|
||||
return internalSlotsMap.get(instance);
|
||||
}
|
||||
};
|
||||
547
node_modules/eslint/lib/cli-engine/file-enumerator.js
generated
vendored
Normal file
547
node_modules/eslint/lib/cli-engine/file-enumerator.js
generated
vendored
Normal file
@@ -0,0 +1,547 @@
|
||||
/**
|
||||
* @fileoverview `FileEnumerator` class.
|
||||
*
|
||||
* `FileEnumerator` class has two responsibilities:
|
||||
*
|
||||
* 1. Find target files by processing glob patterns.
|
||||
* 2. Tie each target file and appropriate configuration.
|
||||
*
|
||||
* It provides a method:
|
||||
*
|
||||
* - `iterateFiles(patterns)`
|
||||
* Iterate files which are matched by given patterns together with the
|
||||
* corresponded configuration. This is for `CLIEngine#executeOnFiles()`.
|
||||
* While iterating files, it loads the configuration file of each directory
|
||||
* before iterate files on the directory, so we can use the configuration
|
||||
* files to determine target files.
|
||||
*
|
||||
* @example
|
||||
* const enumerator = new FileEnumerator();
|
||||
* const linter = new Linter();
|
||||
*
|
||||
* for (const { config, filePath } of enumerator.iterateFiles(["*.js"])) {
|
||||
* const code = fs.readFileSync(filePath, "utf8");
|
||||
* const messages = linter.verify(code, config, filePath);
|
||||
*
|
||||
* console.log(messages);
|
||||
* }
|
||||
*
|
||||
* @author Toru Nagashima <https://github.com/mysticatea>
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const getGlobParent = require("glob-parent");
|
||||
const isGlob = require("is-glob");
|
||||
const escapeRegExp = require("escape-string-regexp");
|
||||
const { Minimatch } = require("minimatch");
|
||||
|
||||
const {
|
||||
Legacy: {
|
||||
IgnorePattern,
|
||||
CascadingConfigArrayFactory
|
||||
}
|
||||
} = require("@eslint/eslintrc");
|
||||
const debug = require("debug")("eslint:file-enumerator");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const minimatchOpts = { dot: true, matchBase: true };
|
||||
const dotfilesPattern = /(?:(?:^\.)|(?:[/\\]\.))[^/\\.].*/u;
|
||||
const NONE = 0;
|
||||
const IGNORED_SILENTLY = 1;
|
||||
const IGNORED = 2;
|
||||
|
||||
// For VSCode intellisense
|
||||
/** @typedef {ReturnType<CascadingConfigArrayFactory.getConfigArrayForFile>} ConfigArray */
|
||||
|
||||
/**
|
||||
* @typedef {Object} FileEnumeratorOptions
|
||||
* @property {CascadingConfigArrayFactory} [configArrayFactory] The factory for config arrays.
|
||||
* @property {string} [cwd] The base directory to start lookup.
|
||||
* @property {string[]} [extensions] The extensions to match files for directory patterns.
|
||||
* @property {boolean} [globInputPaths] Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file.
|
||||
* @property {boolean} [ignore] The flag to check ignored files.
|
||||
* @property {string[]} [rulePaths] The value of `--rulesdir` option.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} FileAndConfig
|
||||
* @property {string} filePath The path to a target file.
|
||||
* @property {ConfigArray} config The config entries of that file.
|
||||
* @property {boolean} ignored If `true` then this file should be ignored and warned because it was directly specified.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} FileEntry
|
||||
* @property {string} filePath The path to a target file.
|
||||
* @property {ConfigArray} config The config entries of that file.
|
||||
* @property {NONE|IGNORED_SILENTLY|IGNORED} flag The flag.
|
||||
* - `NONE` means the file is a target file.
|
||||
* - `IGNORED_SILENTLY` means the file should be ignored silently.
|
||||
* - `IGNORED` means the file should be ignored and warned because it was directly specified.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} FileEnumeratorInternalSlots
|
||||
* @property {CascadingConfigArrayFactory} configArrayFactory The factory for config arrays.
|
||||
* @property {string} cwd The base directory to start lookup.
|
||||
* @property {RegExp|null} extensionRegExp The RegExp to test if a string ends with specific file extensions.
|
||||
* @property {boolean} globInputPaths Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file.
|
||||
* @property {boolean} ignoreFlag The flag to check ignored files.
|
||||
* @property {(filePath:string, dot:boolean) => boolean} defaultIgnores The default predicate function to ignore files.
|
||||
*/
|
||||
|
||||
/** @type {WeakMap<FileEnumerator, FileEnumeratorInternalSlots>} */
|
||||
const internalSlotsMap = new WeakMap();
|
||||
|
||||
/**
|
||||
* Check if a string is a glob pattern or not.
|
||||
* @param {string} pattern A glob pattern.
|
||||
* @returns {boolean} `true` if the string is a glob pattern.
|
||||
*/
|
||||
function isGlobPattern(pattern) {
|
||||
return isGlob(path.sep === "\\" ? pattern.replace(/\\/gu, "/") : pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stats of a given path.
|
||||
* @param {string} filePath The path to target file.
|
||||
* @throws {Error} As may be thrown by `fs.statSync`.
|
||||
* @returns {fs.Stats|null} The stats.
|
||||
* @private
|
||||
*/
|
||||
function statSafeSync(filePath) {
|
||||
try {
|
||||
return fs.statSync(filePath);
|
||||
} catch (error) {
|
||||
|
||||
/* c8 ignore next */
|
||||
if (error.code !== "ENOENT") {
|
||||
throw error;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filenames in a given path to a directory.
|
||||
* @param {string} directoryPath The path to target directory.
|
||||
* @throws {Error} As may be thrown by `fs.readdirSync`.
|
||||
* @returns {import("fs").Dirent[]} The filenames.
|
||||
* @private
|
||||
*/
|
||||
function readdirSafeSync(directoryPath) {
|
||||
try {
|
||||
return fs.readdirSync(directoryPath, { withFileTypes: true });
|
||||
} catch (error) {
|
||||
|
||||
/* c8 ignore next */
|
||||
if (error.code !== "ENOENT") {
|
||||
throw error;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a `RegExp` object to detect extensions.
|
||||
* @param {string[] | null} extensions The extensions to create.
|
||||
* @returns {RegExp | null} The created `RegExp` object or null.
|
||||
*/
|
||||
function createExtensionRegExp(extensions) {
|
||||
if (extensions) {
|
||||
const normalizedExts = extensions.map(ext => escapeRegExp(
|
||||
ext.startsWith(".")
|
||||
? ext.slice(1)
|
||||
: ext
|
||||
));
|
||||
|
||||
return new RegExp(
|
||||
`.\\.(?:${normalizedExts.join("|")})$`,
|
||||
"u"
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The error type when no files match a glob.
|
||||
*/
|
||||
class NoFilesFoundError extends Error {
|
||||
|
||||
/**
|
||||
* @param {string} pattern The glob pattern which was not found.
|
||||
* @param {boolean} globDisabled If `true` then the pattern was a glob pattern, but glob was disabled.
|
||||
*/
|
||||
constructor(pattern, globDisabled) {
|
||||
super(`No files matching '${pattern}' were found${globDisabled ? " (glob was disabled)" : ""}.`);
|
||||
this.messageTemplate = "file-not-found";
|
||||
this.messageData = { pattern, globDisabled };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The error type when there are files matched by a glob, but all of them have been ignored.
|
||||
*/
|
||||
class AllFilesIgnoredError extends Error {
|
||||
|
||||
/**
|
||||
* @param {string} pattern The glob pattern which was not found.
|
||||
*/
|
||||
constructor(pattern) {
|
||||
super(`All files matched by '${pattern}' are ignored.`);
|
||||
this.messageTemplate = "all-files-ignored";
|
||||
this.messageData = { pattern };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class provides the functionality that enumerates every file which is
|
||||
* matched by given glob patterns and that configuration.
|
||||
*/
|
||||
class FileEnumerator {
|
||||
|
||||
/**
|
||||
* Initialize this enumerator.
|
||||
* @param {FileEnumeratorOptions} options The options.
|
||||
*/
|
||||
constructor({
|
||||
cwd = process.cwd(),
|
||||
configArrayFactory = new CascadingConfigArrayFactory({
|
||||
cwd,
|
||||
getEslintRecommendedConfig: () => require("@eslint/js").configs.recommended,
|
||||
getEslintAllConfig: () => require("@eslint/js").configs.all
|
||||
}),
|
||||
extensions = null,
|
||||
globInputPaths = true,
|
||||
errorOnUnmatchedPattern = true,
|
||||
ignore = true
|
||||
} = {}) {
|
||||
internalSlotsMap.set(this, {
|
||||
configArrayFactory,
|
||||
cwd,
|
||||
defaultIgnores: IgnorePattern.createDefaultIgnore(cwd),
|
||||
extensionRegExp: createExtensionRegExp(extensions),
|
||||
globInputPaths,
|
||||
errorOnUnmatchedPattern,
|
||||
ignoreFlag: ignore
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given file is target or not.
|
||||
* @param {string} filePath The path to a candidate file.
|
||||
* @param {ConfigArray} [providedConfig] Optional. The configuration for the file.
|
||||
* @returns {boolean} `true` if the file is a target.
|
||||
*/
|
||||
isTargetPath(filePath, providedConfig) {
|
||||
const {
|
||||
configArrayFactory,
|
||||
extensionRegExp
|
||||
} = internalSlotsMap.get(this);
|
||||
|
||||
// If `--ext` option is present, use it.
|
||||
if (extensionRegExp) {
|
||||
return extensionRegExp.test(filePath);
|
||||
}
|
||||
|
||||
// `.js` file is target by default.
|
||||
if (filePath.endsWith(".js")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// use `overrides[].files` to check additional targets.
|
||||
const config =
|
||||
providedConfig ||
|
||||
configArrayFactory.getConfigArrayForFile(
|
||||
filePath,
|
||||
{ ignoreNotFoundError: true }
|
||||
);
|
||||
|
||||
return config.isAdditionalTargetPath(filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate files which are matched by given glob patterns.
|
||||
* @param {string|string[]} patternOrPatterns The glob patterns to iterate files.
|
||||
* @throws {NoFilesFoundError|AllFilesIgnoredError} On an unmatched pattern.
|
||||
* @returns {IterableIterator<FileAndConfig>} The found files.
|
||||
*/
|
||||
*iterateFiles(patternOrPatterns) {
|
||||
const { globInputPaths, errorOnUnmatchedPattern } = internalSlotsMap.get(this);
|
||||
const patterns = Array.isArray(patternOrPatterns)
|
||||
? patternOrPatterns
|
||||
: [patternOrPatterns];
|
||||
|
||||
debug("Start to iterate files: %o", patterns);
|
||||
|
||||
// The set of paths to remove duplicate.
|
||||
const set = new Set();
|
||||
|
||||
for (const pattern of patterns) {
|
||||
let foundRegardlessOfIgnored = false;
|
||||
let found = false;
|
||||
|
||||
// Skip empty string.
|
||||
if (!pattern) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Iterate files of this pattern.
|
||||
for (const { config, filePath, flag } of this._iterateFiles(pattern)) {
|
||||
foundRegardlessOfIgnored = true;
|
||||
if (flag === IGNORED_SILENTLY) {
|
||||
continue;
|
||||
}
|
||||
found = true;
|
||||
|
||||
// Remove duplicate paths while yielding paths.
|
||||
if (!set.has(filePath)) {
|
||||
set.add(filePath);
|
||||
yield {
|
||||
config,
|
||||
filePath,
|
||||
ignored: flag === IGNORED
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Raise an error if any files were not found.
|
||||
if (errorOnUnmatchedPattern) {
|
||||
if (!foundRegardlessOfIgnored) {
|
||||
throw new NoFilesFoundError(
|
||||
pattern,
|
||||
!globInputPaths && isGlob(pattern)
|
||||
);
|
||||
}
|
||||
if (!found) {
|
||||
throw new AllFilesIgnoredError(pattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug(`Complete iterating files: ${JSON.stringify(patterns)}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate files which are matched by a given glob pattern.
|
||||
* @param {string} pattern The glob pattern to iterate files.
|
||||
* @returns {IterableIterator<FileEntry>} The found files.
|
||||
*/
|
||||
_iterateFiles(pattern) {
|
||||
const { cwd, globInputPaths } = internalSlotsMap.get(this);
|
||||
const absolutePath = path.resolve(cwd, pattern);
|
||||
const isDot = dotfilesPattern.test(pattern);
|
||||
const stat = statSafeSync(absolutePath);
|
||||
|
||||
if (stat && stat.isDirectory()) {
|
||||
return this._iterateFilesWithDirectory(absolutePath, isDot);
|
||||
}
|
||||
if (stat && stat.isFile()) {
|
||||
return this._iterateFilesWithFile(absolutePath);
|
||||
}
|
||||
if (globInputPaths && isGlobPattern(pattern)) {
|
||||
return this._iterateFilesWithGlob(pattern, isDot);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate a file which is matched by a given path.
|
||||
* @param {string} filePath The path to the target file.
|
||||
* @returns {IterableIterator<FileEntry>} The found files.
|
||||
* @private
|
||||
*/
|
||||
_iterateFilesWithFile(filePath) {
|
||||
debug(`File: ${filePath}`);
|
||||
|
||||
const { configArrayFactory } = internalSlotsMap.get(this);
|
||||
const config = configArrayFactory.getConfigArrayForFile(filePath);
|
||||
const ignored = this._isIgnoredFile(filePath, { config, direct: true });
|
||||
const flag = ignored ? IGNORED : NONE;
|
||||
|
||||
return [{ config, filePath, flag }];
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate files in a given path.
|
||||
* @param {string} directoryPath The path to the target directory.
|
||||
* @param {boolean} dotfiles If `true` then it doesn't skip dot files by default.
|
||||
* @returns {IterableIterator<FileEntry>} The found files.
|
||||
* @private
|
||||
*/
|
||||
_iterateFilesWithDirectory(directoryPath, dotfiles) {
|
||||
debug(`Directory: ${directoryPath}`);
|
||||
|
||||
return this._iterateFilesRecursive(
|
||||
directoryPath,
|
||||
{ dotfiles, recursive: true, selector: null }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate files which are matched by a given glob pattern.
|
||||
* @param {string} pattern The glob pattern to iterate files.
|
||||
* @param {boolean} dotfiles If `true` then it doesn't skip dot files by default.
|
||||
* @returns {IterableIterator<FileEntry>} The found files.
|
||||
* @private
|
||||
*/
|
||||
_iterateFilesWithGlob(pattern, dotfiles) {
|
||||
debug(`Glob: ${pattern}`);
|
||||
|
||||
const { cwd } = internalSlotsMap.get(this);
|
||||
const directoryPath = path.resolve(cwd, getGlobParent(pattern));
|
||||
const absolutePath = path.resolve(cwd, pattern);
|
||||
const globPart = absolutePath.slice(directoryPath.length + 1);
|
||||
|
||||
/*
|
||||
* recursive if there are `**` or path separators in the glob part.
|
||||
* Otherwise, patterns such as `src/*.js`, it doesn't need recursive.
|
||||
*/
|
||||
const recursive = /\*\*|\/|\\/u.test(globPart);
|
||||
const selector = new Minimatch(absolutePath, minimatchOpts);
|
||||
|
||||
debug(`recursive? ${recursive}`);
|
||||
|
||||
return this._iterateFilesRecursive(
|
||||
directoryPath,
|
||||
{ dotfiles, recursive, selector }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate files in a given path.
|
||||
* @param {string} directoryPath The path to the target directory.
|
||||
* @param {Object} options The options to iterate files.
|
||||
* @param {boolean} [options.dotfiles] If `true` then it doesn't skip dot files by default.
|
||||
* @param {boolean} [options.recursive] If `true` then it dives into sub directories.
|
||||
* @param {InstanceType<Minimatch>} [options.selector] The matcher to choose files.
|
||||
* @returns {IterableIterator<FileEntry>} The found files.
|
||||
* @private
|
||||
*/
|
||||
*_iterateFilesRecursive(directoryPath, options) {
|
||||
debug(`Enter the directory: ${directoryPath}`);
|
||||
const { configArrayFactory } = internalSlotsMap.get(this);
|
||||
|
||||
/** @type {ConfigArray|null} */
|
||||
let config = null;
|
||||
|
||||
// Enumerate the files of this directory.
|
||||
for (const entry of readdirSafeSync(directoryPath)) {
|
||||
const filePath = path.join(directoryPath, entry.name);
|
||||
const fileInfo = entry.isSymbolicLink() ? statSafeSync(filePath) : entry;
|
||||
|
||||
if (!fileInfo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the file is matched.
|
||||
if (fileInfo.isFile()) {
|
||||
if (!config) {
|
||||
config = configArrayFactory.getConfigArrayForFile(
|
||||
filePath,
|
||||
|
||||
/*
|
||||
* We must ignore `ConfigurationNotFoundError` at this
|
||||
* point because we don't know if target files exist in
|
||||
* this directory.
|
||||
*/
|
||||
{ ignoreNotFoundError: true }
|
||||
);
|
||||
}
|
||||
const matched = options.selector
|
||||
|
||||
// Started with a glob pattern; choose by the pattern.
|
||||
? options.selector.match(filePath)
|
||||
|
||||
// Started with a directory path; choose by file extensions.
|
||||
: this.isTargetPath(filePath, config);
|
||||
|
||||
if (matched) {
|
||||
const ignored = this._isIgnoredFile(filePath, { ...options, config });
|
||||
const flag = ignored ? IGNORED_SILENTLY : NONE;
|
||||
|
||||
debug(`Yield: ${entry.name}${ignored ? " but ignored" : ""}`);
|
||||
yield {
|
||||
config: configArrayFactory.getConfigArrayForFile(filePath),
|
||||
filePath,
|
||||
flag
|
||||
};
|
||||
} else {
|
||||
debug(`Didn't match: ${entry.name}`);
|
||||
}
|
||||
|
||||
// Dive into the sub directory.
|
||||
} else if (options.recursive && fileInfo.isDirectory()) {
|
||||
if (!config) {
|
||||
config = configArrayFactory.getConfigArrayForFile(
|
||||
filePath,
|
||||
{ ignoreNotFoundError: true }
|
||||
);
|
||||
}
|
||||
const ignored = this._isIgnoredFile(
|
||||
filePath + path.sep,
|
||||
{ ...options, config }
|
||||
);
|
||||
|
||||
if (!ignored) {
|
||||
yield* this._iterateFilesRecursive(filePath, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug(`Leave the directory: ${directoryPath}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given file should be ignored.
|
||||
* @param {string} filePath The path to a file to check.
|
||||
* @param {Object} options Options
|
||||
* @param {ConfigArray} [options.config] The config for this file.
|
||||
* @param {boolean} [options.dotfiles] If `true` then this is not ignore dot files by default.
|
||||
* @param {boolean} [options.direct] If `true` then this is a direct specified file.
|
||||
* @returns {boolean} `true` if the file should be ignored.
|
||||
* @private
|
||||
*/
|
||||
_isIgnoredFile(filePath, {
|
||||
config: providedConfig,
|
||||
dotfiles = false,
|
||||
direct = false
|
||||
}) {
|
||||
const {
|
||||
configArrayFactory,
|
||||
defaultIgnores,
|
||||
ignoreFlag
|
||||
} = internalSlotsMap.get(this);
|
||||
|
||||
if (ignoreFlag) {
|
||||
const config =
|
||||
providedConfig ||
|
||||
configArrayFactory.getConfigArrayForFile(
|
||||
filePath,
|
||||
{ ignoreNotFoundError: true }
|
||||
);
|
||||
const ignores =
|
||||
config.extractConfig(filePath).ignores || defaultIgnores;
|
||||
|
||||
return ignores(filePath, dotfiles);
|
||||
}
|
||||
|
||||
return !direct && defaultIgnores(filePath, dotfiles);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = { FileEnumerator };
|
||||
60
node_modules/eslint/lib/cli-engine/formatters/checkstyle.js
generated
vendored
Normal file
60
node_modules/eslint/lib/cli-engine/formatters/checkstyle.js
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* @fileoverview CheckStyle XML reporter
|
||||
* @author Ian Christian Myers
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const xmlEscape = require("../xml-escape");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helper Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the severity of warning or error
|
||||
* @param {Object} message message object to examine
|
||||
* @returns {string} severity level
|
||||
* @private
|
||||
*/
|
||||
function getMessageType(message) {
|
||||
if (message.fatal || message.severity === 2) {
|
||||
return "error";
|
||||
}
|
||||
return "warning";
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = function(results) {
|
||||
|
||||
let output = "";
|
||||
|
||||
output += "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
|
||||
output += "<checkstyle version=\"4.3\">";
|
||||
|
||||
results.forEach(result => {
|
||||
const messages = result.messages;
|
||||
|
||||
output += `<file name="${xmlEscape(result.filePath)}">`;
|
||||
|
||||
messages.forEach(message => {
|
||||
output += [
|
||||
`<error line="${xmlEscape(message.line || 0)}"`,
|
||||
`column="${xmlEscape(message.column || 0)}"`,
|
||||
`severity="${xmlEscape(getMessageType(message))}"`,
|
||||
`message="${xmlEscape(message.message)}${message.ruleId ? ` (${message.ruleId})` : ""}"`,
|
||||
`source="${message.ruleId ? xmlEscape(`eslint.rules.${message.ruleId}`) : ""}" />`
|
||||
].join(" ");
|
||||
});
|
||||
|
||||
output += "</file>";
|
||||
|
||||
});
|
||||
|
||||
output += "</checkstyle>";
|
||||
|
||||
return output;
|
||||
};
|
||||
60
node_modules/eslint/lib/cli-engine/formatters/compact.js
generated
vendored
Normal file
60
node_modules/eslint/lib/cli-engine/formatters/compact.js
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* @fileoverview Compact reporter
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helper Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the severity of warning or error
|
||||
* @param {Object} message message object to examine
|
||||
* @returns {string} severity level
|
||||
* @private
|
||||
*/
|
||||
function getMessageType(message) {
|
||||
if (message.fatal || message.severity === 2) {
|
||||
return "Error";
|
||||
}
|
||||
return "Warning";
|
||||
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = function(results) {
|
||||
|
||||
let output = "",
|
||||
total = 0;
|
||||
|
||||
results.forEach(result => {
|
||||
|
||||
const messages = result.messages;
|
||||
|
||||
total += messages.length;
|
||||
|
||||
messages.forEach(message => {
|
||||
|
||||
output += `${result.filePath}: `;
|
||||
output += `line ${message.line || 0}`;
|
||||
output += `, col ${message.column || 0}`;
|
||||
output += `, ${getMessageType(message)}`;
|
||||
output += ` - ${message.message}`;
|
||||
output += message.ruleId ? ` (${message.ruleId})` : "";
|
||||
output += "\n";
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
if (total > 0) {
|
||||
output += `\n${total} problem${total !== 1 ? "s" : ""}`;
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
||||
46
node_modules/eslint/lib/cli-engine/formatters/formatters-meta.json
generated
vendored
Normal file
46
node_modules/eslint/lib/cli-engine/formatters/formatters-meta.json
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
[
|
||||
{
|
||||
"name": "checkstyle",
|
||||
"description": "Outputs results to the [Checkstyle](https://checkstyle.sourceforge.io/) format."
|
||||
},
|
||||
{
|
||||
"name": "compact",
|
||||
"description": "Human-readable output format. Mimics the default output of JSHint."
|
||||
},
|
||||
{
|
||||
"name": "html",
|
||||
"description": "Outputs results to HTML. The `html` formatter is useful for visual presentation in the browser."
|
||||
},
|
||||
{
|
||||
"name": "jslint-xml",
|
||||
"description": "Outputs results to format compatible with the [JSLint Jenkins plugin](https://plugins.jenkins.io/jslint/)."
|
||||
},
|
||||
{
|
||||
"name": "json-with-metadata",
|
||||
"description": "Outputs JSON-serialized results. The `json-with-metadata` provides the same linting results as the [`json`](#json) formatter with additional metadata about the rules applied. The linting results are included in the `results` property and the rules metadata is included in the `metadata` property.\n\nAlternatively, you can use the [ESLint Node.js API](../../integrate/nodejs-api) to programmatically use ESLint."
|
||||
},
|
||||
{
|
||||
"name": "json",
|
||||
"description": "Outputs JSON-serialized results. The `json` formatter is useful when you want to programmatically work with the CLI's linting results.\n\nAlternatively, you can use the [ESLint Node.js API](../../integrate/nodejs-api) to programmatically use ESLint."
|
||||
},
|
||||
{
|
||||
"name": "junit",
|
||||
"description": "Outputs results to format compatible with the [JUnit Jenkins plugin](https://plugins.jenkins.io/junit/)."
|
||||
},
|
||||
{
|
||||
"name": "stylish",
|
||||
"description": "Human-readable output format. This is the default formatter."
|
||||
},
|
||||
{
|
||||
"name": "tap",
|
||||
"description": "Outputs results to the [Test Anything Protocol (TAP)](https://testanything.org/) specification format."
|
||||
},
|
||||
{
|
||||
"name": "unix",
|
||||
"description": "Outputs results to a format similar to many commands in UNIX-like systems. Parsable with tools such as [grep](https://www.gnu.org/software/grep/manual/grep.html), [sed](https://www.gnu.org/software/sed/manual/sed.html), and [awk](https://www.gnu.org/software/gawk/manual/gawk.html)."
|
||||
},
|
||||
{
|
||||
"name": "visualstudio",
|
||||
"description": "Outputs results to format compatible with the integrated terminal of the [Visual Studio](https://visualstudio.microsoft.com/) IDE. When using Visual Studio, you can click on the linting results in the integrated terminal to go to the issue in the source code."
|
||||
}
|
||||
]
|
||||
351
node_modules/eslint/lib/cli-engine/formatters/html.js
generated
vendored
Normal file
351
node_modules/eslint/lib/cli-engine/formatters/html.js
generated
vendored
Normal file
@@ -0,0 +1,351 @@
|
||||
/**
|
||||
* @fileoverview HTML reporter
|
||||
* @author Julian Laval
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const encodeHTML = (function() {
|
||||
const encodeHTMLRules = {
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
'"': """,
|
||||
"'": "'"
|
||||
};
|
||||
const matchHTML = /[&<>"']/ug;
|
||||
|
||||
return function(code) {
|
||||
return code
|
||||
? code.toString().replace(matchHTML, m => encodeHTMLRules[m] || m)
|
||||
: "";
|
||||
};
|
||||
}());
|
||||
|
||||
/**
|
||||
* Get the final HTML document.
|
||||
* @param {Object} it data for the document.
|
||||
* @returns {string} HTML document.
|
||||
*/
|
||||
function pageTemplate(it) {
|
||||
const { reportColor, reportSummary, date, results } = it;
|
||||
|
||||
return `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>ESLint Report</title>
|
||||
<link rel="icon" type="image/png" sizes="any" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAHaAAAB2gGFomX7AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAABD1JREFUWMPFl11sk2UUx3/nbYtjxS1MF7MLMTECMgSTtSSyrQkLhAj7UBPnDSEGoxegGzMwojhXVpmTAA5iYpSoMQa8GBhFOrMFk03buei6yRAlcmOM0SEmU9d90b19jxcM1o5+sGnsc/e+z/l6ztf/HFFVMnns6QieeOCHBePGsHM+wrOtvLG2C4WRVDSSygNV7sCjlspxwDnPB44aols/DXk+mbMBmx/6OseITF1CuOtfevkPh2Uu+/jbdX8lujSScRlT5r7/QDlAfsRmfzmpnkQ/H3H13gf6bBrBn1uqK8WylgEnU8eZmk1repbfchJG1TyKyIKEwuBHFd3lD3naY3O1siiwXsVoBV2VgM1ht/QQUJk2ByqKghsQziYQ8ifKgexIXmuyzC4r67Y7R+xPAfuB/Nn3Cpva+0s7khpQVtZtd4bt51BWxtBYAiciprG7c7D4SixzU9PYalDL6110Ifb/w8W9eY7JqFeFHbO8fPGyLHwwFHJNJTSgwtVTB9oaw9BlQ+tO93vOxypoaQnfEYlI43SeCHDC4TDq9+51/h5fxr33q0ZfV9g04wat9Q943rjJgCp3952W2i8Bi6eDvdsfKj0cK/DYMRyXL4/sUJUmIHd2zYMezsvLaamp4WpcWN3BXSiHpuMwbGbZlnZ8tXY4rgosy+G7oRwQ0cAsd28YGgqfU5UjCZQDLALxDg+Hv/P5Rqvj4hwrS8izXzWb4spwc1GgENFnkpWRzxeuB+ssUHgLdb9UVdt8vpGdKQpze7n7y1U3DBChNRUuqOo9c+0+qpKKxyZqtAIYla7gY4JszAAQri93BSsMRZoyBcUC+w3Q3AyOA4sNhAOZ0q7Iq0b2vUNvK5zPgP+/H8+Zetdoa6uOikhdGurxebwvJY8Iz3V1rTMNAH+opEuQj5KTT/qA1yC+wyUjBm12OidaUtCcPNNX2h0Hx2JG69VulANZAJZJwfU7rzd/FHixuXniTdM0m4GtSQT7bTartqEh9yfImUEzkwKZmTwmo5a5JwkYBfcDL01/RkR5y8iWhtPBknB8ZxwtU9UjwOrrKCeizzc25nTGg1F/turEHoU9wMLpDvWKf8DTmNCAKnd/tqUTF4ElMXJ+A5rWDJS+41WsGWzALhJ+ErBWrLj9g+pqojHxlXJX8HGUg0BsR/x1yhxf3jm4cSzpQFLp6tmi6PEE7g1ZhtZ91ufpSZUAFa6gC+UoQslNaSmypT1U8mHKiUgEKS8KfgF4EpYunFI16tsHin+OG0LcgQK7yj7g6cSzpva2D3hKVNG0Y3mVO1BkqfSlmJrHBQ4uvM12gJHc6ETW8HZVfMRmXvyxxNC1Z/o839zyXlDuCr4nsC11J+MXueaVJWn6yPv+/pJtc9oLTNN4AeTvNGByd3rlhE2x9s5pLwDoHCy+grDzWmOZ95lUtLYj5Bma126Y8eX0/zj/ADxGyViSg4BXAAAAAElFTkSuQmCC">
|
||||
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PScwIDAgMjk0LjgyNSAyNTguOTgyJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPg0KPHBhdGggZmlsbD0nIzgwODBGMicgZD0nTTk3LjAyMSw5OS4wMTZsNDguNDMyLTI3Ljk2MmMxLjIxMi0wLjcsMi43MDYtMC43LDMuOTE4LDBsNDguNDMzLDI3Ljk2MiBjMS4yMTEsMC43LDEuOTU5LDEuOTkzLDEuOTU5LDMuMzkzdjU1LjkyNGMwLDEuMzk5LTAuNzQ4LDIuNjkzLTEuOTU5LDMuMzk0bC00OC40MzMsMjcuOTYyYy0xLjIxMiwwLjctMi43MDYsMC43LTMuOTE4LDAgbC00OC40MzItMjcuOTYyYy0xLjIxMi0wLjctMS45NTktMS45OTQtMS45NTktMy4zOTR2LTU1LjkyNEM5NS4wNjMsMTAxLjAwOSw5NS44MSw5OS43MTYsOTcuMDIxLDk5LjAxNicvPg0KPHBhdGggZmlsbD0nIzRCMzJDMycgZD0nTTI3My4zMzYsMTI0LjQ4OEwyMTUuNDY5LDIzLjgxNmMtMi4xMDItMy42NC01Ljk4NS02LjMyNS0xMC4xODgtNi4zMjVIODkuNTQ1IGMtNC4yMDQsMC04LjA4OCwyLjY4NS0xMC4xOSw2LjMyNWwtNTcuODY3LDEwMC40NWMtMi4xMDIsMy42NDEtMi4xMDIsOC4yMzYsMCwxMS44NzdsNTcuODY3LDk5Ljg0NyBjMi4xMDIsMy42NCw1Ljk4Niw1LjUwMSwxMC4xOSw1LjUwMWgxMTUuNzM1YzQuMjAzLDAsOC4wODctMS44MDUsMTAuMTg4LTUuNDQ2bDU3Ljg2Ny0xMDAuMDEgQzI3NS40MzksMTMyLjM5NiwyNzUuNDM5LDEyOC4xMjgsMjczLjMzNiwxMjQuNDg4IE0yMjUuNDE5LDE3Mi44OThjMCwxLjQ4LTAuODkxLDIuODQ5LTIuMTc0LDMuNTlsLTczLjcxLDQyLjUyNyBjLTEuMjgyLDAuNzQtMi44ODgsMC43NC00LjE3LDBsLTczLjc2Ny00Mi41MjdjLTEuMjgyLTAuNzQxLTIuMTc5LTIuMTA5LTIuMTc5LTMuNTlWODcuODQzYzAtMS40ODEsMC44ODQtMi44NDksMi4xNjctMy41OSBsNzMuNzA3LTQyLjUyN2MxLjI4Mi0wLjc0MSwyLjg4Ni0wLjc0MSw0LjE2OCwwbDczLjc3Miw0Mi41MjdjMS4yODMsMC43NDEsMi4xODYsMi4xMDksMi4xODYsMy41OVYxNzIuODk4eicvPg0KPC9zdmc+">
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
#overview {
|
||||
padding: 20px 30px;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
table {
|
||||
margin: 30px;
|
||||
width: calc(100% - 60px);
|
||||
max-width: 1000px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ddd;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
th {
|
||||
font-weight: 400;
|
||||
font-size: medium;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
td.clr-1,
|
||||
td.clr-2,
|
||||
th span {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
th span {
|
||||
float: right;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
th span::after {
|
||||
content: "";
|
||||
clear: both;
|
||||
display: block;
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
tr td:first-child,
|
||||
tr td:last-child {
|
||||
color: #9da0a4;
|
||||
}
|
||||
|
||||
#overview.bg-0,
|
||||
tr.bg-0 th {
|
||||
color: #468847;
|
||||
background: #dff0d8;
|
||||
border-bottom: 1px solid #d6e9c6;
|
||||
}
|
||||
|
||||
#overview.bg-1,
|
||||
tr.bg-1 th {
|
||||
color: #f0ad4e;
|
||||
background: #fcf8e3;
|
||||
border-bottom: 1px solid #fbeed5;
|
||||
}
|
||||
|
||||
#overview.bg-2,
|
||||
tr.bg-2 th {
|
||||
color: #b94a48;
|
||||
background: #f2dede;
|
||||
border-bottom: 1px solid #eed3d7;
|
||||
}
|
||||
|
||||
td {
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
td.clr-1 {
|
||||
color: #f0ad4e;
|
||||
}
|
||||
|
||||
td.clr-2 {
|
||||
color: #b94a48;
|
||||
}
|
||||
|
||||
td a {
|
||||
color: #3a33d1;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
td a:hover {
|
||||
color: #272296;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="overview" class="bg-${reportColor}">
|
||||
<h1>ESLint Report</h1>
|
||||
<div>
|
||||
<span>${reportSummary}</span> - Generated on ${date}
|
||||
</div>
|
||||
</div>
|
||||
<table>
|
||||
<tbody>
|
||||
${results}
|
||||
</tbody>
|
||||
</table>
|
||||
<script type="text/javascript">
|
||||
var groups = document.querySelectorAll("tr[data-group]");
|
||||
for (i = 0; i < groups.length; i++) {
|
||||
groups[i].addEventListener("click", function() {
|
||||
var inGroup = document.getElementsByClassName(this.getAttribute("data-group"));
|
||||
this.innerHTML = (this.innerHTML.indexOf("+") > -1) ? this.innerHTML.replace("+", "-") : this.innerHTML.replace("-", "+");
|
||||
for (var j = 0; j < inGroup.length; j++) {
|
||||
inGroup[j].style.display = (inGroup[j].style.display !== "none") ? "none" : "table-row";
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`.trimStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a word and a count, append an s if count is not one.
|
||||
* @param {string} word A word in its singular form.
|
||||
* @param {int} count A number controlling whether word should be pluralized.
|
||||
* @returns {string} The original word with an s on the end if count is not one.
|
||||
*/
|
||||
function pluralize(word, count) {
|
||||
return (count === 1 ? word : `${word}s`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders text along the template of x problems (x errors, x warnings)
|
||||
* @param {string} totalErrors Total errors
|
||||
* @param {string} totalWarnings Total warnings
|
||||
* @returns {string} The formatted string, pluralized where necessary
|
||||
*/
|
||||
function renderSummary(totalErrors, totalWarnings) {
|
||||
const totalProblems = totalErrors + totalWarnings;
|
||||
let renderedText = `${totalProblems} ${pluralize("problem", totalProblems)}`;
|
||||
|
||||
if (totalProblems !== 0) {
|
||||
renderedText += ` (${totalErrors} ${pluralize("error", totalErrors)}, ${totalWarnings} ${pluralize("warning", totalWarnings)})`;
|
||||
}
|
||||
return renderedText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the color based on whether there are errors/warnings...
|
||||
* @param {string} totalErrors Total errors
|
||||
* @param {string} totalWarnings Total warnings
|
||||
* @returns {int} The color code (0 = green, 1 = yellow, 2 = red)
|
||||
*/
|
||||
function renderColor(totalErrors, totalWarnings) {
|
||||
if (totalErrors !== 0) {
|
||||
return 2;
|
||||
}
|
||||
if (totalWarnings !== 0) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get HTML (table row) describing a single message.
|
||||
* @param {Object} it data for the message.
|
||||
* @returns {string} HTML (table row) describing the message.
|
||||
*/
|
||||
function messageTemplate(it) {
|
||||
const {
|
||||
parentIndex,
|
||||
lineNumber,
|
||||
columnNumber,
|
||||
severityNumber,
|
||||
severityName,
|
||||
message,
|
||||
ruleUrl,
|
||||
ruleId
|
||||
} = it;
|
||||
|
||||
return `
|
||||
<tr style="display: none;" class="f-${parentIndex}">
|
||||
<td>${lineNumber}:${columnNumber}</td>
|
||||
<td class="clr-${severityNumber}">${severityName}</td>
|
||||
<td>${encodeHTML(message)}</td>
|
||||
<td>
|
||||
<a href="${ruleUrl ? ruleUrl : ""}" target="_blank" rel="noopener noreferrer">${ruleId ? ruleId : ""}</a>
|
||||
</td>
|
||||
</tr>
|
||||
`.trimStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get HTML (table rows) describing the messages.
|
||||
* @param {Array} messages Messages.
|
||||
* @param {int} parentIndex Index of the parent HTML row.
|
||||
* @param {Object} rulesMeta Dictionary containing metadata for each rule executed by the analysis.
|
||||
* @returns {string} HTML (table rows) describing the messages.
|
||||
*/
|
||||
function renderMessages(messages, parentIndex, rulesMeta) {
|
||||
|
||||
/**
|
||||
* Get HTML (table row) describing a message.
|
||||
* @param {Object} message Message.
|
||||
* @returns {string} HTML (table row) describing a message.
|
||||
*/
|
||||
return messages.map(message => {
|
||||
const lineNumber = message.line || 0;
|
||||
const columnNumber = message.column || 0;
|
||||
let ruleUrl;
|
||||
|
||||
if (rulesMeta) {
|
||||
const meta = rulesMeta[message.ruleId];
|
||||
|
||||
if (meta && meta.docs && meta.docs.url) {
|
||||
ruleUrl = meta.docs.url;
|
||||
}
|
||||
}
|
||||
|
||||
return messageTemplate({
|
||||
parentIndex,
|
||||
lineNumber,
|
||||
columnNumber,
|
||||
severityNumber: message.severity,
|
||||
severityName: message.severity === 1 ? "Warning" : "Error",
|
||||
message: message.message,
|
||||
ruleId: message.ruleId,
|
||||
ruleUrl
|
||||
});
|
||||
}).join("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get HTML (table row) describing the result for a single file.
|
||||
* @param {Object} it data for the file.
|
||||
* @returns {string} HTML (table row) describing the result for the file.
|
||||
*/
|
||||
function resultTemplate(it) {
|
||||
const { color, index, filePath, summary } = it;
|
||||
|
||||
return `
|
||||
<tr class="bg-${color}" data-group="f-${index}">
|
||||
<th colspan="4">
|
||||
[+] ${encodeHTML(filePath)}
|
||||
<span>${encodeHTML(summary)}</span>
|
||||
</th>
|
||||
</tr>
|
||||
`.trimStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the results.
|
||||
* @param {Array} results Test results.
|
||||
* @param {Object} rulesMeta Dictionary containing metadata for each rule executed by the analysis.
|
||||
* @returns {string} HTML string describing the results.
|
||||
*/
|
||||
function renderResults(results, rulesMeta) {
|
||||
return results.map((result, index) => resultTemplate({
|
||||
index,
|
||||
color: renderColor(result.errorCount, result.warningCount),
|
||||
filePath: result.filePath,
|
||||
summary: renderSummary(result.errorCount, result.warningCount)
|
||||
}) + renderMessages(result.messages, index, rulesMeta)).join("\n");
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = function(results, data) {
|
||||
let totalErrors,
|
||||
totalWarnings;
|
||||
|
||||
const metaData = data ? data.rulesMeta : {};
|
||||
|
||||
totalErrors = 0;
|
||||
totalWarnings = 0;
|
||||
|
||||
// Iterate over results to get totals
|
||||
results.forEach(result => {
|
||||
totalErrors += result.errorCount;
|
||||
totalWarnings += result.warningCount;
|
||||
});
|
||||
|
||||
return pageTemplate({
|
||||
date: new Date(),
|
||||
reportColor: renderColor(totalErrors, totalWarnings),
|
||||
reportSummary: renderSummary(totalErrors, totalWarnings),
|
||||
results: renderResults(results, metaData)
|
||||
});
|
||||
};
|
||||
41
node_modules/eslint/lib/cli-engine/formatters/jslint-xml.js
generated
vendored
Normal file
41
node_modules/eslint/lib/cli-engine/formatters/jslint-xml.js
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @fileoverview JSLint XML reporter
|
||||
* @author Ian Christian Myers
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const xmlEscape = require("../xml-escape");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = function(results) {
|
||||
|
||||
let output = "";
|
||||
|
||||
output += "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
|
||||
output += "<jslint>";
|
||||
|
||||
results.forEach(result => {
|
||||
const messages = result.messages;
|
||||
|
||||
output += `<file name="${result.filePath}">`;
|
||||
|
||||
messages.forEach(message => {
|
||||
output += [
|
||||
`<issue line="${message.line}"`,
|
||||
`char="${message.column}"`,
|
||||
`evidence="${xmlEscape(message.source || "")}"`,
|
||||
`reason="${xmlEscape(message.message || "")}${message.ruleId ? ` (${message.ruleId})` : ""}" />`
|
||||
].join(" ");
|
||||
});
|
||||
|
||||
output += "</file>";
|
||||
|
||||
});
|
||||
|
||||
output += "</jslint>";
|
||||
|
||||
return output;
|
||||
};
|
||||
16
node_modules/eslint/lib/cli-engine/formatters/json-with-metadata.js
generated
vendored
Normal file
16
node_modules/eslint/lib/cli-engine/formatters/json-with-metadata.js
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @fileoverview JSON reporter, including rules metadata
|
||||
* @author Chris Meyer
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = function(results, data) {
|
||||
return JSON.stringify({
|
||||
results,
|
||||
metadata: data
|
||||
});
|
||||
};
|
||||
13
node_modules/eslint/lib/cli-engine/formatters/json.js
generated
vendored
Normal file
13
node_modules/eslint/lib/cli-engine/formatters/json.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @fileoverview JSON reporter
|
||||
* @author Burak Yigit Kaya aka BYK
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = function(results) {
|
||||
return JSON.stringify(results);
|
||||
};
|
||||
82
node_modules/eslint/lib/cli-engine/formatters/junit.js
generated
vendored
Normal file
82
node_modules/eslint/lib/cli-engine/formatters/junit.js
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* @fileoverview jUnit Reporter
|
||||
* @author Jamund Ferguson
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const xmlEscape = require("../xml-escape");
|
||||
const path = require("path");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helper Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the severity of warning or error
|
||||
* @param {Object} message message object to examine
|
||||
* @returns {string} severity level
|
||||
* @private
|
||||
*/
|
||||
function getMessageType(message) {
|
||||
if (message.fatal || message.severity === 2) {
|
||||
return "Error";
|
||||
}
|
||||
return "Warning";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a full file path without extension
|
||||
* @param {string} filePath input file path
|
||||
* @returns {string} file path without extension
|
||||
* @private
|
||||
*/
|
||||
function pathWithoutExt(filePath) {
|
||||
return path.join(path.dirname(filePath), path.basename(filePath, path.extname(filePath)));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = function(results) {
|
||||
|
||||
let output = "";
|
||||
|
||||
output += "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
|
||||
output += "<testsuites>\n";
|
||||
|
||||
results.forEach(result => {
|
||||
|
||||
const messages = result.messages;
|
||||
const classname = pathWithoutExt(result.filePath);
|
||||
|
||||
if (messages.length > 0) {
|
||||
output += `<testsuite package="org.eslint" time="0" tests="${messages.length}" errors="${messages.length}" name="${result.filePath}">\n`;
|
||||
messages.forEach(message => {
|
||||
const type = message.fatal ? "error" : "failure";
|
||||
|
||||
output += `<testcase time="0" name="org.eslint.${message.ruleId || "unknown"}" classname="${classname}">`;
|
||||
output += `<${type} message="${xmlEscape(message.message || "")}">`;
|
||||
output += "<![CDATA[";
|
||||
output += `line ${message.line || 0}, col `;
|
||||
output += `${message.column || 0}, ${getMessageType(message)}`;
|
||||
output += ` - ${xmlEscape(message.message || "")}`;
|
||||
output += (message.ruleId ? ` (${message.ruleId})` : "");
|
||||
output += "]]>";
|
||||
output += `</${type}>`;
|
||||
output += "</testcase>\n";
|
||||
});
|
||||
output += "</testsuite>\n";
|
||||
} else {
|
||||
output += `<testsuite package="org.eslint" time="0" tests="1" errors="0" name="${result.filePath}">\n`;
|
||||
output += `<testcase time="0" name="${result.filePath}" classname="${classname}" />\n`;
|
||||
output += "</testsuite>\n";
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
output += "</testsuites>\n";
|
||||
|
||||
return output;
|
||||
};
|
||||
101
node_modules/eslint/lib/cli-engine/formatters/stylish.js
generated
vendored
Normal file
101
node_modules/eslint/lib/cli-engine/formatters/stylish.js
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* @fileoverview Stylish reporter
|
||||
* @author Sindre Sorhus
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const chalk = require("chalk"),
|
||||
stripAnsi = require("strip-ansi"),
|
||||
table = require("text-table");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Given a word and a count, append an s if count is not one.
|
||||
* @param {string} word A word in its singular form.
|
||||
* @param {int} count A number controlling whether word should be pluralized.
|
||||
* @returns {string} The original word with an s on the end if count is not one.
|
||||
*/
|
||||
function pluralize(word, count) {
|
||||
return (count === 1 ? word : `${word}s`);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = function(results) {
|
||||
|
||||
let output = "\n",
|
||||
errorCount = 0,
|
||||
warningCount = 0,
|
||||
fixableErrorCount = 0,
|
||||
fixableWarningCount = 0,
|
||||
summaryColor = "yellow";
|
||||
|
||||
results.forEach(result => {
|
||||
const messages = result.messages;
|
||||
|
||||
if (messages.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
errorCount += result.errorCount;
|
||||
warningCount += result.warningCount;
|
||||
fixableErrorCount += result.fixableErrorCount;
|
||||
fixableWarningCount += result.fixableWarningCount;
|
||||
|
||||
output += `${chalk.underline(result.filePath)}\n`;
|
||||
|
||||
output += `${table(
|
||||
messages.map(message => {
|
||||
let messageType;
|
||||
|
||||
if (message.fatal || message.severity === 2) {
|
||||
messageType = chalk.red("error");
|
||||
summaryColor = "red";
|
||||
} else {
|
||||
messageType = chalk.yellow("warning");
|
||||
}
|
||||
|
||||
return [
|
||||
"",
|
||||
message.line || 0,
|
||||
message.column || 0,
|
||||
messageType,
|
||||
message.message.replace(/([^ ])\.$/u, "$1"),
|
||||
chalk.dim(message.ruleId || "")
|
||||
];
|
||||
}),
|
||||
{
|
||||
align: ["", "r", "l"],
|
||||
stringLength(str) {
|
||||
return stripAnsi(str).length;
|
||||
}
|
||||
}
|
||||
).split("\n").map(el => el.replace(/(\d+)\s+(\d+)/u, (m, p1, p2) => chalk.dim(`${p1}:${p2}`))).join("\n")}\n\n`;
|
||||
});
|
||||
|
||||
const total = errorCount + warningCount;
|
||||
|
||||
if (total > 0) {
|
||||
output += chalk[summaryColor].bold([
|
||||
"\u2716 ", total, pluralize(" problem", total),
|
||||
" (", errorCount, pluralize(" error", errorCount), ", ",
|
||||
warningCount, pluralize(" warning", warningCount), ")\n"
|
||||
].join(""));
|
||||
|
||||
if (fixableErrorCount > 0 || fixableWarningCount > 0) {
|
||||
output += chalk[summaryColor].bold([
|
||||
" ", fixableErrorCount, pluralize(" error", fixableErrorCount), " and ",
|
||||
fixableWarningCount, pluralize(" warning", fixableWarningCount),
|
||||
" potentially fixable with the `--fix` option.\n"
|
||||
].join(""));
|
||||
}
|
||||
}
|
||||
|
||||
// Resets output color, for prevent change on top level
|
||||
return total > 0 ? chalk.reset(output) : "";
|
||||
};
|
||||
95
node_modules/eslint/lib/cli-engine/formatters/tap.js
generated
vendored
Normal file
95
node_modules/eslint/lib/cli-engine/formatters/tap.js
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* @fileoverview TAP reporter
|
||||
* @author Jonathan Kingston
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const yaml = require("js-yaml");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helper Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a canonical error level string based upon the error message passed in.
|
||||
* @param {Object} message Individual error message provided by eslint
|
||||
* @returns {string} Error level string
|
||||
*/
|
||||
function getMessageType(message) {
|
||||
if (message.fatal || message.severity === 2) {
|
||||
return "error";
|
||||
}
|
||||
return "warning";
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes in a JavaScript object and outputs a TAP diagnostics string
|
||||
* @param {Object} diagnostic JavaScript object to be embedded as YAML into output.
|
||||
* @returns {string} diagnostics string with YAML embedded - TAP version 13 compliant
|
||||
*/
|
||||
function outputDiagnostics(diagnostic) {
|
||||
const prefix = " ";
|
||||
let output = `${prefix}---\n`;
|
||||
|
||||
output += prefix + yaml.dump(diagnostic).split("\n").join(`\n${prefix}`);
|
||||
output += "...\n";
|
||||
return output;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = function(results) {
|
||||
let output = `TAP version 13\n1..${results.length}\n`;
|
||||
|
||||
results.forEach((result, id) => {
|
||||
const messages = result.messages;
|
||||
let testResult = "ok";
|
||||
let diagnostics = {};
|
||||
|
||||
if (messages.length > 0) {
|
||||
messages.forEach(message => {
|
||||
const severity = getMessageType(message);
|
||||
const diagnostic = {
|
||||
message: message.message,
|
||||
severity,
|
||||
data: {
|
||||
line: message.line || 0,
|
||||
column: message.column || 0,
|
||||
ruleId: message.ruleId || ""
|
||||
}
|
||||
};
|
||||
|
||||
// This ensures a warning message is not flagged as error
|
||||
if (severity === "error") {
|
||||
testResult = "not ok";
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have multiple messages place them under a messages key
|
||||
* The first error will be logged as message key
|
||||
* This is to adhere to TAP 13 loosely defined specification of having a message key
|
||||
*/
|
||||
if ("message" in diagnostics) {
|
||||
if (typeof diagnostics.messages === "undefined") {
|
||||
diagnostics.messages = [];
|
||||
}
|
||||
diagnostics.messages.push(diagnostic);
|
||||
} else {
|
||||
diagnostics = diagnostic;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
output += `${testResult} ${id + 1} - ${result.filePath}\n`;
|
||||
|
||||
// If we have an error include diagnostics
|
||||
if (messages.length > 0) {
|
||||
output += outputDiagnostics(diagnostics);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return output;
|
||||
};
|
||||
58
node_modules/eslint/lib/cli-engine/formatters/unix.js
generated
vendored
Normal file
58
node_modules/eslint/lib/cli-engine/formatters/unix.js
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* @fileoverview unix-style formatter.
|
||||
* @author oshi-shinobu
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helper Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a canonical error level string based upon the error message passed in.
|
||||
* @param {Object} message Individual error message provided by eslint
|
||||
* @returns {string} Error level string
|
||||
*/
|
||||
function getMessageType(message) {
|
||||
if (message.fatal || message.severity === 2) {
|
||||
return "Error";
|
||||
}
|
||||
return "Warning";
|
||||
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = function(results) {
|
||||
|
||||
let output = "",
|
||||
total = 0;
|
||||
|
||||
results.forEach(result => {
|
||||
|
||||
const messages = result.messages;
|
||||
|
||||
total += messages.length;
|
||||
|
||||
messages.forEach(message => {
|
||||
|
||||
output += `${result.filePath}:`;
|
||||
output += `${message.line || 0}:`;
|
||||
output += `${message.column || 0}:`;
|
||||
output += ` ${message.message} `;
|
||||
output += `[${getMessageType(message)}${message.ruleId ? `/${message.ruleId}` : ""}]`;
|
||||
output += "\n";
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
if (total > 0) {
|
||||
output += `\n${total} problem${total !== 1 ? "s" : ""}`;
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
||||
63
node_modules/eslint/lib/cli-engine/formatters/visualstudio.js
generated
vendored
Normal file
63
node_modules/eslint/lib/cli-engine/formatters/visualstudio.js
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @fileoverview Visual Studio compatible formatter
|
||||
* @author Ronald Pijnacker
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helper Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the severity of warning or error
|
||||
* @param {Object} message message object to examine
|
||||
* @returns {string} severity level
|
||||
* @private
|
||||
*/
|
||||
function getMessageType(message) {
|
||||
if (message.fatal || message.severity === 2) {
|
||||
return "error";
|
||||
}
|
||||
return "warning";
|
||||
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = function(results) {
|
||||
|
||||
let output = "",
|
||||
total = 0;
|
||||
|
||||
results.forEach(result => {
|
||||
|
||||
const messages = result.messages;
|
||||
|
||||
total += messages.length;
|
||||
|
||||
messages.forEach(message => {
|
||||
|
||||
output += result.filePath;
|
||||
output += `(${message.line || 0}`;
|
||||
output += message.column ? `,${message.column}` : "";
|
||||
output += `): ${getMessageType(message)}`;
|
||||
output += message.ruleId ? ` ${message.ruleId}` : "";
|
||||
output += ` : ${message.message}`;
|
||||
output += "\n";
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
if (total === 0) {
|
||||
output += "no problems";
|
||||
} else {
|
||||
output += `\n${total} problem${total !== 1 ? "s" : ""}`;
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
||||
35
node_modules/eslint/lib/cli-engine/hash.js
generated
vendored
Normal file
35
node_modules/eslint/lib/cli-engine/hash.js
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* @fileoverview Defining the hashing function in one place.
|
||||
* @author Michael Ficarra
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const murmur = require("imurmurhash");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* hash the given string
|
||||
* @param {string} str the string to hash
|
||||
* @returns {string} the hash
|
||||
*/
|
||||
function hash(str) {
|
||||
return murmur(str).result().toString(36);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = hash;
|
||||
7
node_modules/eslint/lib/cli-engine/index.js
generated
vendored
Normal file
7
node_modules/eslint/lib/cli-engine/index.js
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
const { CLIEngine } = require("./cli-engine");
|
||||
|
||||
module.exports = {
|
||||
CLIEngine
|
||||
};
|
||||
203
node_modules/eslint/lib/cli-engine/lint-result-cache.js
generated
vendored
Normal file
203
node_modules/eslint/lib/cli-engine/lint-result-cache.js
generated
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
/**
|
||||
* @fileoverview Utility for caching lint results.
|
||||
* @author Kevin Partington
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const assert = require("assert");
|
||||
const fs = require("fs");
|
||||
const fileEntryCache = require("file-entry-cache");
|
||||
const stringify = require("json-stable-stringify-without-jsonify");
|
||||
const pkg = require("../../package.json");
|
||||
const hash = require("./hash");
|
||||
|
||||
const debug = require("debug")("eslint:lint-result-cache");
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const configHashCache = new WeakMap();
|
||||
const nodeVersion = process && process.version;
|
||||
|
||||
const validCacheStrategies = ["metadata", "content"];
|
||||
const invalidCacheStrategyErrorMessage = `Cache strategy must be one of: ${validCacheStrategies
|
||||
.map(strategy => `"${strategy}"`)
|
||||
.join(", ")}`;
|
||||
|
||||
/**
|
||||
* Tests whether a provided cacheStrategy is valid
|
||||
* @param {string} cacheStrategy The cache strategy to use
|
||||
* @returns {boolean} true if `cacheStrategy` is one of `validCacheStrategies`; false otherwise
|
||||
*/
|
||||
function isValidCacheStrategy(cacheStrategy) {
|
||||
return (
|
||||
validCacheStrategies.includes(cacheStrategy)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the hash of the config
|
||||
* @param {ConfigArray} config The config.
|
||||
* @returns {string} The hash of the config
|
||||
*/
|
||||
function hashOfConfigFor(config) {
|
||||
if (!configHashCache.has(config)) {
|
||||
configHashCache.set(config, hash(`${pkg.version}_${nodeVersion}_${stringify(config)}`));
|
||||
}
|
||||
|
||||
return configHashCache.get(config);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Lint result cache. This wraps around the file-entry-cache module,
|
||||
* transparently removing properties that are difficult or expensive to
|
||||
* serialize and adding them back in on retrieval.
|
||||
*/
|
||||
class LintResultCache {
|
||||
|
||||
/**
|
||||
* Creates a new LintResultCache instance.
|
||||
* @param {string} cacheFileLocation The cache file location.
|
||||
* @param {"metadata" | "content"} cacheStrategy The cache strategy to use.
|
||||
*/
|
||||
constructor(cacheFileLocation, cacheStrategy) {
|
||||
assert(cacheFileLocation, "Cache file location is required");
|
||||
assert(cacheStrategy, "Cache strategy is required");
|
||||
assert(
|
||||
isValidCacheStrategy(cacheStrategy),
|
||||
invalidCacheStrategyErrorMessage
|
||||
);
|
||||
|
||||
debug(`Caching results to ${cacheFileLocation}`);
|
||||
|
||||
const useChecksum = cacheStrategy === "content";
|
||||
|
||||
debug(
|
||||
`Using "${cacheStrategy}" strategy to detect changes`
|
||||
);
|
||||
|
||||
this.fileEntryCache = fileEntryCache.create(
|
||||
cacheFileLocation,
|
||||
void 0,
|
||||
useChecksum
|
||||
);
|
||||
this.cacheFileLocation = cacheFileLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve cached lint results for a given file path, if present in the
|
||||
* cache. If the file is present and has not been changed, rebuild any
|
||||
* missing result information.
|
||||
* @param {string} filePath The file for which to retrieve lint results.
|
||||
* @param {ConfigArray} config The config of the file.
|
||||
* @returns {Object|null} The rebuilt lint results, or null if the file is
|
||||
* changed or not in the filesystem.
|
||||
*/
|
||||
getCachedLintResults(filePath, config) {
|
||||
|
||||
/*
|
||||
* Cached lint results are valid if and only if:
|
||||
* 1. The file is present in the filesystem
|
||||
* 2. The file has not changed since the time it was previously linted
|
||||
* 3. The ESLint configuration has not changed since the time the file
|
||||
* was previously linted
|
||||
* If any of these are not true, we will not reuse the lint results.
|
||||
*/
|
||||
const fileDescriptor = this.fileEntryCache.getFileDescriptor(filePath);
|
||||
const hashOfConfig = hashOfConfigFor(config);
|
||||
const changed =
|
||||
fileDescriptor.changed ||
|
||||
fileDescriptor.meta.hashOfConfig !== hashOfConfig;
|
||||
|
||||
if (fileDescriptor.notFound) {
|
||||
debug(`File not found on the file system: ${filePath}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
debug(`Cache entry not found or no longer valid: ${filePath}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const cachedResults = fileDescriptor.meta.results;
|
||||
|
||||
// Just in case, not sure if this can ever happen.
|
||||
if (!cachedResults) {
|
||||
return cachedResults;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shallow clone the object to ensure that any properties added or modified afterwards
|
||||
* will not be accidentally stored in the cache file when `reconcile()` is called.
|
||||
* https://github.com/eslint/eslint/issues/13507
|
||||
* All intentional changes to the cache file must be done through `setCachedLintResults()`.
|
||||
*/
|
||||
const results = { ...cachedResults };
|
||||
|
||||
// If source is present but null, need to reread the file from the filesystem.
|
||||
if (results.source === null) {
|
||||
debug(`Rereading cached result source from filesystem: ${filePath}`);
|
||||
results.source = fs.readFileSync(filePath, "utf-8");
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cached lint results for a given file path, after removing any
|
||||
* information that will be both unnecessary and difficult to serialize.
|
||||
* Avoids caching results with an "output" property (meaning fixes were
|
||||
* applied), to prevent potentially incorrect results if fixes are not
|
||||
* written to disk.
|
||||
* @param {string} filePath The file for which to set lint results.
|
||||
* @param {ConfigArray} config The config of the file.
|
||||
* @param {Object} result The lint result to be set for the file.
|
||||
* @returns {void}
|
||||
*/
|
||||
setCachedLintResults(filePath, config, result) {
|
||||
if (result && Object.prototype.hasOwnProperty.call(result, "output")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fileDescriptor = this.fileEntryCache.getFileDescriptor(filePath);
|
||||
|
||||
if (fileDescriptor && !fileDescriptor.notFound) {
|
||||
debug(`Updating cached result: ${filePath}`);
|
||||
|
||||
// Serialize the result, except that we want to remove the file source if present.
|
||||
const resultToSerialize = Object.assign({}, result);
|
||||
|
||||
/*
|
||||
* Set result.source to null.
|
||||
* In `getCachedLintResults`, if source is explicitly null, we will
|
||||
* read the file from the filesystem to set the value again.
|
||||
*/
|
||||
if (Object.prototype.hasOwnProperty.call(resultToSerialize, "source")) {
|
||||
resultToSerialize.source = null;
|
||||
}
|
||||
|
||||
fileDescriptor.meta.results = resultToSerialize;
|
||||
fileDescriptor.meta.hashOfConfig = hashOfConfigFor(config);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists the in-memory cache to disk.
|
||||
* @returns {void}
|
||||
*/
|
||||
reconcile() {
|
||||
debug(`Persisting cached results: ${this.cacheFileLocation}`);
|
||||
this.fileEntryCache.reconcile();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LintResultCache;
|
||||
46
node_modules/eslint/lib/cli-engine/load-rules.js
generated
vendored
Normal file
46
node_modules/eslint/lib/cli-engine/load-rules.js
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* @fileoverview Module for loading rules from files and directories.
|
||||
* @author Michael Ficarra
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const fs = require("fs"),
|
||||
path = require("path");
|
||||
|
||||
const rulesDirCache = {};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Load all rule modules from specified directory.
|
||||
* @param {string} relativeRulesDir Path to rules directory, may be relative.
|
||||
* @param {string} cwd Current working directory
|
||||
* @returns {Object} Loaded rule modules.
|
||||
*/
|
||||
module.exports = function(relativeRulesDir, cwd) {
|
||||
const rulesDir = path.resolve(cwd, relativeRulesDir);
|
||||
|
||||
// cache will help performance as IO operation are expensive
|
||||
if (rulesDirCache[rulesDir]) {
|
||||
return rulesDirCache[rulesDir];
|
||||
}
|
||||
|
||||
const rules = Object.create(null);
|
||||
|
||||
fs.readdirSync(rulesDir).forEach(file => {
|
||||
if (path.extname(file) !== ".js") {
|
||||
return;
|
||||
}
|
||||
rules[file.slice(0, -3)] = require(path.join(rulesDir, file));
|
||||
});
|
||||
rulesDirCache[rulesDir] = rules;
|
||||
|
||||
return rules;
|
||||
};
|
||||
34
node_modules/eslint/lib/cli-engine/xml-escape.js
generated
vendored
Normal file
34
node_modules/eslint/lib/cli-engine/xml-escape.js
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @fileoverview XML character escaper
|
||||
* @author George Chung
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the escaped value for a character
|
||||
* @param {string} s string to examine
|
||||
* @returns {string} severity level
|
||||
* @private
|
||||
*/
|
||||
module.exports = function(s) {
|
||||
return (`${s}`).replace(/[<>&"'\x00-\x1F\x7F\u0080-\uFFFF]/gu, c => { // eslint-disable-line no-control-regex -- Converting controls to entities
|
||||
switch (c) {
|
||||
case "<":
|
||||
return "<";
|
||||
case ">":
|
||||
return ">";
|
||||
case "&":
|
||||
return "&";
|
||||
case "\"":
|
||||
return """;
|
||||
case "'":
|
||||
return "'";
|
||||
default:
|
||||
return `&#${c.charCodeAt(0)};`;
|
||||
}
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user