.
This commit is contained in:
54
qwen/nodejs/node_modules/eslint/lib/api.js
generated
vendored
Normal file
54
qwen/nodejs/node_modules/eslint/lib/api.js
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* @fileoverview Expose out ESLint and CLI to require.
|
||||
* @author Ian Christian Myers
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const { ESLint, FlatESLint } = require("./eslint");
|
||||
const { shouldUseFlatConfig } = require("./eslint/flat-eslint");
|
||||
const { Linter } = require("./linter");
|
||||
const { RuleTester } = require("./rule-tester");
|
||||
const { SourceCode } = require("./source-code");
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Loads the correct ESLint constructor given the options.
|
||||
* @param {Object} [options] The options object
|
||||
* @param {boolean} [options.useFlatConfig] Whether or not to use a flat config
|
||||
* @param {string} [options.cwd] The current working directory
|
||||
* @returns {Promise<ESLint|LegacyESLint>} The ESLint constructor
|
||||
*/
|
||||
async function loadESLint({ useFlatConfig, cwd = process.cwd() } = {}) {
|
||||
|
||||
/*
|
||||
* Note: The v9.x version of this function doesn't have a cwd option
|
||||
* because it's not used. It's only used in the v8.x version of this
|
||||
* function.
|
||||
*/
|
||||
|
||||
const shouldESLintUseFlatConfig = typeof useFlatConfig === "boolean"
|
||||
? useFlatConfig
|
||||
: await shouldUseFlatConfig({ cwd });
|
||||
|
||||
return shouldESLintUseFlatConfig ? FlatESLint : ESLint;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Exports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
module.exports = {
|
||||
Linter,
|
||||
loadESLint,
|
||||
ESLint,
|
||||
RuleTester,
|
||||
SourceCode
|
||||
};
|
||||
1078
qwen/nodejs/node_modules/eslint/lib/cli-engine/cli-engine.js
generated
vendored
Normal file
1078
qwen/nodejs/node_modules/eslint/lib/cli-engine/cli-engine.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
547
qwen/nodejs/node_modules/eslint/lib/cli-engine/file-enumerator.js
generated
vendored
Normal file
547
qwen/nodejs/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
qwen/nodejs/node_modules/eslint/lib/cli-engine/formatters/checkstyle.js
generated
vendored
Normal file
60
qwen/nodejs/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
qwen/nodejs/node_modules/eslint/lib/cli-engine/formatters/compact.js
generated
vendored
Normal file
60
qwen/nodejs/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
qwen/nodejs/node_modules/eslint/lib/cli-engine/formatters/formatters-meta.json
generated
vendored
Normal file
46
qwen/nodejs/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
qwen/nodejs/node_modules/eslint/lib/cli-engine/formatters/html.js
generated
vendored
Normal file
351
qwen/nodejs/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
qwen/nodejs/node_modules/eslint/lib/cli-engine/formatters/jslint-xml.js
generated
vendored
Normal file
41
qwen/nodejs/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
qwen/nodejs/node_modules/eslint/lib/cli-engine/formatters/json-with-metadata.js
generated
vendored
Normal file
16
qwen/nodejs/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
qwen/nodejs/node_modules/eslint/lib/cli-engine/formatters/json.js
generated
vendored
Normal file
13
qwen/nodejs/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
qwen/nodejs/node_modules/eslint/lib/cli-engine/formatters/junit.js
generated
vendored
Normal file
82
qwen/nodejs/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
qwen/nodejs/node_modules/eslint/lib/cli-engine/formatters/stylish.js
generated
vendored
Normal file
101
qwen/nodejs/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
qwen/nodejs/node_modules/eslint/lib/cli-engine/formatters/tap.js
generated
vendored
Normal file
95
qwen/nodejs/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
qwen/nodejs/node_modules/eslint/lib/cli-engine/formatters/unix.js
generated
vendored
Normal file
58
qwen/nodejs/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
qwen/nodejs/node_modules/eslint/lib/cli-engine/formatters/visualstudio.js
generated
vendored
Normal file
63
qwen/nodejs/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
qwen/nodejs/node_modules/eslint/lib/cli-engine/hash.js
generated
vendored
Normal file
35
qwen/nodejs/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
qwen/nodejs/node_modules/eslint/lib/cli-engine/index.js
generated
vendored
Normal file
7
qwen/nodejs/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
qwen/nodejs/node_modules/eslint/lib/cli-engine/lint-result-cache.js
generated
vendored
Normal file
203
qwen/nodejs/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
qwen/nodejs/node_modules/eslint/lib/cli-engine/load-rules.js
generated
vendored
Normal file
46
qwen/nodejs/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
qwen/nodejs/node_modules/eslint/lib/cli-engine/xml-escape.js
generated
vendored
Normal file
34
qwen/nodejs/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)};`;
|
||||
}
|
||||
});
|
||||
};
|
||||
471
qwen/nodejs/node_modules/eslint/lib/cli.js
generated
vendored
Normal file
471
qwen/nodejs/node_modules/eslint/lib/cli.js
generated
vendored
Normal file
@@ -0,0 +1,471 @@
|
||||
/**
|
||||
* @fileoverview Main CLI object.
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
* NOTE: 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"),
|
||||
path = require("path"),
|
||||
{ promisify } = require("util"),
|
||||
{ ESLint } = require("./eslint"),
|
||||
{ FlatESLint, shouldUseFlatConfig } = require("./eslint/flat-eslint"),
|
||||
createCLIOptions = require("./options"),
|
||||
log = require("./shared/logging"),
|
||||
RuntimeInfo = require("./shared/runtime-info"),
|
||||
{ normalizeSeverityToString } = require("./shared/severity");
|
||||
const { Legacy: { naming } } = require("@eslint/eslintrc");
|
||||
const { ModuleImporter } = require("@humanwhocodes/module-importer");
|
||||
|
||||
const debug = require("debug")("eslint:cli");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Types
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @typedef {import("./eslint/eslint").ESLintOptions} ESLintOptions */
|
||||
/** @typedef {import("./eslint/eslint").LintMessage} LintMessage */
|
||||
/** @typedef {import("./eslint/eslint").LintResult} LintResult */
|
||||
/** @typedef {import("./options").ParsedCLIOptions} ParsedCLIOptions */
|
||||
/** @typedef {import("./shared/types").ResultsMeta} ResultsMeta */
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const mkdir = promisify(fs.mkdir);
|
||||
const stat = promisify(fs.stat);
|
||||
const writeFile = promisify(fs.writeFile);
|
||||
|
||||
/**
|
||||
* Predicate function for whether or not to apply fixes in quiet mode.
|
||||
* If a message is a warning, do not apply a fix.
|
||||
* @param {LintMessage} message The lint result.
|
||||
* @returns {boolean} True if the lint message is an error (and thus should be
|
||||
* autofixed), false otherwise.
|
||||
*/
|
||||
function quietFixPredicate(message) {
|
||||
return message.severity === 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the CLI options into the options expected by the ESLint constructor.
|
||||
* @param {ParsedCLIOptions} cliOptions The CLI options to translate.
|
||||
* @param {"flat"|"eslintrc"} [configType="eslintrc"] The format of the
|
||||
* config to generate.
|
||||
* @returns {Promise<ESLintOptions>} The options object for the ESLint constructor.
|
||||
* @private
|
||||
*/
|
||||
async function translateOptions({
|
||||
cache,
|
||||
cacheFile,
|
||||
cacheLocation,
|
||||
cacheStrategy,
|
||||
config,
|
||||
configLookup,
|
||||
env,
|
||||
errorOnUnmatchedPattern,
|
||||
eslintrc,
|
||||
ext,
|
||||
fix,
|
||||
fixDryRun,
|
||||
fixType,
|
||||
global,
|
||||
ignore,
|
||||
ignorePath,
|
||||
ignorePattern,
|
||||
inlineConfig,
|
||||
parser,
|
||||
parserOptions,
|
||||
plugin,
|
||||
quiet,
|
||||
reportUnusedDisableDirectives,
|
||||
reportUnusedDisableDirectivesSeverity,
|
||||
resolvePluginsRelativeTo,
|
||||
rule,
|
||||
rulesdir,
|
||||
warnIgnored
|
||||
}, configType) {
|
||||
|
||||
let overrideConfig, overrideConfigFile;
|
||||
const importer = new ModuleImporter();
|
||||
|
||||
if (configType === "flat") {
|
||||
overrideConfigFile = (typeof config === "string") ? config : !configLookup;
|
||||
if (overrideConfigFile === false) {
|
||||
overrideConfigFile = void 0;
|
||||
}
|
||||
|
||||
let globals = {};
|
||||
|
||||
if (global) {
|
||||
globals = global.reduce((obj, name) => {
|
||||
if (name.endsWith(":true")) {
|
||||
obj[name.slice(0, -5)] = "writable";
|
||||
} else {
|
||||
obj[name] = "readonly";
|
||||
}
|
||||
return obj;
|
||||
}, globals);
|
||||
}
|
||||
|
||||
overrideConfig = [{
|
||||
languageOptions: {
|
||||
globals,
|
||||
parserOptions: parserOptions || {}
|
||||
},
|
||||
rules: rule ? rule : {}
|
||||
}];
|
||||
|
||||
if (reportUnusedDisableDirectives || reportUnusedDisableDirectivesSeverity !== void 0) {
|
||||
overrideConfig[0].linterOptions = {
|
||||
reportUnusedDisableDirectives: reportUnusedDisableDirectives
|
||||
? "error"
|
||||
: normalizeSeverityToString(reportUnusedDisableDirectivesSeverity)
|
||||
};
|
||||
}
|
||||
|
||||
if (parser) {
|
||||
overrideConfig[0].languageOptions.parser = await importer.import(parser);
|
||||
}
|
||||
|
||||
if (plugin) {
|
||||
const plugins = {};
|
||||
|
||||
for (const pluginName of plugin) {
|
||||
|
||||
const shortName = naming.getShorthandName(pluginName, "eslint-plugin");
|
||||
const longName = naming.normalizePackageName(pluginName, "eslint-plugin");
|
||||
|
||||
plugins[shortName] = await importer.import(longName);
|
||||
}
|
||||
|
||||
overrideConfig[0].plugins = plugins;
|
||||
}
|
||||
|
||||
} else {
|
||||
overrideConfigFile = config;
|
||||
|
||||
overrideConfig = {
|
||||
env: env && env.reduce((obj, name) => {
|
||||
obj[name] = true;
|
||||
return obj;
|
||||
}, {}),
|
||||
globals: global && global.reduce((obj, name) => {
|
||||
if (name.endsWith(":true")) {
|
||||
obj[name.slice(0, -5)] = "writable";
|
||||
} else {
|
||||
obj[name] = "readonly";
|
||||
}
|
||||
return obj;
|
||||
}, {}),
|
||||
ignorePatterns: ignorePattern,
|
||||
parser,
|
||||
parserOptions,
|
||||
plugins: plugin,
|
||||
rules: rule
|
||||
};
|
||||
}
|
||||
|
||||
const options = {
|
||||
allowInlineConfig: inlineConfig,
|
||||
cache,
|
||||
cacheLocation: cacheLocation || cacheFile,
|
||||
cacheStrategy,
|
||||
errorOnUnmatchedPattern,
|
||||
fix: (fix || fixDryRun) && (quiet ? quietFixPredicate : true),
|
||||
fixTypes: fixType,
|
||||
ignore,
|
||||
overrideConfig,
|
||||
overrideConfigFile
|
||||
};
|
||||
|
||||
if (configType === "flat") {
|
||||
options.ignorePatterns = ignorePattern;
|
||||
options.warnIgnored = warnIgnored;
|
||||
} else {
|
||||
options.resolvePluginsRelativeTo = resolvePluginsRelativeTo;
|
||||
options.rulePaths = rulesdir;
|
||||
options.useEslintrc = eslintrc;
|
||||
options.extensions = ext;
|
||||
options.ignorePath = ignorePath;
|
||||
if (reportUnusedDisableDirectives || reportUnusedDisableDirectivesSeverity !== void 0) {
|
||||
options.reportUnusedDisableDirectives = reportUnusedDisableDirectives
|
||||
? "error"
|
||||
: normalizeSeverityToString(reportUnusedDisableDirectivesSeverity);
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count error messages.
|
||||
* @param {LintResult[]} results The lint results.
|
||||
* @returns {{errorCount:number;fatalErrorCount:number,warningCount:number}} The number of error messages.
|
||||
*/
|
||||
function countErrors(results) {
|
||||
let errorCount = 0;
|
||||
let fatalErrorCount = 0;
|
||||
let warningCount = 0;
|
||||
|
||||
for (const result of results) {
|
||||
errorCount += result.errorCount;
|
||||
fatalErrorCount += result.fatalErrorCount;
|
||||
warningCount += result.warningCount;
|
||||
}
|
||||
|
||||
return { errorCount, fatalErrorCount, warningCount };
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given file path is a directory or not.
|
||||
* @param {string} filePath The path to a file to check.
|
||||
* @returns {Promise<boolean>} `true` if the given path is a directory.
|
||||
*/
|
||||
async function isDirectory(filePath) {
|
||||
try {
|
||||
return (await stat(filePath)).isDirectory();
|
||||
} catch (error) {
|
||||
if (error.code === "ENOENT" || error.code === "ENOTDIR") {
|
||||
return false;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the results of the linting.
|
||||
* @param {ESLint} engine The ESLint instance to use.
|
||||
* @param {LintResult[]} results The results to print.
|
||||
* @param {string} format The name of the formatter to use or the path to the formatter.
|
||||
* @param {string} outputFile The path for the output file.
|
||||
* @param {ResultsMeta} resultsMeta Warning count and max threshold.
|
||||
* @returns {Promise<boolean>} True if the printing succeeds, false if not.
|
||||
* @private
|
||||
*/
|
||||
async function printResults(engine, results, format, outputFile, resultsMeta) {
|
||||
let formatter;
|
||||
|
||||
try {
|
||||
formatter = await engine.loadFormatter(format);
|
||||
} catch (e) {
|
||||
log.error(e.message);
|
||||
return false;
|
||||
}
|
||||
|
||||
const output = await formatter.format(results, resultsMeta);
|
||||
|
||||
if (output) {
|
||||
if (outputFile) {
|
||||
const filePath = path.resolve(process.cwd(), outputFile);
|
||||
|
||||
if (await isDirectory(filePath)) {
|
||||
log.error("Cannot write to output file path, it is a directory: %s", outputFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
await mkdir(path.dirname(filePath), { recursive: true });
|
||||
await writeFile(filePath, output);
|
||||
} catch (ex) {
|
||||
log.error("There was a problem writing the output file:\n%s", ex);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
log.info(output);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Encapsulates all CLI behavior for eslint. Makes it easier to test as well as
|
||||
* for other Node.js programs to effectively run the CLI.
|
||||
*/
|
||||
const cli = {
|
||||
|
||||
/**
|
||||
* Executes the CLI based on an array of arguments that is passed in.
|
||||
* @param {string|Array|Object} args The arguments to process.
|
||||
* @param {string} [text] The text to lint (used for TTY).
|
||||
* @param {boolean} [allowFlatConfig] Whether or not to allow flat config.
|
||||
* @returns {Promise<number>} The exit code for the operation.
|
||||
*/
|
||||
async execute(args, text, allowFlatConfig) {
|
||||
if (Array.isArray(args)) {
|
||||
debug("CLI args: %o", args.slice(2));
|
||||
}
|
||||
|
||||
/*
|
||||
* Before doing anything, we need to see if we are using a
|
||||
* flat config file. If so, then we need to change the way command
|
||||
* line args are parsed. This is temporary, and when we fully
|
||||
* switch to flat config we can remove this logic.
|
||||
*/
|
||||
|
||||
const usingFlatConfig = allowFlatConfig && await shouldUseFlatConfig();
|
||||
|
||||
debug("Using flat config?", usingFlatConfig);
|
||||
|
||||
const CLIOptions = createCLIOptions(usingFlatConfig);
|
||||
|
||||
/** @type {ParsedCLIOptions} */
|
||||
let options;
|
||||
|
||||
try {
|
||||
options = CLIOptions.parse(args);
|
||||
} catch (error) {
|
||||
debug("Error parsing CLI options:", error.message);
|
||||
|
||||
let errorMessage = error.message;
|
||||
|
||||
if (usingFlatConfig) {
|
||||
errorMessage += "\nYou're using eslint.config.js, some command line flags are no longer available. Please see https://eslint.org/docs/latest/use/command-line-interface for details.";
|
||||
}
|
||||
|
||||
log.error(errorMessage);
|
||||
return 2;
|
||||
}
|
||||
|
||||
const files = options._;
|
||||
const useStdin = typeof text === "string";
|
||||
|
||||
if (options.help) {
|
||||
log.info(CLIOptions.generateHelp());
|
||||
return 0;
|
||||
}
|
||||
if (options.version) {
|
||||
log.info(RuntimeInfo.version());
|
||||
return 0;
|
||||
}
|
||||
if (options.envInfo) {
|
||||
try {
|
||||
log.info(RuntimeInfo.environment());
|
||||
return 0;
|
||||
} catch (err) {
|
||||
debug("Error retrieving environment info");
|
||||
log.error(err.message);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.printConfig) {
|
||||
if (files.length) {
|
||||
log.error("The --print-config option must be used with exactly one file name.");
|
||||
return 2;
|
||||
}
|
||||
if (useStdin) {
|
||||
log.error("The --print-config option is not available for piped-in code.");
|
||||
return 2;
|
||||
}
|
||||
|
||||
const engine = usingFlatConfig
|
||||
? new FlatESLint(await translateOptions(options, "flat"))
|
||||
: new ESLint(await translateOptions(options));
|
||||
const fileConfig =
|
||||
await engine.calculateConfigForFile(options.printConfig);
|
||||
|
||||
log.info(JSON.stringify(fileConfig, null, " "));
|
||||
return 0;
|
||||
}
|
||||
|
||||
debug(`Running on ${useStdin ? "text" : "files"}`);
|
||||
|
||||
if (options.fix && options.fixDryRun) {
|
||||
log.error("The --fix option and the --fix-dry-run option cannot be used together.");
|
||||
return 2;
|
||||
}
|
||||
if (useStdin && options.fix) {
|
||||
log.error("The --fix option is not available for piped-in code; use --fix-dry-run instead.");
|
||||
return 2;
|
||||
}
|
||||
if (options.fixType && !options.fix && !options.fixDryRun) {
|
||||
log.error("The --fix-type option requires either --fix or --fix-dry-run.");
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (options.reportUnusedDisableDirectives && options.reportUnusedDisableDirectivesSeverity !== void 0) {
|
||||
log.error("The --report-unused-disable-directives option and the --report-unused-disable-directives-severity option cannot be used together.");
|
||||
return 2;
|
||||
}
|
||||
|
||||
const ActiveESLint = usingFlatConfig ? FlatESLint : ESLint;
|
||||
|
||||
const engine = new ActiveESLint(await translateOptions(options, usingFlatConfig ? "flat" : "eslintrc"));
|
||||
let results;
|
||||
|
||||
if (useStdin) {
|
||||
results = await engine.lintText(text, {
|
||||
filePath: options.stdinFilename,
|
||||
|
||||
// flatConfig respects CLI flag and constructor warnIgnored, eslintrc forces true for backwards compatibility
|
||||
warnIgnored: usingFlatConfig ? void 0 : true
|
||||
});
|
||||
} else {
|
||||
results = await engine.lintFiles(files);
|
||||
}
|
||||
|
||||
if (options.fix) {
|
||||
debug("Fix mode enabled - applying fixes");
|
||||
await ActiveESLint.outputFixes(results);
|
||||
}
|
||||
|
||||
let resultsToPrint = results;
|
||||
|
||||
if (options.quiet) {
|
||||
debug("Quiet mode enabled - filtering out warnings");
|
||||
resultsToPrint = ActiveESLint.getErrorResults(resultsToPrint);
|
||||
}
|
||||
|
||||
const resultCounts = countErrors(results);
|
||||
const tooManyWarnings = options.maxWarnings >= 0 && resultCounts.warningCount > options.maxWarnings;
|
||||
const resultsMeta = tooManyWarnings
|
||||
? {
|
||||
maxWarningsExceeded: {
|
||||
maxWarnings: options.maxWarnings,
|
||||
foundWarnings: resultCounts.warningCount
|
||||
}
|
||||
}
|
||||
: {};
|
||||
|
||||
if (await printResults(engine, resultsToPrint, options.format, options.outputFile, resultsMeta)) {
|
||||
|
||||
// Errors and warnings from the original unfiltered results should determine the exit code
|
||||
const shouldExitForFatalErrors =
|
||||
options.exitOnFatalError && resultCounts.fatalErrorCount > 0;
|
||||
|
||||
if (!resultCounts.errorCount && tooManyWarnings) {
|
||||
log.error(
|
||||
"ESLint found too many warnings (maximum: %s).",
|
||||
options.maxWarnings
|
||||
);
|
||||
}
|
||||
|
||||
if (shouldExitForFatalErrors) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
return (resultCounts.errorCount || tooManyWarnings) ? 1 : 0;
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = cli;
|
||||
67
qwen/nodejs/node_modules/eslint/lib/config/default-config.js
generated
vendored
Normal file
67
qwen/nodejs/node_modules/eslint/lib/config/default-config.js
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @fileoverview Default configuration
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const Rules = require("../rules");
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
exports.defaultConfig = [
|
||||
{
|
||||
plugins: {
|
||||
"@": {
|
||||
|
||||
/*
|
||||
* Because we try to delay loading rules until absolutely
|
||||
* necessary, a proxy allows us to hook into the lazy-loading
|
||||
* aspect of the rules map while still keeping all of the
|
||||
* relevant configuration inside of the config array.
|
||||
*/
|
||||
rules: new Proxy({}, {
|
||||
get(target, property) {
|
||||
return Rules.get(property);
|
||||
},
|
||||
|
||||
has(target, property) {
|
||||
return Rules.has(property);
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
languageOptions: {
|
||||
sourceType: "module",
|
||||
ecmaVersion: "latest",
|
||||
parser: require("espree"),
|
||||
parserOptions: {}
|
||||
}
|
||||
},
|
||||
|
||||
// default ignores are listed here
|
||||
{
|
||||
ignores: [
|
||||
"**/node_modules/",
|
||||
".git/"
|
||||
]
|
||||
},
|
||||
|
||||
// intentionally empty config to ensure these files are globbed by default
|
||||
{
|
||||
files: ["**/*.js", "**/*.mjs"]
|
||||
},
|
||||
{
|
||||
files: ["**/*.cjs"],
|
||||
languageOptions: {
|
||||
sourceType: "commonjs",
|
||||
ecmaVersion: "latest"
|
||||
}
|
||||
}
|
||||
];
|
||||
380
qwen/nodejs/node_modules/eslint/lib/config/flat-config-array.js
generated
vendored
Normal file
380
qwen/nodejs/node_modules/eslint/lib/config/flat-config-array.js
generated
vendored
Normal file
@@ -0,0 +1,380 @@
|
||||
/**
|
||||
* @fileoverview Flat Config Array
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const { ConfigArray, ConfigArraySymbol } = require("@humanwhocodes/config-array");
|
||||
const { flatConfigSchema } = require("./flat-config-schema");
|
||||
const { RuleValidator } = require("./rule-validator");
|
||||
const { defaultConfig } = require("./default-config");
|
||||
const jsPlugin = require("@eslint/js");
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Fields that are considered metadata and not part of the config object.
|
||||
*/
|
||||
const META_FIELDS = new Set(["name"]);
|
||||
|
||||
const ruleValidator = new RuleValidator();
|
||||
|
||||
/**
|
||||
* Splits a plugin identifier in the form a/b/c into two parts: a/b and c.
|
||||
* @param {string} identifier The identifier to parse.
|
||||
* @returns {{objectName: string, pluginName: string}} The parts of the plugin
|
||||
* name.
|
||||
*/
|
||||
function splitPluginIdentifier(identifier) {
|
||||
const parts = identifier.split("/");
|
||||
|
||||
return {
|
||||
objectName: parts.pop(),
|
||||
pluginName: parts.join("/")
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of an object in the config by reading its `meta` key.
|
||||
* @param {Object} object The object to check.
|
||||
* @returns {string?} The name of the object if found or `null` if there
|
||||
* is no name.
|
||||
*/
|
||||
function getObjectId(object) {
|
||||
|
||||
// first check old-style name
|
||||
let name = object.name;
|
||||
|
||||
if (!name) {
|
||||
|
||||
if (!object.meta) {
|
||||
return null;
|
||||
}
|
||||
|
||||
name = object.meta.name;
|
||||
|
||||
if (!name) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// now check for old-style version
|
||||
let version = object.version;
|
||||
|
||||
if (!version) {
|
||||
version = object.meta && object.meta.version;
|
||||
}
|
||||
|
||||
// if there's a version then append that
|
||||
if (version) {
|
||||
return `${name}@${version}`;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a config error with details about where the error occurred.
|
||||
* @param {Error} error The original error.
|
||||
* @param {number} originalLength The original length of the config array.
|
||||
* @param {number} baseLength The length of the base config.
|
||||
* @returns {TypeError} The new error with details.
|
||||
*/
|
||||
function wrapConfigErrorWithDetails(error, originalLength, baseLength) {
|
||||
|
||||
let location = "user-defined";
|
||||
let configIndex = error.index;
|
||||
|
||||
/*
|
||||
* A config array is set up in this order:
|
||||
* 1. Base config
|
||||
* 2. Original configs
|
||||
* 3. User-defined configs
|
||||
* 4. CLI-defined configs
|
||||
*
|
||||
* So we need to adjust the index to account for the base config.
|
||||
*
|
||||
* - If the index is less than the base length, it's in the base config
|
||||
* (as specified by `baseConfig` argument to `FlatConfigArray` constructor).
|
||||
* - If the index is greater than the base length but less than the original
|
||||
* length + base length, it's in the original config. The original config
|
||||
* is passed to the `FlatConfigArray` constructor as the first argument.
|
||||
* - Otherwise, it's in the user-defined config, which is loaded from the
|
||||
* config file and merged with any command-line options.
|
||||
*/
|
||||
if (error.index < baseLength) {
|
||||
location = "base";
|
||||
} else if (error.index < originalLength + baseLength) {
|
||||
location = "original";
|
||||
configIndex = error.index - baseLength;
|
||||
} else {
|
||||
configIndex = error.index - originalLength - baseLength;
|
||||
}
|
||||
|
||||
return new TypeError(
|
||||
`${error.message.slice(0, -1)} at ${location} index ${configIndex}.`,
|
||||
{ cause: error }
|
||||
);
|
||||
}
|
||||
|
||||
const originalBaseConfig = Symbol("originalBaseConfig");
|
||||
const originalLength = Symbol("originalLength");
|
||||
const baseLength = Symbol("baseLength");
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Exports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Represents an array containing configuration information for ESLint.
|
||||
*/
|
||||
class FlatConfigArray extends ConfigArray {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {*[]} configs An array of configuration information.
|
||||
* @param {{basePath: string, shouldIgnore: boolean, baseConfig: FlatConfig}} options The options
|
||||
* to use for the config array instance.
|
||||
*/
|
||||
constructor(configs, {
|
||||
basePath,
|
||||
shouldIgnore = true,
|
||||
baseConfig = defaultConfig
|
||||
} = {}) {
|
||||
super(configs, {
|
||||
basePath,
|
||||
schema: flatConfigSchema
|
||||
});
|
||||
|
||||
/**
|
||||
* The original length of the array before any modifications.
|
||||
* @type {number}
|
||||
*/
|
||||
this[originalLength] = this.length;
|
||||
|
||||
if (baseConfig[Symbol.iterator]) {
|
||||
this.unshift(...baseConfig);
|
||||
} else {
|
||||
this.unshift(baseConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* The length of the array after applying the base config.
|
||||
* @type {number}
|
||||
*/
|
||||
this[baseLength] = this.length - this[originalLength];
|
||||
|
||||
/**
|
||||
* The base config used to build the config array.
|
||||
* @type {Array<FlatConfig>}
|
||||
*/
|
||||
this[originalBaseConfig] = baseConfig;
|
||||
Object.defineProperty(this, originalBaseConfig, { writable: false });
|
||||
|
||||
/**
|
||||
* Determines if `ignores` fields should be honored.
|
||||
* If true, then all `ignores` fields are honored.
|
||||
* if false, then only `ignores` fields in the baseConfig are honored.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.shouldIgnore = shouldIgnore;
|
||||
Object.defineProperty(this, "shouldIgnore", { writable: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the array by calling the superclass method and catching/rethrowing
|
||||
* any ConfigError exceptions with additional details.
|
||||
* @param {any} [context] The context to use to normalize the array.
|
||||
* @returns {Promise<FlatConfigArray>} A promise that resolves when the array is normalized.
|
||||
*/
|
||||
normalize(context) {
|
||||
return super.normalize(context)
|
||||
.catch(error => {
|
||||
if (error.name === "ConfigError") {
|
||||
throw wrapConfigErrorWithDetails(error, this[originalLength], this[baseLength]);
|
||||
}
|
||||
|
||||
throw error;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the array by calling the superclass method and catching/rethrowing
|
||||
* any ConfigError exceptions with additional details.
|
||||
* @param {any} [context] The context to use to normalize the array.
|
||||
* @returns {FlatConfigArray} The current instance.
|
||||
* @throws {TypeError} If the config is invalid.
|
||||
*/
|
||||
normalizeSync(context) {
|
||||
|
||||
try {
|
||||
|
||||
return super.normalizeSync(context);
|
||||
|
||||
} catch (error) {
|
||||
|
||||
if (error.name === "ConfigError") {
|
||||
throw wrapConfigErrorWithDetails(error, this[originalLength], this[baseLength]);
|
||||
}
|
||||
|
||||
throw error;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable class-methods-use-this -- Desired as instance method */
|
||||
/**
|
||||
* Replaces a config with another config to allow us to put strings
|
||||
* in the config array that will be replaced by objects before
|
||||
* normalization.
|
||||
* @param {Object} config The config to preprocess.
|
||||
* @returns {Object} The preprocessed config.
|
||||
*/
|
||||
[ConfigArraySymbol.preprocessConfig](config) {
|
||||
if (config === "eslint:recommended") {
|
||||
|
||||
// if we are in a Node.js environment warn the user
|
||||
if (typeof process !== "undefined" && process.emitWarning) {
|
||||
process.emitWarning("The 'eslint:recommended' string configuration is deprecated and will be replaced by the @eslint/js package's 'recommended' config.");
|
||||
}
|
||||
|
||||
return jsPlugin.configs.recommended;
|
||||
}
|
||||
|
||||
if (config === "eslint:all") {
|
||||
|
||||
// if we are in a Node.js environment warn the user
|
||||
if (typeof process !== "undefined" && process.emitWarning) {
|
||||
process.emitWarning("The 'eslint:all' string configuration is deprecated and will be replaced by the @eslint/js package's 'all' config.");
|
||||
}
|
||||
|
||||
return jsPlugin.configs.all;
|
||||
}
|
||||
|
||||
/*
|
||||
* If a config object has `ignores` and no other non-meta fields, then it's an object
|
||||
* for global ignores. If `shouldIgnore` is false, that object shouldn't apply,
|
||||
* so we'll remove its `ignores`.
|
||||
*/
|
||||
if (
|
||||
!this.shouldIgnore &&
|
||||
!this[originalBaseConfig].includes(config) &&
|
||||
config.ignores &&
|
||||
Object.keys(config).filter(key => !META_FIELDS.has(key)).length === 1
|
||||
) {
|
||||
/* eslint-disable-next-line no-unused-vars -- need to strip off other keys */
|
||||
const { ignores, ...otherKeys } = config;
|
||||
|
||||
return otherKeys;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalizes the config by replacing plugin references with their objects
|
||||
* and validating rule option schemas.
|
||||
* @param {Object} config The config to finalize.
|
||||
* @returns {Object} The finalized config.
|
||||
* @throws {TypeError} If the config is invalid.
|
||||
*/
|
||||
[ConfigArraySymbol.finalizeConfig](config) {
|
||||
|
||||
const { plugins, languageOptions, processor } = config;
|
||||
let parserName, processorName;
|
||||
let invalidParser = false,
|
||||
invalidProcessor = false;
|
||||
|
||||
// Check parser value
|
||||
if (languageOptions && languageOptions.parser) {
|
||||
const { parser } = languageOptions;
|
||||
|
||||
if (typeof parser === "object") {
|
||||
parserName = getObjectId(parser);
|
||||
|
||||
if (!parserName) {
|
||||
invalidParser = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
invalidParser = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check processor value
|
||||
if (processor) {
|
||||
if (typeof processor === "string") {
|
||||
const { pluginName, objectName: localProcessorName } = splitPluginIdentifier(processor);
|
||||
|
||||
processorName = processor;
|
||||
|
||||
if (!plugins || !plugins[pluginName] || !plugins[pluginName].processors || !plugins[pluginName].processors[localProcessorName]) {
|
||||
throw new TypeError(`Key "processor": Could not find "${localProcessorName}" in plugin "${pluginName}".`);
|
||||
}
|
||||
|
||||
config.processor = plugins[pluginName].processors[localProcessorName];
|
||||
} else if (typeof processor === "object") {
|
||||
processorName = getObjectId(processor);
|
||||
|
||||
if (!processorName) {
|
||||
invalidProcessor = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
invalidProcessor = true;
|
||||
}
|
||||
}
|
||||
|
||||
ruleValidator.validate(config);
|
||||
|
||||
// apply special logic for serialization into JSON
|
||||
/* eslint-disable object-shorthand -- shorthand would change "this" value */
|
||||
Object.defineProperty(config, "toJSON", {
|
||||
value: function() {
|
||||
|
||||
if (invalidParser) {
|
||||
throw new Error("Could not serialize parser object (missing 'meta' object).");
|
||||
}
|
||||
|
||||
if (invalidProcessor) {
|
||||
throw new Error("Could not serialize processor object (missing 'meta' object).");
|
||||
}
|
||||
|
||||
return {
|
||||
...this,
|
||||
plugins: Object.entries(plugins).map(([namespace, plugin]) => {
|
||||
|
||||
const pluginId = getObjectId(plugin);
|
||||
|
||||
if (!pluginId) {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
return `${namespace}:${pluginId}`;
|
||||
}),
|
||||
languageOptions: {
|
||||
...languageOptions,
|
||||
parser: parserName
|
||||
},
|
||||
processor: processorName
|
||||
};
|
||||
}
|
||||
});
|
||||
/* eslint-enable object-shorthand -- ok to enable now */
|
||||
|
||||
return config;
|
||||
}
|
||||
/* eslint-enable class-methods-use-this -- Desired as instance method */
|
||||
|
||||
}
|
||||
|
||||
exports.FlatConfigArray = FlatConfigArray;
|
||||
111
qwen/nodejs/node_modules/eslint/lib/config/flat-config-helpers.js
generated
vendored
Normal file
111
qwen/nodejs/node_modules/eslint/lib/config/flat-config-helpers.js
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* @fileoverview Shared functions to work with configs.
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Parses a ruleId into its plugin and rule parts.
|
||||
* @param {string} ruleId The rule ID to parse.
|
||||
* @returns {{pluginName:string,ruleName:string}} The plugin and rule
|
||||
* parts of the ruleId;
|
||||
*/
|
||||
function parseRuleId(ruleId) {
|
||||
let pluginName, ruleName;
|
||||
|
||||
// distinguish between core rules and plugin rules
|
||||
if (ruleId.includes("/")) {
|
||||
|
||||
// mimic scoped npm packages
|
||||
if (ruleId.startsWith("@")) {
|
||||
pluginName = ruleId.slice(0, ruleId.lastIndexOf("/"));
|
||||
} else {
|
||||
pluginName = ruleId.slice(0, ruleId.indexOf("/"));
|
||||
}
|
||||
|
||||
ruleName = ruleId.slice(pluginName.length + 1);
|
||||
} else {
|
||||
pluginName = "@";
|
||||
ruleName = ruleId;
|
||||
}
|
||||
|
||||
return {
|
||||
pluginName,
|
||||
ruleName
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a rule instance from a given config based on the ruleId.
|
||||
* @param {string} ruleId The rule ID to look for.
|
||||
* @param {FlatConfig} config The config to search.
|
||||
* @returns {import("../shared/types").Rule|undefined} The rule if found
|
||||
* or undefined if not.
|
||||
*/
|
||||
function getRuleFromConfig(ruleId, config) {
|
||||
|
||||
const { pluginName, ruleName } = parseRuleId(ruleId);
|
||||
|
||||
const plugin = config.plugins && config.plugins[pluginName];
|
||||
let rule = plugin && plugin.rules && plugin.rules[ruleName];
|
||||
|
||||
|
||||
// normalize function rules into objects
|
||||
if (rule && typeof rule === "function") {
|
||||
rule = {
|
||||
create: rule
|
||||
};
|
||||
}
|
||||
|
||||
return rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a complete options schema for a rule.
|
||||
* @param {{create: Function, schema: (Array|null)}} rule A new-style rule object
|
||||
* @returns {Object} JSON Schema for the rule's options.
|
||||
*/
|
||||
function getRuleOptionsSchema(rule) {
|
||||
|
||||
if (!rule) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const schema = rule.schema || rule.meta && rule.meta.schema;
|
||||
|
||||
if (Array.isArray(schema)) {
|
||||
if (schema.length) {
|
||||
return {
|
||||
type: "array",
|
||||
items: schema,
|
||||
minItems: 0,
|
||||
maxItems: schema.length
|
||||
};
|
||||
}
|
||||
return {
|
||||
type: "array",
|
||||
minItems: 0,
|
||||
maxItems: 0
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// Given a full schema, leave it alone
|
||||
return schema || null;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Exports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
module.exports = {
|
||||
parseRuleId,
|
||||
getRuleFromConfig,
|
||||
getRuleOptionsSchema
|
||||
};
|
||||
598
qwen/nodejs/node_modules/eslint/lib/config/flat-config-schema.js
generated
vendored
Normal file
598
qwen/nodejs/node_modules/eslint/lib/config/flat-config-schema.js
generated
vendored
Normal file
@@ -0,0 +1,598 @@
|
||||
/**
|
||||
* @fileoverview Flat config schema
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Note: This can be removed in ESLint v9 because structuredClone is available globally
|
||||
* starting in Node.js v17.
|
||||
*/
|
||||
const structuredClone = require("@ungap/structured-clone").default;
|
||||
const { normalizeSeverityToNumber } = require("../shared/severity");
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Type Definitions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @typedef ObjectPropertySchema
|
||||
* @property {Function|string} merge The function or name of the function to call
|
||||
* to merge multiple objects with this property.
|
||||
* @property {Function|string} validate The function or name of the function to call
|
||||
* to validate the value of this property.
|
||||
*/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const ruleSeverities = new Map([
|
||||
[0, 0], ["off", 0],
|
||||
[1, 1], ["warn", 1],
|
||||
[2, 2], ["error", 2]
|
||||
]);
|
||||
|
||||
const globalVariablesValues = new Set([
|
||||
true, "true", "writable", "writeable",
|
||||
false, "false", "readonly", "readable", null,
|
||||
"off"
|
||||
]);
|
||||
|
||||
/**
|
||||
* Check if a value is a non-null object.
|
||||
* @param {any} value The value to check.
|
||||
* @returns {boolean} `true` if the value is a non-null object.
|
||||
*/
|
||||
function isNonNullObject(value) {
|
||||
return typeof value === "object" && value !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a value is a non-null non-array object.
|
||||
* @param {any} value The value to check.
|
||||
* @returns {boolean} `true` if the value is a non-null non-array object.
|
||||
*/
|
||||
function isNonArrayObject(value) {
|
||||
return isNonNullObject(value) && !Array.isArray(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a value is undefined.
|
||||
* @param {any} value The value to check.
|
||||
* @returns {boolean} `true` if the value is undefined.
|
||||
*/
|
||||
function isUndefined(value) {
|
||||
return typeof value === "undefined";
|
||||
}
|
||||
|
||||
/**
|
||||
* Deeply merges two non-array objects.
|
||||
* @param {Object} first The base object.
|
||||
* @param {Object} second The overrides object.
|
||||
* @param {Map<string, Map<string, Object>>} [mergeMap] Maps the combination of first and second arguments to a merged result.
|
||||
* @returns {Object} An object with properties from both first and second.
|
||||
*/
|
||||
function deepMerge(first, second, mergeMap = new Map()) {
|
||||
|
||||
let secondMergeMap = mergeMap.get(first);
|
||||
|
||||
if (secondMergeMap) {
|
||||
const result = secondMergeMap.get(second);
|
||||
|
||||
if (result) {
|
||||
|
||||
// If this combination of first and second arguments has been already visited, return the previously created result.
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
secondMergeMap = new Map();
|
||||
mergeMap.set(first, secondMergeMap);
|
||||
}
|
||||
|
||||
/*
|
||||
* First create a result object where properties from the second object
|
||||
* overwrite properties from the first. This sets up a baseline to use
|
||||
* later rather than needing to inspect and change every property
|
||||
* individually.
|
||||
*/
|
||||
const result = {
|
||||
...first,
|
||||
...second
|
||||
};
|
||||
|
||||
delete result.__proto__; // eslint-disable-line no-proto -- don't merge own property "__proto__"
|
||||
|
||||
// Store the pending result for this combination of first and second arguments.
|
||||
secondMergeMap.set(second, result);
|
||||
|
||||
for (const key of Object.keys(second)) {
|
||||
|
||||
// avoid hairy edge case
|
||||
if (key === "__proto__" || !Object.prototype.propertyIsEnumerable.call(first, key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const firstValue = first[key];
|
||||
const secondValue = second[key];
|
||||
|
||||
if (isNonArrayObject(firstValue) && isNonArrayObject(secondValue)) {
|
||||
result[key] = deepMerge(firstValue, secondValue, mergeMap);
|
||||
} else if (isUndefined(secondValue)) {
|
||||
result[key] = firstValue;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the rule options config for a given rule by ensuring that
|
||||
* it is an array and that the first item is 0, 1, or 2.
|
||||
* @param {Array|string|number} ruleOptions The rule options config.
|
||||
* @returns {Array} An array of rule options.
|
||||
*/
|
||||
function normalizeRuleOptions(ruleOptions) {
|
||||
|
||||
const finalOptions = Array.isArray(ruleOptions)
|
||||
? ruleOptions.slice(0)
|
||||
: [ruleOptions];
|
||||
|
||||
finalOptions[0] = ruleSeverities.get(finalOptions[0]);
|
||||
return structuredClone(finalOptions);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Assertions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The error type when a rule's options are configured with an invalid type.
|
||||
*/
|
||||
class InvalidRuleOptionsError extends Error {
|
||||
|
||||
/**
|
||||
* @param {string} ruleId Rule name being configured.
|
||||
* @param {any} value The invalid value.
|
||||
*/
|
||||
constructor(ruleId, value) {
|
||||
super(`Key "${ruleId}": Expected severity of "off", 0, "warn", 1, "error", or 2.`);
|
||||
this.messageTemplate = "invalid-rule-options";
|
||||
this.messageData = { ruleId, value };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a value is a valid rule options entry.
|
||||
* @param {string} ruleId Rule name being configured.
|
||||
* @param {any} value The value to check.
|
||||
* @returns {void}
|
||||
* @throws {InvalidRuleOptionsError} If the value isn't a valid rule options.
|
||||
*/
|
||||
function assertIsRuleOptions(ruleId, value) {
|
||||
if (typeof value !== "string" && typeof value !== "number" && !Array.isArray(value)) {
|
||||
throw new InvalidRuleOptionsError(ruleId, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The error type when a rule's severity is invalid.
|
||||
*/
|
||||
class InvalidRuleSeverityError extends Error {
|
||||
|
||||
/**
|
||||
* @param {string} ruleId Rule name being configured.
|
||||
* @param {any} value The invalid value.
|
||||
*/
|
||||
constructor(ruleId, value) {
|
||||
super(`Key "${ruleId}": Expected severity of "off", 0, "warn", 1, "error", or 2.`);
|
||||
this.messageTemplate = "invalid-rule-severity";
|
||||
this.messageData = { ruleId, value };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a value is valid rule severity.
|
||||
* @param {string} ruleId Rule name being configured.
|
||||
* @param {any} value The value to check.
|
||||
* @returns {void}
|
||||
* @throws {InvalidRuleSeverityError} If the value isn't a valid rule severity.
|
||||
*/
|
||||
function assertIsRuleSeverity(ruleId, value) {
|
||||
const severity = ruleSeverities.get(value);
|
||||
|
||||
if (typeof severity === "undefined") {
|
||||
throw new InvalidRuleSeverityError(ruleId, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a given string is the form pluginName/objectName.
|
||||
* @param {string} value The string to check.
|
||||
* @returns {void}
|
||||
* @throws {TypeError} If the string isn't in the correct format.
|
||||
*/
|
||||
function assertIsPluginMemberName(value) {
|
||||
if (!/[@a-z0-9-_$]+(?:\/(?:[a-z0-9-_$]+))+$/iu.test(value)) {
|
||||
throw new TypeError(`Expected string in the form "pluginName/objectName" but found "${value}".`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a value is an object.
|
||||
* @param {any} value The value to check.
|
||||
* @returns {void}
|
||||
* @throws {TypeError} If the value isn't an object.
|
||||
*/
|
||||
function assertIsObject(value) {
|
||||
if (!isNonNullObject(value)) {
|
||||
throw new TypeError("Expected an object.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The error type when there's an eslintrc-style options in a flat config.
|
||||
*/
|
||||
class IncompatibleKeyError extends Error {
|
||||
|
||||
/**
|
||||
* @param {string} key The invalid key.
|
||||
*/
|
||||
constructor(key) {
|
||||
super("This appears to be in eslintrc format rather than flat config format.");
|
||||
this.messageTemplate = "eslintrc-incompat";
|
||||
this.messageData = { key };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The error type when there's an eslintrc-style plugins array found.
|
||||
*/
|
||||
class IncompatiblePluginsError extends Error {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Array<string>} plugins The plugins array.
|
||||
*/
|
||||
constructor(plugins) {
|
||||
super("This appears to be in eslintrc format (array of strings) rather than flat config format (object).");
|
||||
this.messageTemplate = "eslintrc-plugins";
|
||||
this.messageData = { plugins };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Low-Level Schemas
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const booleanSchema = {
|
||||
merge: "replace",
|
||||
validate: "boolean"
|
||||
};
|
||||
|
||||
const ALLOWED_SEVERITIES = new Set(["error", "warn", "off", 2, 1, 0]);
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const disableDirectiveSeveritySchema = {
|
||||
merge(first, second) {
|
||||
const value = second === void 0 ? first : second;
|
||||
|
||||
if (typeof value === "boolean") {
|
||||
return value ? "warn" : "off";
|
||||
}
|
||||
|
||||
return normalizeSeverityToNumber(value);
|
||||
},
|
||||
validate(value) {
|
||||
if (!(ALLOWED_SEVERITIES.has(value) || typeof value === "boolean")) {
|
||||
throw new TypeError("Expected one of: \"error\", \"warn\", \"off\", 0, 1, 2, or a boolean.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const deepObjectAssignSchema = {
|
||||
merge(first = {}, second = {}) {
|
||||
return deepMerge(first, second);
|
||||
},
|
||||
validate: "object"
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// High-Level Schemas
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const globalsSchema = {
|
||||
merge: "assign",
|
||||
validate(value) {
|
||||
|
||||
assertIsObject(value);
|
||||
|
||||
for (const key of Object.keys(value)) {
|
||||
|
||||
// avoid hairy edge case
|
||||
if (key === "__proto__") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key !== key.trim()) {
|
||||
throw new TypeError(`Global "${key}" has leading or trailing whitespace.`);
|
||||
}
|
||||
|
||||
if (!globalVariablesValues.has(value[key])) {
|
||||
throw new TypeError(`Key "${key}": Expected "readonly", "writable", or "off".`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const parserSchema = {
|
||||
merge: "replace",
|
||||
validate(value) {
|
||||
|
||||
if (!value || typeof value !== "object" ||
|
||||
(typeof value.parse !== "function" && typeof value.parseForESLint !== "function")
|
||||
) {
|
||||
throw new TypeError("Expected object with parse() or parseForESLint() method.");
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const pluginsSchema = {
|
||||
merge(first = {}, second = {}) {
|
||||
const keys = new Set([...Object.keys(first), ...Object.keys(second)]);
|
||||
const result = {};
|
||||
|
||||
// manually validate that plugins are not redefined
|
||||
for (const key of keys) {
|
||||
|
||||
// avoid hairy edge case
|
||||
if (key === "__proto__") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key in first && key in second && first[key] !== second[key]) {
|
||||
throw new TypeError(`Cannot redefine plugin "${key}".`);
|
||||
}
|
||||
|
||||
result[key] = second[key] || first[key];
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
validate(value) {
|
||||
|
||||
// first check the value to be sure it's an object
|
||||
if (value === null || typeof value !== "object") {
|
||||
throw new TypeError("Expected an object.");
|
||||
}
|
||||
|
||||
// make sure it's not an array, which would mean eslintrc-style is used
|
||||
if (Array.isArray(value)) {
|
||||
throw new IncompatiblePluginsError(value);
|
||||
}
|
||||
|
||||
// second check the keys to make sure they are objects
|
||||
for (const key of Object.keys(value)) {
|
||||
|
||||
// avoid hairy edge case
|
||||
if (key === "__proto__") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value[key] === null || typeof value[key] !== "object") {
|
||||
throw new TypeError(`Key "${key}": Expected an object.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const processorSchema = {
|
||||
merge: "replace",
|
||||
validate(value) {
|
||||
if (typeof value === "string") {
|
||||
assertIsPluginMemberName(value);
|
||||
} else if (value && typeof value === "object") {
|
||||
if (typeof value.preprocess !== "function" || typeof value.postprocess !== "function") {
|
||||
throw new TypeError("Object must have a preprocess() and a postprocess() method.");
|
||||
}
|
||||
} else {
|
||||
throw new TypeError("Expected an object or a string.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const rulesSchema = {
|
||||
merge(first = {}, second = {}) {
|
||||
|
||||
const result = {
|
||||
...first,
|
||||
...second
|
||||
};
|
||||
|
||||
|
||||
for (const ruleId of Object.keys(result)) {
|
||||
|
||||
try {
|
||||
|
||||
// avoid hairy edge case
|
||||
if (ruleId === "__proto__") {
|
||||
|
||||
/* eslint-disable-next-line no-proto -- Though deprecated, may still be present */
|
||||
delete result.__proto__;
|
||||
continue;
|
||||
}
|
||||
|
||||
result[ruleId] = normalizeRuleOptions(result[ruleId]);
|
||||
|
||||
/*
|
||||
* If either rule config is missing, then the correct
|
||||
* config is already present and we just need to normalize
|
||||
* the severity.
|
||||
*/
|
||||
if (!(ruleId in first) || !(ruleId in second)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const firstRuleOptions = normalizeRuleOptions(first[ruleId]);
|
||||
const secondRuleOptions = normalizeRuleOptions(second[ruleId]);
|
||||
|
||||
/*
|
||||
* If the second rule config only has a severity (length of 1),
|
||||
* then use that severity and keep the rest of the options from
|
||||
* the first rule config.
|
||||
*/
|
||||
if (secondRuleOptions.length === 1) {
|
||||
result[ruleId] = [secondRuleOptions[0], ...firstRuleOptions.slice(1)];
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* In any other situation, then the second rule config takes
|
||||
* precedence. That means the value at `result[ruleId]` is
|
||||
* already correct and no further work is necessary.
|
||||
*/
|
||||
} catch (ex) {
|
||||
throw new Error(`Key "${ruleId}": ${ex.message}`, { cause: ex });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
|
||||
},
|
||||
|
||||
validate(value) {
|
||||
assertIsObject(value);
|
||||
|
||||
/*
|
||||
* We are not checking the rule schema here because there is no
|
||||
* guarantee that the rule definition is present at this point. Instead
|
||||
* we wait and check the rule schema during the finalization step
|
||||
* of calculating a config.
|
||||
*/
|
||||
for (const ruleId of Object.keys(value)) {
|
||||
|
||||
// avoid hairy edge case
|
||||
if (ruleId === "__proto__") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const ruleOptions = value[ruleId];
|
||||
|
||||
assertIsRuleOptions(ruleId, ruleOptions);
|
||||
|
||||
if (Array.isArray(ruleOptions)) {
|
||||
assertIsRuleSeverity(ruleId, ruleOptions[0]);
|
||||
} else {
|
||||
assertIsRuleSeverity(ruleId, ruleOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const ecmaVersionSchema = {
|
||||
merge: "replace",
|
||||
validate(value) {
|
||||
if (typeof value === "number" || value === "latest") {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new TypeError("Expected a number or \"latest\".");
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {ObjectPropertySchema} */
|
||||
const sourceTypeSchema = {
|
||||
merge: "replace",
|
||||
validate(value) {
|
||||
if (typeof value !== "string" || !/^(?:script|module|commonjs)$/u.test(value)) {
|
||||
throw new TypeError("Expected \"script\", \"module\", or \"commonjs\".");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a schema that always throws an error. Useful for warning
|
||||
* about eslintrc-style keys.
|
||||
* @param {string} key The eslintrc key to create a schema for.
|
||||
* @returns {ObjectPropertySchema} The schema.
|
||||
*/
|
||||
function createEslintrcErrorSchema(key) {
|
||||
return {
|
||||
merge: "replace",
|
||||
validate() {
|
||||
throw new IncompatibleKeyError(key);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const eslintrcKeys = [
|
||||
"env",
|
||||
"extends",
|
||||
"globals",
|
||||
"ignorePatterns",
|
||||
"noInlineConfig",
|
||||
"overrides",
|
||||
"parser",
|
||||
"parserOptions",
|
||||
"reportUnusedDisableDirectives",
|
||||
"root"
|
||||
];
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Full schema
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const flatConfigSchema = {
|
||||
|
||||
// eslintrc-style keys that should always error
|
||||
...Object.fromEntries(eslintrcKeys.map(key => [key, createEslintrcErrorSchema(key)])),
|
||||
|
||||
// flat config keys
|
||||
settings: deepObjectAssignSchema,
|
||||
linterOptions: {
|
||||
schema: {
|
||||
noInlineConfig: booleanSchema,
|
||||
reportUnusedDisableDirectives: disableDirectiveSeveritySchema
|
||||
}
|
||||
},
|
||||
languageOptions: {
|
||||
schema: {
|
||||
ecmaVersion: ecmaVersionSchema,
|
||||
sourceType: sourceTypeSchema,
|
||||
globals: globalsSchema,
|
||||
parser: parserSchema,
|
||||
parserOptions: deepObjectAssignSchema
|
||||
}
|
||||
},
|
||||
processor: processorSchema,
|
||||
plugins: pluginsSchema,
|
||||
rules: rulesSchema
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Exports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
module.exports = {
|
||||
flatConfigSchema,
|
||||
assertIsRuleSeverity,
|
||||
assertIsRuleOptions
|
||||
};
|
||||
158
qwen/nodejs/node_modules/eslint/lib/config/rule-validator.js
generated
vendored
Normal file
158
qwen/nodejs/node_modules/eslint/lib/config/rule-validator.js
generated
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* @fileoverview Rule Validator
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const ajvImport = require("../shared/ajv");
|
||||
const ajv = ajvImport();
|
||||
const {
|
||||
parseRuleId,
|
||||
getRuleFromConfig,
|
||||
getRuleOptionsSchema
|
||||
} = require("./flat-config-helpers");
|
||||
const ruleReplacements = require("../../conf/replacements.json");
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Throws a helpful error when a rule cannot be found.
|
||||
* @param {Object} ruleId The rule identifier.
|
||||
* @param {string} ruleId.pluginName The ID of the rule to find.
|
||||
* @param {string} ruleId.ruleName The ID of the rule to find.
|
||||
* @param {Object} config The config to search in.
|
||||
* @throws {TypeError} For missing plugin or rule.
|
||||
* @returns {void}
|
||||
*/
|
||||
function throwRuleNotFoundError({ pluginName, ruleName }, config) {
|
||||
|
||||
const ruleId = pluginName === "@" ? ruleName : `${pluginName}/${ruleName}`;
|
||||
|
||||
const errorMessageHeader = `Key "rules": Key "${ruleId}"`;
|
||||
let errorMessage = `${errorMessageHeader}: Could not find plugin "${pluginName}".`;
|
||||
|
||||
// if the plugin exists then we need to check if the rule exists
|
||||
if (config.plugins && config.plugins[pluginName]) {
|
||||
const replacementRuleName = ruleReplacements.rules[ruleName];
|
||||
|
||||
if (pluginName === "@" && replacementRuleName) {
|
||||
|
||||
errorMessage = `${errorMessageHeader}: Rule "${ruleName}" was removed and replaced by "${replacementRuleName}".`;
|
||||
|
||||
} else {
|
||||
|
||||
errorMessage = `${errorMessageHeader}: Could not find "${ruleName}" in plugin "${pluginName}".`;
|
||||
|
||||
// otherwise, let's see if we can find the rule name elsewhere
|
||||
for (const [otherPluginName, otherPlugin] of Object.entries(config.plugins)) {
|
||||
if (otherPlugin.rules && otherPlugin.rules[ruleName]) {
|
||||
errorMessage += ` Did you mean "${otherPluginName}/${ruleName}"?`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// falls through to throw error
|
||||
}
|
||||
|
||||
throw new TypeError(errorMessage);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Exports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Implements validation functionality for the rules portion of a config.
|
||||
*/
|
||||
class RuleValidator {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*/
|
||||
constructor() {
|
||||
|
||||
/**
|
||||
* A collection of compiled validators for rules that have already
|
||||
* been validated.
|
||||
* @type {WeakMap}
|
||||
*/
|
||||
this.validators = new WeakMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates all of the rule configurations in a config against each
|
||||
* rule's schema.
|
||||
* @param {Object} config The full config to validate. This object must
|
||||
* contain both the rules section and the plugins section.
|
||||
* @returns {void}
|
||||
* @throws {Error} If a rule's configuration does not match its schema.
|
||||
*/
|
||||
validate(config) {
|
||||
|
||||
if (!config.rules) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const [ruleId, ruleOptions] of Object.entries(config.rules)) {
|
||||
|
||||
// check for edge case
|
||||
if (ruleId === "__proto__") {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If a rule is disabled, we don't do any validation. This allows
|
||||
* users to safely set any value to 0 or "off" without worrying
|
||||
* that it will cause a validation error.
|
||||
*
|
||||
* Note: ruleOptions is always an array at this point because
|
||||
* this validation occurs after FlatConfigArray has merged and
|
||||
* normalized values.
|
||||
*/
|
||||
if (ruleOptions[0] === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const rule = getRuleFromConfig(ruleId, config);
|
||||
|
||||
if (!rule) {
|
||||
throwRuleNotFoundError(parseRuleId(ruleId), config);
|
||||
}
|
||||
|
||||
// Precompile and cache validator the first time
|
||||
if (!this.validators.has(rule)) {
|
||||
const schema = getRuleOptionsSchema(rule);
|
||||
|
||||
if (schema) {
|
||||
this.validators.set(rule, ajv.compile(schema));
|
||||
}
|
||||
}
|
||||
|
||||
const validateRule = this.validators.get(rule);
|
||||
|
||||
if (validateRule) {
|
||||
|
||||
validateRule(ruleOptions.slice(1));
|
||||
|
||||
if (validateRule.errors) {
|
||||
throw new Error(`Key "rules": Key "${ruleId}": ${
|
||||
validateRule.errors.map(
|
||||
error => `\tValue ${JSON.stringify(error.data)} ${error.message}.\n`
|
||||
).join("")
|
||||
}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exports.RuleValidator = RuleValidator;
|
||||
932
qwen/nodejs/node_modules/eslint/lib/eslint/eslint-helpers.js
generated
vendored
Normal file
932
qwen/nodejs/node_modules/eslint/lib/eslint/eslint-helpers.js
generated
vendored
Normal file
@@ -0,0 +1,932 @@
|
||||
/**
|
||||
* @fileoverview Helper functions for ESLint class
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const fsp = fs.promises;
|
||||
const isGlob = require("is-glob");
|
||||
const hash = require("../cli-engine/hash");
|
||||
const minimatch = require("minimatch");
|
||||
const fswalk = require("@nodelib/fs.walk");
|
||||
const globParent = require("glob-parent");
|
||||
const isPathInside = require("is-path-inside");
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Fixup references
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const Minimatch = minimatch.Minimatch;
|
||||
const MINIMATCH_OPTIONS = { dot: true };
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Types
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @typedef {Object} GlobSearch
|
||||
* @property {Array<string>} patterns The normalized patterns to use for a search.
|
||||
* @property {Array<string>} rawPatterns The patterns as entered by the user
|
||||
* before doing any normalization.
|
||||
*/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Errors
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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} globEnabled If `false` then the pattern was a glob pattern, but glob was disabled.
|
||||
*/
|
||||
constructor(pattern, globEnabled) {
|
||||
super(`No files matching '${pattern}' were found${!globEnabled ? " (glob was disabled)" : ""}.`);
|
||||
this.messageTemplate = "file-not-found";
|
||||
this.messageData = { pattern, globDisabled: !globEnabled };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The error type when a search fails to match multiple patterns.
|
||||
*/
|
||||
class UnmatchedSearchPatternsError extends Error {
|
||||
|
||||
/**
|
||||
* @param {Object} options The options for the error.
|
||||
* @param {string} options.basePath The directory that was searched.
|
||||
* @param {Array<string>} options.unmatchedPatterns The glob patterns
|
||||
* which were not found.
|
||||
* @param {Array<string>} options.patterns The glob patterns that were
|
||||
* searched.
|
||||
* @param {Array<string>} options.rawPatterns The raw glob patterns that
|
||||
* were searched.
|
||||
*/
|
||||
constructor({ basePath, unmatchedPatterns, patterns, rawPatterns }) {
|
||||
super(`No files matching '${rawPatterns}' in '${basePath}' were found.`);
|
||||
this.basePath = basePath;
|
||||
this.unmatchedPatterns = unmatchedPatterns;
|
||||
this.patterns = patterns;
|
||||
this.rawPatterns = rawPatterns;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// General Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Check if a given value is a non-empty string or not.
|
||||
* @param {any} x The value to check.
|
||||
* @returns {boolean} `true` if `x` is a non-empty string.
|
||||
*/
|
||||
function isNonEmptyString(x) {
|
||||
return typeof x === "string" && x.trim() !== "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given value is an array of non-empty strings or not.
|
||||
* @param {any} x The value to check.
|
||||
* @returns {boolean} `true` if `x` is an array of non-empty strings.
|
||||
*/
|
||||
function isArrayOfNonEmptyString(x) {
|
||||
return Array.isArray(x) && x.every(isNonEmptyString);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// File-related Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Normalizes slashes in a file pattern to posix-style.
|
||||
* @param {string} pattern The pattern to replace slashes in.
|
||||
* @returns {string} The pattern with slashes normalized.
|
||||
*/
|
||||
function normalizeToPosix(pattern) {
|
||||
return pattern.replace(/\\/gu, "/");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 === "\\" ? normalizeToPosix(pattern) : pattern);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines if a given glob pattern will return any results.
|
||||
* Used primarily to help with useful error messages.
|
||||
* @param {Object} options The options for the function.
|
||||
* @param {string} options.basePath The directory to search.
|
||||
* @param {string} options.pattern A glob pattern to match.
|
||||
* @returns {Promise<boolean>} True if there is a glob match, false if not.
|
||||
*/
|
||||
function globMatch({ basePath, pattern }) {
|
||||
|
||||
let found = false;
|
||||
const patternToUse = path.isAbsolute(pattern)
|
||||
? normalizeToPosix(path.relative(basePath, pattern))
|
||||
: pattern;
|
||||
|
||||
const matcher = new Minimatch(patternToUse, MINIMATCH_OPTIONS);
|
||||
|
||||
const fsWalkSettings = {
|
||||
|
||||
deepFilter(entry) {
|
||||
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
|
||||
|
||||
return !found && matcher.match(relativePath, true);
|
||||
},
|
||||
|
||||
entryFilter(entry) {
|
||||
if (found || entry.dirent.isDirectory()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
|
||||
|
||||
if (matcher.match(relativePath)) {
|
||||
found = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
return new Promise(resolve => {
|
||||
|
||||
// using a stream so we can exit early because we just need one match
|
||||
const globStream = fswalk.walkStream(basePath, fsWalkSettings);
|
||||
|
||||
globStream.on("data", () => {
|
||||
globStream.destroy();
|
||||
resolve(true);
|
||||
});
|
||||
|
||||
// swallow errors as they're not important here
|
||||
globStream.on("error", () => { });
|
||||
|
||||
globStream.on("end", () => {
|
||||
resolve(false);
|
||||
});
|
||||
globStream.read();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches a directory looking for matching glob patterns. This uses
|
||||
* the config array's logic to determine if a directory or file should
|
||||
* be ignored, so it is consistent with how ignoring works throughout
|
||||
* ESLint.
|
||||
* @param {Object} options The options for this function.
|
||||
* @param {string} options.basePath The directory to search.
|
||||
* @param {Array<string>} options.patterns An array of glob patterns
|
||||
* to match.
|
||||
* @param {Array<string>} options.rawPatterns An array of glob patterns
|
||||
* as the user inputted them. Used for errors.
|
||||
* @param {FlatConfigArray} options.configs The config array to use for
|
||||
* determining what to ignore.
|
||||
* @param {boolean} options.errorOnUnmatchedPattern Determines if an error
|
||||
* should be thrown when a pattern is unmatched.
|
||||
* @returns {Promise<Array<string>>} An array of matching file paths
|
||||
* or an empty array if there are no matches.
|
||||
* @throws {UnmatchedSearchPatternsError} If there is a pattern that doesn't
|
||||
* match any files.
|
||||
*/
|
||||
async function globSearch({
|
||||
basePath,
|
||||
patterns,
|
||||
rawPatterns,
|
||||
configs,
|
||||
errorOnUnmatchedPattern
|
||||
}) {
|
||||
|
||||
if (patterns.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/*
|
||||
* In this section we are converting the patterns into Minimatch
|
||||
* instances for performance reasons. Because we are doing the same
|
||||
* matches repeatedly, it's best to compile those patterns once and
|
||||
* reuse them multiple times.
|
||||
*
|
||||
* To do that, we convert any patterns with an absolute path into a
|
||||
* relative path and normalize it to Posix-style slashes. We also keep
|
||||
* track of the relative patterns to map them back to the original
|
||||
* patterns, which we need in order to throw an error if there are any
|
||||
* unmatched patterns.
|
||||
*/
|
||||
const relativeToPatterns = new Map();
|
||||
const matchers = patterns.map((pattern, i) => {
|
||||
const patternToUse = path.isAbsolute(pattern)
|
||||
? normalizeToPosix(path.relative(basePath, pattern))
|
||||
: pattern;
|
||||
|
||||
relativeToPatterns.set(patternToUse, patterns[i]);
|
||||
|
||||
return new Minimatch(patternToUse, MINIMATCH_OPTIONS);
|
||||
});
|
||||
|
||||
/*
|
||||
* We track unmatched patterns because we may want to throw an error when
|
||||
* they occur. To start, this set is initialized with all of the patterns.
|
||||
* Every time a match occurs, the pattern is removed from the set, making
|
||||
* it easy to tell if we have any unmatched patterns left at the end of
|
||||
* search.
|
||||
*/
|
||||
const unmatchedPatterns = new Set([...relativeToPatterns.keys()]);
|
||||
|
||||
const filePaths = (await new Promise((resolve, reject) => {
|
||||
|
||||
let promiseRejected = false;
|
||||
|
||||
/**
|
||||
* Wraps a boolean-returning filter function. The wrapped function will reject the promise if an error occurs.
|
||||
* @param {Function} filter A filter function to wrap.
|
||||
* @returns {Function} A function similar to the wrapped filter that rejects the promise if an error occurs.
|
||||
*/
|
||||
function wrapFilter(filter) {
|
||||
return (...args) => {
|
||||
|
||||
// No need to run the filter if an error has been thrown.
|
||||
if (!promiseRejected) {
|
||||
try {
|
||||
return filter(...args);
|
||||
} catch (error) {
|
||||
promiseRejected = true;
|
||||
reject(error);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
fswalk.walk(
|
||||
basePath,
|
||||
{
|
||||
deepFilter: wrapFilter(entry => {
|
||||
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
|
||||
const matchesPattern = matchers.some(matcher => matcher.match(relativePath, true));
|
||||
|
||||
return matchesPattern && !configs.isDirectoryIgnored(entry.path);
|
||||
}),
|
||||
entryFilter: wrapFilter(entry => {
|
||||
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
|
||||
|
||||
// entries may be directories or files so filter out directories
|
||||
if (entry.dirent.isDirectory()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Optimization: We need to track when patterns are left unmatched
|
||||
* and so we use `unmatchedPatterns` to do that. There is a bit of
|
||||
* complexity here because the same file can be matched by more than
|
||||
* one pattern. So, when we start, we actually need to test every
|
||||
* pattern against every file. Once we know there are no remaining
|
||||
* unmatched patterns, then we can switch to just looking for the
|
||||
* first matching pattern for improved speed.
|
||||
*/
|
||||
const matchesPattern = unmatchedPatterns.size > 0
|
||||
? matchers.reduce((previousValue, matcher) => {
|
||||
const pathMatches = matcher.match(relativePath);
|
||||
|
||||
/*
|
||||
* We updated the unmatched patterns set only if the path
|
||||
* matches and the file isn't ignored. If the file is
|
||||
* ignored, that means there wasn't a match for the
|
||||
* pattern so it should not be removed.
|
||||
*
|
||||
* Performance note: isFileIgnored() aggressively caches
|
||||
* results so there is no performance penalty for calling
|
||||
* it twice with the same argument.
|
||||
*/
|
||||
if (pathMatches && !configs.isFileIgnored(entry.path)) {
|
||||
unmatchedPatterns.delete(matcher.pattern);
|
||||
}
|
||||
|
||||
return pathMatches || previousValue;
|
||||
}, false)
|
||||
: matchers.some(matcher => matcher.match(relativePath));
|
||||
|
||||
return matchesPattern && !configs.isFileIgnored(entry.path);
|
||||
})
|
||||
},
|
||||
(error, entries) => {
|
||||
|
||||
// If the promise is already rejected, calling `resolve` or `reject` will do nothing.
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(entries);
|
||||
}
|
||||
}
|
||||
);
|
||||
})).map(entry => entry.path);
|
||||
|
||||
// now check to see if we have any unmatched patterns
|
||||
if (errorOnUnmatchedPattern && unmatchedPatterns.size > 0) {
|
||||
throw new UnmatchedSearchPatternsError({
|
||||
basePath,
|
||||
unmatchedPatterns: [...unmatchedPatterns].map(
|
||||
pattern => relativeToPatterns.get(pattern)
|
||||
),
|
||||
patterns,
|
||||
rawPatterns
|
||||
});
|
||||
}
|
||||
|
||||
return filePaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an error for unmatched patterns. The error will only contain information about the first one.
|
||||
* Checks to see if there are any ignored results for a given search.
|
||||
* @param {Object} options The options for this function.
|
||||
* @param {string} options.basePath The directory to search.
|
||||
* @param {Array<string>} options.patterns An array of glob patterns
|
||||
* that were used in the original search.
|
||||
* @param {Array<string>} options.rawPatterns An array of glob patterns
|
||||
* as the user inputted them. Used for errors.
|
||||
* @param {Array<string>} options.unmatchedPatterns A non-empty array of glob patterns
|
||||
* that were unmatched in the original search.
|
||||
* @returns {void} Always throws an error.
|
||||
* @throws {NoFilesFoundError} If the first unmatched pattern
|
||||
* doesn't match any files even when there are no ignores.
|
||||
* @throws {AllFilesIgnoredError} If the first unmatched pattern
|
||||
* matches some files when there are no ignores.
|
||||
*/
|
||||
async function throwErrorForUnmatchedPatterns({
|
||||
basePath,
|
||||
patterns,
|
||||
rawPatterns,
|
||||
unmatchedPatterns
|
||||
}) {
|
||||
|
||||
const pattern = unmatchedPatterns[0];
|
||||
const rawPattern = rawPatterns[patterns.indexOf(pattern)];
|
||||
|
||||
const patternHasMatch = await globMatch({
|
||||
basePath,
|
||||
pattern
|
||||
});
|
||||
|
||||
if (patternHasMatch) {
|
||||
throw new AllFilesIgnoredError(rawPattern);
|
||||
}
|
||||
|
||||
// if we get here there are truly no matches
|
||||
throw new NoFilesFoundError(rawPattern, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs multiple glob searches in parallel.
|
||||
* @param {Object} options The options for this function.
|
||||
* @param {Map<string,GlobSearch>} options.searches
|
||||
* An array of glob patterns to match.
|
||||
* @param {FlatConfigArray} options.configs The config array to use for
|
||||
* determining what to ignore.
|
||||
* @param {boolean} options.errorOnUnmatchedPattern Determines if an
|
||||
* unmatched glob pattern should throw an error.
|
||||
* @returns {Promise<Array<string>>} An array of matching file paths
|
||||
* or an empty array if there are no matches.
|
||||
*/
|
||||
async function globMultiSearch({ searches, configs, errorOnUnmatchedPattern }) {
|
||||
|
||||
/*
|
||||
* For convenience, we normalized the search map into an array of objects.
|
||||
* Next, we filter out all searches that have no patterns. This happens
|
||||
* primarily for the cwd, which is prepopulated in the searches map as an
|
||||
* optimization. However, if it has no patterns, it means all patterns
|
||||
* occur outside of the cwd and we can safely filter out that search.
|
||||
*/
|
||||
const normalizedSearches = [...searches].map(
|
||||
([basePath, { patterns, rawPatterns }]) => ({ basePath, patterns, rawPatterns })
|
||||
).filter(({ patterns }) => patterns.length > 0);
|
||||
|
||||
const results = await Promise.allSettled(
|
||||
normalizedSearches.map(
|
||||
({ basePath, patterns, rawPatterns }) => globSearch({
|
||||
basePath,
|
||||
patterns,
|
||||
rawPatterns,
|
||||
configs,
|
||||
errorOnUnmatchedPattern
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
const filePaths = [];
|
||||
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
|
||||
const result = results[i];
|
||||
const currentSearch = normalizedSearches[i];
|
||||
|
||||
if (result.status === "fulfilled") {
|
||||
|
||||
// if the search was successful just add the results
|
||||
if (result.value.length > 0) {
|
||||
filePaths.push(...result.value);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// if we make it here then there was an error
|
||||
const error = result.reason;
|
||||
|
||||
// unexpected errors should be re-thrown
|
||||
if (!error.basePath) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (errorOnUnmatchedPattern) {
|
||||
|
||||
await throwErrorForUnmatchedPatterns({
|
||||
...currentSearch,
|
||||
unmatchedPatterns: error.unmatchedPatterns
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return filePaths;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all files matching the options specified.
|
||||
* @param {Object} args The arguments objects.
|
||||
* @param {Array<string>} args.patterns An array of glob patterns.
|
||||
* @param {boolean} args.globInputPaths true to interpret glob patterns,
|
||||
* false to not interpret glob patterns.
|
||||
* @param {string} args.cwd The current working directory to find from.
|
||||
* @param {FlatConfigArray} args.configs The configs for the current run.
|
||||
* @param {boolean} args.errorOnUnmatchedPattern Determines if an unmatched pattern
|
||||
* should throw an error.
|
||||
* @returns {Promise<Array<string>>} The fully resolved file paths.
|
||||
* @throws {AllFilesIgnoredError} If there are no results due to an ignore pattern.
|
||||
* @throws {NoFilesFoundError} If no files matched the given patterns.
|
||||
*/
|
||||
async function findFiles({
|
||||
patterns,
|
||||
globInputPaths,
|
||||
cwd,
|
||||
configs,
|
||||
errorOnUnmatchedPattern
|
||||
}) {
|
||||
|
||||
const results = [];
|
||||
const missingPatterns = [];
|
||||
let globbyPatterns = [];
|
||||
let rawPatterns = [];
|
||||
const searches = new Map([[cwd, { patterns: globbyPatterns, rawPatterns: [] }]]);
|
||||
|
||||
// check to see if we have explicit files and directories
|
||||
const filePaths = patterns.map(filePath => path.resolve(cwd, filePath));
|
||||
const stats = await Promise.all(
|
||||
filePaths.map(
|
||||
filePath => fsp.stat(filePath).catch(() => { })
|
||||
)
|
||||
);
|
||||
|
||||
stats.forEach((stat, index) => {
|
||||
|
||||
const filePath = filePaths[index];
|
||||
const pattern = normalizeToPosix(patterns[index]);
|
||||
|
||||
if (stat) {
|
||||
|
||||
// files are added directly to the list
|
||||
if (stat.isFile()) {
|
||||
results.push(filePath);
|
||||
}
|
||||
|
||||
// directories need extensions attached
|
||||
if (stat.isDirectory()) {
|
||||
|
||||
// group everything in cwd together and split out others
|
||||
if (isPathInside(filePath, cwd)) {
|
||||
({ patterns: globbyPatterns, rawPatterns } = searches.get(cwd));
|
||||
} else {
|
||||
if (!searches.has(filePath)) {
|
||||
searches.set(filePath, { patterns: [], rawPatterns: [] });
|
||||
}
|
||||
({ patterns: globbyPatterns, rawPatterns } = searches.get(filePath));
|
||||
}
|
||||
|
||||
globbyPatterns.push(`${normalizeToPosix(filePath)}/**`);
|
||||
rawPatterns.push(pattern);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// save patterns for later use based on whether globs are enabled
|
||||
if (globInputPaths && isGlobPattern(pattern)) {
|
||||
|
||||
const basePath = path.resolve(cwd, globParent(pattern));
|
||||
|
||||
// group in cwd if possible and split out others
|
||||
if (isPathInside(basePath, cwd)) {
|
||||
({ patterns: globbyPatterns, rawPatterns } = searches.get(cwd));
|
||||
} else {
|
||||
if (!searches.has(basePath)) {
|
||||
searches.set(basePath, { patterns: [], rawPatterns: [] });
|
||||
}
|
||||
({ patterns: globbyPatterns, rawPatterns } = searches.get(basePath));
|
||||
}
|
||||
|
||||
globbyPatterns.push(filePath);
|
||||
rawPatterns.push(pattern);
|
||||
} else {
|
||||
missingPatterns.push(pattern);
|
||||
}
|
||||
});
|
||||
|
||||
// there were patterns that didn't match anything, tell the user
|
||||
if (errorOnUnmatchedPattern && missingPatterns.length) {
|
||||
throw new NoFilesFoundError(missingPatterns[0], globInputPaths);
|
||||
}
|
||||
|
||||
// now we are safe to do the search
|
||||
const globbyResults = await globMultiSearch({
|
||||
searches,
|
||||
configs,
|
||||
errorOnUnmatchedPattern
|
||||
});
|
||||
|
||||
return [
|
||||
...new Set([
|
||||
...results,
|
||||
...globbyResults.map(filePath => path.resolve(filePath))
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Results-related Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 isInNodeModules = baseDir && path.dirname(path.relative(baseDir, filePath)).split(path.sep).includes("node_modules");
|
||||
|
||||
if (isInNodeModules) {
|
||||
message = "File ignored by default because it is located under the node_modules directory. Use ignore pattern \"!**/node_modules/\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning.";
|
||||
} else {
|
||||
message = "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning.";
|
||||
}
|
||||
|
||||
return {
|
||||
filePath: path.resolve(filePath),
|
||||
messages: [
|
||||
{
|
||||
ruleId: null,
|
||||
fatal: false,
|
||||
severity: 1,
|
||||
message,
|
||||
nodeType: null
|
||||
}
|
||||
],
|
||||
suppressedMessages: [],
|
||||
errorCount: 0,
|
||||
warningCount: 1,
|
||||
fatalErrorCount: 0,
|
||||
fixableErrorCount: 0,
|
||||
fixableWarningCount: 0
|
||||
};
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Options-related Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
/**
|
||||
* Check if a given value is a valid fix type or not.
|
||||
* @param {any} x The value to check.
|
||||
* @returns {boolean} `true` if `x` is valid fix type.
|
||||
*/
|
||||
function isFixType(x) {
|
||||
return x === "directive" || x === "problem" || x === "suggestion" || x === "layout";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given value is an array of fix types or not.
|
||||
* @param {any} x The value to check.
|
||||
* @returns {boolean} `true` if `x` is an array of fix types.
|
||||
*/
|
||||
function isFixTypeArray(x) {
|
||||
return Array.isArray(x) && x.every(isFixType);
|
||||
}
|
||||
|
||||
/**
|
||||
* The error for invalid options.
|
||||
*/
|
||||
class ESLintInvalidOptionsError extends Error {
|
||||
constructor(messages) {
|
||||
super(`Invalid Options:\n- ${messages.join("\n- ")}`);
|
||||
this.code = "ESLINT_INVALID_OPTIONS";
|
||||
Error.captureStackTrace(this, ESLintInvalidOptionsError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and normalizes options for the wrapped CLIEngine instance.
|
||||
* @param {FlatESLintOptions} options The options to process.
|
||||
* @throws {ESLintInvalidOptionsError} If of any of a variety of type errors.
|
||||
* @returns {FlatESLintOptions} The normalized options.
|
||||
*/
|
||||
function processOptions({
|
||||
allowInlineConfig = true, // ← we cannot use `overrideConfig.noInlineConfig` instead because `allowInlineConfig` has side-effect that suppress warnings that show inline configs are ignored.
|
||||
baseConfig = null,
|
||||
cache = false,
|
||||
cacheLocation = ".eslintcache",
|
||||
cacheStrategy = "metadata",
|
||||
cwd = process.cwd(),
|
||||
errorOnUnmatchedPattern = true,
|
||||
fix = false,
|
||||
fixTypes = null, // ← should be null by default because if it's an array then it suppresses rules that don't have the `meta.type` property.
|
||||
globInputPaths = true,
|
||||
ignore = true,
|
||||
ignorePatterns = null,
|
||||
overrideConfig = null,
|
||||
overrideConfigFile = null,
|
||||
plugins = {},
|
||||
warnIgnored = true,
|
||||
...unknownOptions
|
||||
}) {
|
||||
const errors = [];
|
||||
const unknownOptionKeys = Object.keys(unknownOptions);
|
||||
|
||||
if (unknownOptionKeys.length >= 1) {
|
||||
errors.push(`Unknown options: ${unknownOptionKeys.join(", ")}`);
|
||||
if (unknownOptionKeys.includes("cacheFile")) {
|
||||
errors.push("'cacheFile' has been removed. Please use the 'cacheLocation' option instead.");
|
||||
}
|
||||
if (unknownOptionKeys.includes("configFile")) {
|
||||
errors.push("'configFile' has been removed. Please use the 'overrideConfigFile' option instead.");
|
||||
}
|
||||
if (unknownOptionKeys.includes("envs")) {
|
||||
errors.push("'envs' has been removed.");
|
||||
}
|
||||
if (unknownOptionKeys.includes("extensions")) {
|
||||
errors.push("'extensions' has been removed.");
|
||||
}
|
||||
if (unknownOptionKeys.includes("resolvePluginsRelativeTo")) {
|
||||
errors.push("'resolvePluginsRelativeTo' has been removed.");
|
||||
}
|
||||
if (unknownOptionKeys.includes("globals")) {
|
||||
errors.push("'globals' has been removed. Please use the 'overrideConfig.languageOptions.globals' option instead.");
|
||||
}
|
||||
if (unknownOptionKeys.includes("ignorePath")) {
|
||||
errors.push("'ignorePath' has been removed.");
|
||||
}
|
||||
if (unknownOptionKeys.includes("ignorePattern")) {
|
||||
errors.push("'ignorePattern' has been removed. Please use the 'overrideConfig.ignorePatterns' option instead.");
|
||||
}
|
||||
if (unknownOptionKeys.includes("parser")) {
|
||||
errors.push("'parser' has been removed. Please use the 'overrideConfig.languageOptions.parser' option instead.");
|
||||
}
|
||||
if (unknownOptionKeys.includes("parserOptions")) {
|
||||
errors.push("'parserOptions' has been removed. Please use the 'overrideConfig.languageOptions.parserOptions' option instead.");
|
||||
}
|
||||
if (unknownOptionKeys.includes("rules")) {
|
||||
errors.push("'rules' has been removed. Please use the 'overrideConfig.rules' option instead.");
|
||||
}
|
||||
if (unknownOptionKeys.includes("rulePaths")) {
|
||||
errors.push("'rulePaths' has been removed. Please define your rules using plugins.");
|
||||
}
|
||||
if (unknownOptionKeys.includes("reportUnusedDisableDirectives")) {
|
||||
errors.push("'reportUnusedDisableDirectives' has been removed. Please use the 'overrideConfig.linterOptions.reportUnusedDisableDirectives' option instead.");
|
||||
}
|
||||
}
|
||||
if (typeof allowInlineConfig !== "boolean") {
|
||||
errors.push("'allowInlineConfig' must be a boolean.");
|
||||
}
|
||||
if (typeof baseConfig !== "object") {
|
||||
errors.push("'baseConfig' must be an object or null.");
|
||||
}
|
||||
if (typeof cache !== "boolean") {
|
||||
errors.push("'cache' must be a boolean.");
|
||||
}
|
||||
if (!isNonEmptyString(cacheLocation)) {
|
||||
errors.push("'cacheLocation' must be a non-empty string.");
|
||||
}
|
||||
if (
|
||||
cacheStrategy !== "metadata" &&
|
||||
cacheStrategy !== "content"
|
||||
) {
|
||||
errors.push("'cacheStrategy' must be any of \"metadata\", \"content\".");
|
||||
}
|
||||
if (!isNonEmptyString(cwd) || !path.isAbsolute(cwd)) {
|
||||
errors.push("'cwd' must be an absolute path.");
|
||||
}
|
||||
if (typeof errorOnUnmatchedPattern !== "boolean") {
|
||||
errors.push("'errorOnUnmatchedPattern' must be a boolean.");
|
||||
}
|
||||
if (typeof fix !== "boolean" && typeof fix !== "function") {
|
||||
errors.push("'fix' must be a boolean or a function.");
|
||||
}
|
||||
if (fixTypes !== null && !isFixTypeArray(fixTypes)) {
|
||||
errors.push("'fixTypes' must be an array of any of \"directive\", \"problem\", \"suggestion\", and \"layout\".");
|
||||
}
|
||||
if (typeof globInputPaths !== "boolean") {
|
||||
errors.push("'globInputPaths' must be a boolean.");
|
||||
}
|
||||
if (typeof ignore !== "boolean") {
|
||||
errors.push("'ignore' must be a boolean.");
|
||||
}
|
||||
if (!isArrayOfNonEmptyString(ignorePatterns) && ignorePatterns !== null) {
|
||||
errors.push("'ignorePatterns' must be an array of non-empty strings or null.");
|
||||
}
|
||||
if (typeof overrideConfig !== "object") {
|
||||
errors.push("'overrideConfig' must be an object or null.");
|
||||
}
|
||||
if (!isNonEmptyString(overrideConfigFile) && overrideConfigFile !== null && overrideConfigFile !== true) {
|
||||
errors.push("'overrideConfigFile' must be a non-empty string, null, or true.");
|
||||
}
|
||||
if (typeof plugins !== "object") {
|
||||
errors.push("'plugins' must be an object or null.");
|
||||
} else if (plugins !== null && Object.keys(plugins).includes("")) {
|
||||
errors.push("'plugins' must not include an empty string.");
|
||||
}
|
||||
if (Array.isArray(plugins)) {
|
||||
errors.push("'plugins' doesn't add plugins to configuration to load. Please use the 'overrideConfig.plugins' option instead.");
|
||||
}
|
||||
if (typeof warnIgnored !== "boolean") {
|
||||
errors.push("'warnIgnored' must be a boolean.");
|
||||
}
|
||||
if (errors.length > 0) {
|
||||
throw new ESLintInvalidOptionsError(errors);
|
||||
}
|
||||
|
||||
return {
|
||||
allowInlineConfig,
|
||||
baseConfig,
|
||||
cache,
|
||||
cacheLocation,
|
||||
cacheStrategy,
|
||||
|
||||
// when overrideConfigFile is true that means don't do config file lookup
|
||||
configFile: overrideConfigFile === true ? false : overrideConfigFile,
|
||||
overrideConfig,
|
||||
cwd: path.normalize(cwd),
|
||||
errorOnUnmatchedPattern,
|
||||
fix,
|
||||
fixTypes,
|
||||
globInputPaths,
|
||||
ignore,
|
||||
ignorePatterns,
|
||||
warnIgnored
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Cache-related helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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 in 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;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Exports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
module.exports = {
|
||||
isGlobPattern,
|
||||
findFiles,
|
||||
|
||||
isNonEmptyString,
|
||||
isArrayOfNonEmptyString,
|
||||
|
||||
createIgnoreResult,
|
||||
isErrorMessage,
|
||||
|
||||
processOptions,
|
||||
|
||||
getCacheFile
|
||||
};
|
||||
707
qwen/nodejs/node_modules/eslint/lib/eslint/eslint.js
generated
vendored
Normal file
707
qwen/nodejs/node_modules/eslint/lib/eslint/eslint.js
generated
vendored
Normal file
@@ -0,0 +1,707 @@
|
||||
/**
|
||||
* @fileoverview Main API Class
|
||||
* @author Kai Cataldo
|
||||
* @author Toru Nagashima
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const { promisify } = require("util");
|
||||
const { CLIEngine, getCLIEngineInternalSlots } = require("../cli-engine/cli-engine");
|
||||
const BuiltinRules = require("../rules");
|
||||
const {
|
||||
Legacy: {
|
||||
ConfigOps: {
|
||||
getRuleSeverity
|
||||
}
|
||||
}
|
||||
} = require("@eslint/eslintrc");
|
||||
const { version } = require("../../package.json");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Typedefs
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @typedef {import("../cli-engine/cli-engine").LintReport} CLIEngineLintReport */
|
||||
/** @typedef {import("../shared/types").DeprecatedRuleInfo} DeprecatedRuleInfo */
|
||||
/** @typedef {import("../shared/types").ConfigData} ConfigData */
|
||||
/** @typedef {import("../shared/types").LintMessage} LintMessage */
|
||||
/** @typedef {import("../shared/types").SuppressedLintMessage} SuppressedLintMessage */
|
||||
/** @typedef {import("../shared/types").Plugin} Plugin */
|
||||
/** @typedef {import("../shared/types").Rule} Rule */
|
||||
/** @typedef {import("../shared/types").LintResult} LintResult */
|
||||
/** @typedef {import("../shared/types").ResultsMeta} ResultsMeta */
|
||||
|
||||
/**
|
||||
* The main formatter object.
|
||||
* @typedef LoadedFormatter
|
||||
* @property {(results: LintResult[], resultsMeta: ResultsMeta) => string | Promise<string>} format format function.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The options with which to configure the ESLint instance.
|
||||
* @typedef {Object} ESLintOptions
|
||||
* @property {boolean} [allowInlineConfig] Enable or disable inline configuration comments.
|
||||
* @property {ConfigData} [baseConfig] Base config object, extended by all configs used with this instance
|
||||
* @property {boolean} [cache] Enable result caching.
|
||||
* @property {string} [cacheLocation] The cache file to use instead of .eslintcache.
|
||||
* @property {"metadata" | "content"} [cacheStrategy] The strategy used to detect changed files.
|
||||
* @property {string} [cwd] The value to use for the current working directory.
|
||||
* @property {boolean} [errorOnUnmatchedPattern] If `false` then `ESLint#lintFiles()` doesn't throw even if no target files found. Defaults to `true`.
|
||||
* @property {string[]} [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 {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] False disables use of .eslintignore.
|
||||
* @property {string} [ignorePath] The ignore file to use instead of .eslintignore.
|
||||
* @property {ConfigData} [overrideConfig] Override config object, overrides all configs used with this instance
|
||||
* @property {string} [overrideConfigFile] The configuration file to use.
|
||||
* @property {Record<string,Plugin>|null} [plugins] Preloaded plugins. This is a map-like object, keys are plugin IDs and each value is implementation.
|
||||
* @property {"error" | "warn" | "off"} [reportUnusedDisableDirectives] the severity to report unused eslint-disable directives.
|
||||
* @property {string} [resolvePluginsRelativeTo] The folder where plugins should be resolved from, defaulting to the CWD.
|
||||
* @property {string[]} [rulePaths] An array of directories to load custom rules from.
|
||||
* @property {boolean} [useEslintrc] False disables looking for .eslintrc.* files.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A rules metadata object.
|
||||
* @typedef {Object} RulesMeta
|
||||
* @property {string} id The plugin ID.
|
||||
* @property {Object} definition The plugin definition.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Private members for the `ESLint` instance.
|
||||
* @typedef {Object} ESLintPrivateMembers
|
||||
* @property {CLIEngine} cliEngine The wrapped CLIEngine instance.
|
||||
* @property {ESLintOptions} options The options used to instantiate the ESLint instance.
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const writeFile = promisify(fs.writeFile);
|
||||
|
||||
/**
|
||||
* The map with which to store private class members.
|
||||
* @type {WeakMap<ESLint, ESLintPrivateMembers>}
|
||||
*/
|
||||
const privateMembersMap = new WeakMap();
|
||||
|
||||
/**
|
||||
* Check if a given value is a non-empty string or not.
|
||||
* @param {any} x The value to check.
|
||||
* @returns {boolean} `true` if `x` is a non-empty string.
|
||||
*/
|
||||
function isNonEmptyString(x) {
|
||||
return typeof x === "string" && x.trim() !== "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given value is an array of non-empty strings or not.
|
||||
* @param {any} x The value to check.
|
||||
* @returns {boolean} `true` if `x` is an array of non-empty strings.
|
||||
*/
|
||||
function isArrayOfNonEmptyString(x) {
|
||||
return Array.isArray(x) && x.every(isNonEmptyString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given value is a valid fix type or not.
|
||||
* @param {any} x The value to check.
|
||||
* @returns {boolean} `true` if `x` is valid fix type.
|
||||
*/
|
||||
function isFixType(x) {
|
||||
return x === "directive" || x === "problem" || x === "suggestion" || x === "layout";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given value is an array of fix types or not.
|
||||
* @param {any} x The value to check.
|
||||
* @returns {boolean} `true` if `x` is an array of fix types.
|
||||
*/
|
||||
function isFixTypeArray(x) {
|
||||
return Array.isArray(x) && x.every(isFixType);
|
||||
}
|
||||
|
||||
/**
|
||||
* The error for invalid options.
|
||||
*/
|
||||
class ESLintInvalidOptionsError extends Error {
|
||||
constructor(messages) {
|
||||
super(`Invalid Options:\n- ${messages.join("\n- ")}`);
|
||||
this.code = "ESLINT_INVALID_OPTIONS";
|
||||
Error.captureStackTrace(this, ESLintInvalidOptionsError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and normalizes options for the wrapped CLIEngine instance.
|
||||
* @param {ESLintOptions} options The options to process.
|
||||
* @throws {ESLintInvalidOptionsError} If of any of a variety of type errors.
|
||||
* @returns {ESLintOptions} The normalized options.
|
||||
*/
|
||||
function processOptions({
|
||||
allowInlineConfig = true, // ← we cannot use `overrideConfig.noInlineConfig` instead because `allowInlineConfig` has side-effect that suppress warnings that show inline configs are ignored.
|
||||
baseConfig = null,
|
||||
cache = false,
|
||||
cacheLocation = ".eslintcache",
|
||||
cacheStrategy = "metadata",
|
||||
cwd = process.cwd(),
|
||||
errorOnUnmatchedPattern = true,
|
||||
extensions = null, // ← should be null by default because if it's an array then it suppresses RFC20 feature.
|
||||
fix = false,
|
||||
fixTypes = null, // ← should be null by default because if it's an array then it suppresses rules that don't have the `meta.type` property.
|
||||
globInputPaths = true,
|
||||
ignore = true,
|
||||
ignorePath = null, // ← should be null by default because if it's a string then it may throw ENOENT.
|
||||
overrideConfig = null,
|
||||
overrideConfigFile = null,
|
||||
plugins = {},
|
||||
reportUnusedDisableDirectives = null, // ← should be null by default because if it's a string then it overrides the 'reportUnusedDisableDirectives' setting in config files. And we cannot use `overrideConfig.reportUnusedDisableDirectives` instead because we cannot configure the `error` severity with that.
|
||||
resolvePluginsRelativeTo = null, // ← should be null by default because if it's a string then it suppresses RFC47 feature.
|
||||
rulePaths = [],
|
||||
useEslintrc = true,
|
||||
...unknownOptions
|
||||
}) {
|
||||
const errors = [];
|
||||
const unknownOptionKeys = Object.keys(unknownOptions);
|
||||
|
||||
if (unknownOptionKeys.length >= 1) {
|
||||
errors.push(`Unknown options: ${unknownOptionKeys.join(", ")}`);
|
||||
if (unknownOptionKeys.includes("cacheFile")) {
|
||||
errors.push("'cacheFile' has been removed. Please use the 'cacheLocation' option instead.");
|
||||
}
|
||||
if (unknownOptionKeys.includes("configFile")) {
|
||||
errors.push("'configFile' has been removed. Please use the 'overrideConfigFile' option instead.");
|
||||
}
|
||||
if (unknownOptionKeys.includes("envs")) {
|
||||
errors.push("'envs' has been removed. Please use the 'overrideConfig.env' option instead.");
|
||||
}
|
||||
if (unknownOptionKeys.includes("globals")) {
|
||||
errors.push("'globals' has been removed. Please use the 'overrideConfig.globals' option instead.");
|
||||
}
|
||||
if (unknownOptionKeys.includes("ignorePattern")) {
|
||||
errors.push("'ignorePattern' has been removed. Please use the 'overrideConfig.ignorePatterns' option instead.");
|
||||
}
|
||||
if (unknownOptionKeys.includes("parser")) {
|
||||
errors.push("'parser' has been removed. Please use the 'overrideConfig.parser' option instead.");
|
||||
}
|
||||
if (unknownOptionKeys.includes("parserOptions")) {
|
||||
errors.push("'parserOptions' has been removed. Please use the 'overrideConfig.parserOptions' option instead.");
|
||||
}
|
||||
if (unknownOptionKeys.includes("rules")) {
|
||||
errors.push("'rules' has been removed. Please use the 'overrideConfig.rules' option instead.");
|
||||
}
|
||||
}
|
||||
if (typeof allowInlineConfig !== "boolean") {
|
||||
errors.push("'allowInlineConfig' must be a boolean.");
|
||||
}
|
||||
if (typeof baseConfig !== "object") {
|
||||
errors.push("'baseConfig' must be an object or null.");
|
||||
}
|
||||
if (typeof cache !== "boolean") {
|
||||
errors.push("'cache' must be a boolean.");
|
||||
}
|
||||
if (!isNonEmptyString(cacheLocation)) {
|
||||
errors.push("'cacheLocation' must be a non-empty string.");
|
||||
}
|
||||
if (
|
||||
cacheStrategy !== "metadata" &&
|
||||
cacheStrategy !== "content"
|
||||
) {
|
||||
errors.push("'cacheStrategy' must be any of \"metadata\", \"content\".");
|
||||
}
|
||||
if (!isNonEmptyString(cwd) || !path.isAbsolute(cwd)) {
|
||||
errors.push("'cwd' must be an absolute path.");
|
||||
}
|
||||
if (typeof errorOnUnmatchedPattern !== "boolean") {
|
||||
errors.push("'errorOnUnmatchedPattern' must be a boolean.");
|
||||
}
|
||||
if (!isArrayOfNonEmptyString(extensions) && extensions !== null) {
|
||||
errors.push("'extensions' must be an array of non-empty strings or null.");
|
||||
}
|
||||
if (typeof fix !== "boolean" && typeof fix !== "function") {
|
||||
errors.push("'fix' must be a boolean or a function.");
|
||||
}
|
||||
if (fixTypes !== null && !isFixTypeArray(fixTypes)) {
|
||||
errors.push("'fixTypes' must be an array of any of \"directive\", \"problem\", \"suggestion\", and \"layout\".");
|
||||
}
|
||||
if (typeof globInputPaths !== "boolean") {
|
||||
errors.push("'globInputPaths' must be a boolean.");
|
||||
}
|
||||
if (typeof ignore !== "boolean") {
|
||||
errors.push("'ignore' must be a boolean.");
|
||||
}
|
||||
if (!isNonEmptyString(ignorePath) && ignorePath !== null) {
|
||||
errors.push("'ignorePath' must be a non-empty string or null.");
|
||||
}
|
||||
if (typeof overrideConfig !== "object") {
|
||||
errors.push("'overrideConfig' must be an object or null.");
|
||||
}
|
||||
if (!isNonEmptyString(overrideConfigFile) && overrideConfigFile !== null) {
|
||||
errors.push("'overrideConfigFile' must be a non-empty string or null.");
|
||||
}
|
||||
if (typeof plugins !== "object") {
|
||||
errors.push("'plugins' must be an object or null.");
|
||||
} else if (plugins !== null && Object.keys(plugins).includes("")) {
|
||||
errors.push("'plugins' must not include an empty string.");
|
||||
}
|
||||
if (Array.isArray(plugins)) {
|
||||
errors.push("'plugins' doesn't add plugins to configuration to load. Please use the 'overrideConfig.plugins' option instead.");
|
||||
}
|
||||
if (
|
||||
reportUnusedDisableDirectives !== "error" &&
|
||||
reportUnusedDisableDirectives !== "warn" &&
|
||||
reportUnusedDisableDirectives !== "off" &&
|
||||
reportUnusedDisableDirectives !== null
|
||||
) {
|
||||
errors.push("'reportUnusedDisableDirectives' must be any of \"error\", \"warn\", \"off\", and null.");
|
||||
}
|
||||
if (
|
||||
!isNonEmptyString(resolvePluginsRelativeTo) &&
|
||||
resolvePluginsRelativeTo !== null
|
||||
) {
|
||||
errors.push("'resolvePluginsRelativeTo' must be a non-empty string or null.");
|
||||
}
|
||||
if (!isArrayOfNonEmptyString(rulePaths)) {
|
||||
errors.push("'rulePaths' must be an array of non-empty strings.");
|
||||
}
|
||||
if (typeof useEslintrc !== "boolean") {
|
||||
errors.push("'useEslintrc' must be a boolean.");
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new ESLintInvalidOptionsError(errors);
|
||||
}
|
||||
|
||||
return {
|
||||
allowInlineConfig,
|
||||
baseConfig,
|
||||
cache,
|
||||
cacheLocation,
|
||||
cacheStrategy,
|
||||
configFile: overrideConfigFile,
|
||||
cwd: path.normalize(cwd),
|
||||
errorOnUnmatchedPattern,
|
||||
extensions,
|
||||
fix,
|
||||
fixTypes,
|
||||
globInputPaths,
|
||||
ignore,
|
||||
ignorePath,
|
||||
reportUnusedDisableDirectives,
|
||||
resolvePluginsRelativeTo,
|
||||
rulePaths,
|
||||
useEslintrc
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a value has one or more properties and that value is not undefined.
|
||||
* @param {any} obj The value to check.
|
||||
* @returns {boolean} `true` if `obj` has one or more properties that that value is not undefined.
|
||||
*/
|
||||
function hasDefinedProperty(obj) {
|
||||
if (typeof obj === "object" && obj !== null) {
|
||||
for (const key in obj) {
|
||||
if (typeof obj[key] !== "undefined") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create rulesMeta object.
|
||||
* @param {Map<string,Rule>} rules a map of rules from which to generate the object.
|
||||
* @returns {Object} metadata for all enabled rules.
|
||||
*/
|
||||
function createRulesMeta(rules) {
|
||||
return Array.from(rules).reduce((retVal, [id, rule]) => {
|
||||
retVal[id] = rule.meta;
|
||||
return retVal;
|
||||
}, {});
|
||||
}
|
||||
|
||||
/** @type {WeakMap<ExtractedConfig, DeprecatedRuleInfo[]>} */
|
||||
const usedDeprecatedRulesCache = new WeakMap();
|
||||
|
||||
/**
|
||||
* Create used deprecated rule list.
|
||||
* @param {CLIEngine} cliEngine The CLIEngine instance.
|
||||
* @param {string} maybeFilePath The absolute path to a lint target file or `"<text>"`.
|
||||
* @returns {DeprecatedRuleInfo[]} The used deprecated rule list.
|
||||
*/
|
||||
function getOrFindUsedDeprecatedRules(cliEngine, maybeFilePath) {
|
||||
const {
|
||||
configArrayFactory,
|
||||
options: { cwd }
|
||||
} = getCLIEngineInternalSlots(cliEngine);
|
||||
const filePath = path.isAbsolute(maybeFilePath)
|
||||
? maybeFilePath
|
||||
: path.join(cwd, "__placeholder__.js");
|
||||
const configArray = configArrayFactory.getConfigArrayForFile(filePath);
|
||||
const config = configArray.extractConfig(filePath);
|
||||
|
||||
// Most files use the same config, so cache it.
|
||||
if (!usedDeprecatedRulesCache.has(config)) {
|
||||
const pluginRules = configArray.pluginRules;
|
||||
const retv = [];
|
||||
|
||||
for (const [ruleId, ruleConf] of Object.entries(config.rules)) {
|
||||
if (getRuleSeverity(ruleConf) === 0) {
|
||||
continue;
|
||||
}
|
||||
const rule = pluginRules.get(ruleId) || BuiltinRules.get(ruleId);
|
||||
const meta = rule && rule.meta;
|
||||
|
||||
if (meta && meta.deprecated) {
|
||||
retv.push({ ruleId, replacedBy: meta.replacedBy || [] });
|
||||
}
|
||||
}
|
||||
|
||||
usedDeprecatedRulesCache.set(config, Object.freeze(retv));
|
||||
}
|
||||
|
||||
return usedDeprecatedRulesCache.get(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the linting results generated by a CLIEngine linting report to
|
||||
* match the ESLint class's API.
|
||||
* @param {CLIEngine} cliEngine The CLIEngine instance.
|
||||
* @param {CLIEngineLintReport} report The CLIEngine linting report to process.
|
||||
* @returns {LintResult[]} The processed linting results.
|
||||
*/
|
||||
function processCLIEngineLintReport(cliEngine, { results }) {
|
||||
const descriptor = {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get() {
|
||||
return getOrFindUsedDeprecatedRules(cliEngine, this.filePath);
|
||||
}
|
||||
};
|
||||
|
||||
for (const result of results) {
|
||||
Object.defineProperty(result, "usedDeprecatedRules", descriptor);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* An Array.prototype.sort() compatible compare function to order results by their file path.
|
||||
* @param {LintResult} a The first lint result.
|
||||
* @param {LintResult} b The second lint result.
|
||||
* @returns {number} An integer representing the order in which the two results should occur.
|
||||
*/
|
||||
function compareResultsByFilePath(a, b) {
|
||||
if (a.filePath < b.filePath) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (a.filePath > b.filePath) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main API.
|
||||
*/
|
||||
class ESLint {
|
||||
|
||||
/**
|
||||
* Creates a new instance of the main ESLint API.
|
||||
* @param {ESLintOptions} options The options for this instance.
|
||||
*/
|
||||
constructor(options = {}) {
|
||||
const processedOptions = processOptions(options);
|
||||
const cliEngine = new CLIEngine(processedOptions, { preloadedPlugins: options.plugins });
|
||||
const {
|
||||
configArrayFactory,
|
||||
lastConfigArrays
|
||||
} = getCLIEngineInternalSlots(cliEngine);
|
||||
let updated = false;
|
||||
|
||||
/*
|
||||
* Address `overrideConfig` to set override config.
|
||||
* Operate the `configArrayFactory` internal slot directly because this
|
||||
* functionality doesn't exist as the public API of CLIEngine.
|
||||
*/
|
||||
if (hasDefinedProperty(options.overrideConfig)) {
|
||||
configArrayFactory.setOverrideConfig(options.overrideConfig);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
// Update caches.
|
||||
if (updated) {
|
||||
configArrayFactory.clearCache();
|
||||
lastConfigArrays[0] = configArrayFactory.getConfigArrayForFile();
|
||||
}
|
||||
|
||||
// Initialize private properties.
|
||||
privateMembersMap.set(this, {
|
||||
cliEngine,
|
||||
options: processedOptions
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The version text.
|
||||
* @type {string}
|
||||
*/
|
||||
static get version() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs fixes from the given results to files.
|
||||
* @param {LintResult[]} results The lint results.
|
||||
* @returns {Promise<void>} Returns a promise that is used to track side effects.
|
||||
*/
|
||||
static async outputFixes(results) {
|
||||
if (!Array.isArray(results)) {
|
||||
throw new Error("'results' must be an array");
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
results
|
||||
.filter(result => {
|
||||
if (typeof result !== "object" || result === null) {
|
||||
throw new Error("'results' must include only objects");
|
||||
}
|
||||
return (
|
||||
typeof result.output === "string" &&
|
||||
path.isAbsolute(result.filePath)
|
||||
);
|
||||
})
|
||||
.map(r => writeFile(r.filePath, r.output))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns results that only contains errors.
|
||||
* @param {LintResult[]} results The results to filter.
|
||||
* @returns {LintResult[]} The filtered results.
|
||||
*/
|
||||
static getErrorResults(results) {
|
||||
return CLIEngine.getErrorResults(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns meta objects for each rule represented in the lint results.
|
||||
* @param {LintResult[]} results The results to fetch rules meta for.
|
||||
* @returns {Object} A mapping of ruleIds to rule meta objects.
|
||||
*/
|
||||
getRulesMetaForResults(results) {
|
||||
|
||||
const resultRuleIds = new Set();
|
||||
|
||||
// first gather all ruleIds from all results
|
||||
|
||||
for (const result of results) {
|
||||
for (const { ruleId } of result.messages) {
|
||||
resultRuleIds.add(ruleId);
|
||||
}
|
||||
for (const { ruleId } of result.suppressedMessages) {
|
||||
resultRuleIds.add(ruleId);
|
||||
}
|
||||
}
|
||||
|
||||
// create a map of all rules in the results
|
||||
|
||||
const { cliEngine } = privateMembersMap.get(this);
|
||||
const rules = cliEngine.getRules();
|
||||
const resultRules = new Map();
|
||||
|
||||
for (const [ruleId, rule] of rules) {
|
||||
if (resultRuleIds.has(ruleId)) {
|
||||
resultRules.set(ruleId, rule);
|
||||
}
|
||||
}
|
||||
|
||||
return createRulesMeta(resultRules);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the current configuration on an array of file and directory names.
|
||||
* @param {string[]} patterns An array of file and directory names.
|
||||
* @returns {Promise<LintResult[]>} The results of linting the file patterns given.
|
||||
*/
|
||||
async lintFiles(patterns) {
|
||||
if (!isNonEmptyString(patterns) && !isArrayOfNonEmptyString(patterns)) {
|
||||
throw new Error("'patterns' must be a non-empty string or an array of non-empty strings");
|
||||
}
|
||||
const { cliEngine } = privateMembersMap.get(this);
|
||||
|
||||
return processCLIEngineLintReport(
|
||||
cliEngine,
|
||||
cliEngine.executeOnFiles(patterns)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the current configuration on text.
|
||||
* @param {string} code A string of JavaScript code to lint.
|
||||
* @param {Object} [options] The options.
|
||||
* @param {string} [options.filePath] The path to the file of the source code.
|
||||
* @param {boolean} [options.warnIgnored] When set to true, warn if given filePath is an ignored path.
|
||||
* @returns {Promise<LintResult[]>} The results of linting the string of code given.
|
||||
*/
|
||||
async lintText(code, options = {}) {
|
||||
if (typeof code !== "string") {
|
||||
throw new Error("'code' must be a string");
|
||||
}
|
||||
if (typeof options !== "object") {
|
||||
throw new Error("'options' must be an object, null, or undefined");
|
||||
}
|
||||
const {
|
||||
filePath,
|
||||
warnIgnored = false,
|
||||
...unknownOptions
|
||||
} = options || {};
|
||||
|
||||
const unknownOptionKeys = Object.keys(unknownOptions);
|
||||
|
||||
if (unknownOptionKeys.length > 0) {
|
||||
throw new Error(`'options' must not include the unknown option(s): ${unknownOptionKeys.join(", ")}`);
|
||||
}
|
||||
|
||||
if (filePath !== void 0 && !isNonEmptyString(filePath)) {
|
||||
throw new Error("'options.filePath' must be a non-empty string or undefined");
|
||||
}
|
||||
if (typeof warnIgnored !== "boolean") {
|
||||
throw new Error("'options.warnIgnored' must be a boolean or undefined");
|
||||
}
|
||||
|
||||
const { cliEngine } = privateMembersMap.get(this);
|
||||
|
||||
return processCLIEngineLintReport(
|
||||
cliEngine,
|
||||
cliEngine.executeOnText(code, filePath, warnIgnored)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the formatter representing the given formatter name.
|
||||
* @param {string} [name] The name of the formatter to load.
|
||||
* The following values are allowed:
|
||||
* - `undefined` ... Load `stylish` builtin formatter.
|
||||
* - A builtin formatter name ... Load the builtin formatter.
|
||||
* - A third-party formatter name:
|
||||
* - `foo` → `eslint-formatter-foo`
|
||||
* - `@foo` → `@foo/eslint-formatter`
|
||||
* - `@foo/bar` → `@foo/eslint-formatter-bar`
|
||||
* - A file path ... Load the file.
|
||||
* @returns {Promise<LoadedFormatter>} A promise resolving to the formatter object.
|
||||
* This promise will be rejected if the given formatter was not found or not
|
||||
* a function.
|
||||
*/
|
||||
async loadFormatter(name = "stylish") {
|
||||
if (typeof name !== "string") {
|
||||
throw new Error("'name' must be a string");
|
||||
}
|
||||
|
||||
const { cliEngine, options } = privateMembersMap.get(this);
|
||||
const formatter = cliEngine.getFormatter(name);
|
||||
|
||||
if (typeof formatter !== "function") {
|
||||
throw new Error(`Formatter must be a function, but got a ${typeof formatter}.`);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
/**
|
||||
* The main formatter method.
|
||||
* @param {LintResult[]} results The lint results to format.
|
||||
* @param {ResultsMeta} resultsMeta Warning count and max threshold.
|
||||
* @returns {string | Promise<string>} The formatted lint results.
|
||||
*/
|
||||
format(results, resultsMeta) {
|
||||
let rulesMeta = null;
|
||||
|
||||
results.sort(compareResultsByFilePath);
|
||||
|
||||
return formatter(results, {
|
||||
...resultsMeta,
|
||||
get cwd() {
|
||||
return options.cwd;
|
||||
},
|
||||
get rulesMeta() {
|
||||
if (!rulesMeta) {
|
||||
rulesMeta = createRulesMeta(cliEngine.getRules());
|
||||
}
|
||||
|
||||
return rulesMeta;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @returns {Promise<ConfigData>} A configuration object for the file.
|
||||
*/
|
||||
async calculateConfigForFile(filePath) {
|
||||
if (!isNonEmptyString(filePath)) {
|
||||
throw new Error("'filePath' must be a non-empty string");
|
||||
}
|
||||
const { cliEngine } = privateMembersMap.get(this);
|
||||
|
||||
return cliEngine.getConfigForFile(filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given path is ignored by ESLint.
|
||||
* @param {string} filePath The path of the file to check.
|
||||
* @returns {Promise<boolean>} Whether or not the given path is ignored.
|
||||
*/
|
||||
async isPathIgnored(filePath) {
|
||||
if (!isNonEmptyString(filePath)) {
|
||||
throw new Error("'filePath' must be a non-empty string");
|
||||
}
|
||||
const { cliEngine } = privateMembersMap.get(this);
|
||||
|
||||
return cliEngine.isPathIgnored(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of configuration used by this class.
|
||||
* @type {string}
|
||||
* @static
|
||||
*/
|
||||
ESLint.configType = "eslintrc";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = {
|
||||
ESLint,
|
||||
|
||||
/**
|
||||
* Get the private class members of a given ESLint instance for tests.
|
||||
* @param {ESLint} instance The ESLint instance to get.
|
||||
* @returns {ESLintPrivateMembers} The instance's private class members.
|
||||
*/
|
||||
getESLintPrivateMembers(instance) {
|
||||
return privateMembersMap.get(instance);
|
||||
}
|
||||
};
|
||||
1159
qwen/nodejs/node_modules/eslint/lib/eslint/flat-eslint.js
generated
vendored
Normal file
1159
qwen/nodejs/node_modules/eslint/lib/eslint/flat-eslint.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
9
qwen/nodejs/node_modules/eslint/lib/eslint/index.js
generated
vendored
Normal file
9
qwen/nodejs/node_modules/eslint/lib/eslint/index.js
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
const { ESLint } = require("./eslint");
|
||||
const { FlatESLint } = require("./flat-eslint");
|
||||
|
||||
module.exports = {
|
||||
ESLint,
|
||||
FlatESLint
|
||||
};
|
||||
465
qwen/nodejs/node_modules/eslint/lib/linter/apply-disable-directives.js
generated
vendored
Normal file
465
qwen/nodejs/node_modules/eslint/lib/linter/apply-disable-directives.js
generated
vendored
Normal file
@@ -0,0 +1,465 @@
|
||||
/**
|
||||
* @fileoverview A module that filters reported problems based on `eslint-disable` and `eslint-enable` comments
|
||||
* @author Teddy Katz
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Typedefs
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @typedef {import("../shared/types").LintMessage} LintMessage */
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Module Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const escapeRegExp = require("escape-string-regexp");
|
||||
|
||||
/**
|
||||
* Compares the locations of two objects in a source file
|
||||
* @param {{line: number, column: number}} itemA The first object
|
||||
* @param {{line: number, column: number}} itemB The second object
|
||||
* @returns {number} A value less than 1 if itemA appears before itemB in the source file, greater than 1 if
|
||||
* itemA appears after itemB in the source file, or 0 if itemA and itemB have the same location.
|
||||
*/
|
||||
function compareLocations(itemA, itemB) {
|
||||
return itemA.line - itemB.line || itemA.column - itemB.column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups a set of directives into sub-arrays by their parent comment.
|
||||
* @param {Iterable<Directive>} directives Unused directives to be removed.
|
||||
* @returns {Directive[][]} Directives grouped by their parent comment.
|
||||
*/
|
||||
function groupByParentComment(directives) {
|
||||
const groups = new Map();
|
||||
|
||||
for (const directive of directives) {
|
||||
const { unprocessedDirective: { parentComment } } = directive;
|
||||
|
||||
if (groups.has(parentComment)) {
|
||||
groups.get(parentComment).push(directive);
|
||||
} else {
|
||||
groups.set(parentComment, [directive]);
|
||||
}
|
||||
}
|
||||
|
||||
return [...groups.values()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates removal details for a set of directives within the same comment.
|
||||
* @param {Directive[]} directives Unused directives to be removed.
|
||||
* @param {Token} commentToken The backing Comment token.
|
||||
* @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
|
||||
*/
|
||||
function createIndividualDirectivesRemoval(directives, commentToken) {
|
||||
|
||||
/*
|
||||
* `commentToken.value` starts right after `//` or `/*`.
|
||||
* All calculated offsets will be relative to this index.
|
||||
*/
|
||||
const commentValueStart = commentToken.range[0] + "//".length;
|
||||
|
||||
// Find where the list of rules starts. `\S+` matches with the directive name (e.g. `eslint-disable-line`)
|
||||
const listStartOffset = /^\s*\S+\s+/u.exec(commentToken.value)[0].length;
|
||||
|
||||
/*
|
||||
* Get the list text without any surrounding whitespace. In order to preserve the original
|
||||
* formatting, we don't want to change that whitespace.
|
||||
*
|
||||
* // eslint-disable-line rule-one , rule-two , rule-three -- comment
|
||||
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
*/
|
||||
const listText = commentToken.value
|
||||
.slice(listStartOffset) // remove directive name and all whitespace before the list
|
||||
.split(/\s-{2,}\s/u)[0] // remove `-- comment`, if it exists
|
||||
.trimEnd(); // remove all whitespace after the list
|
||||
|
||||
/*
|
||||
* We can assume that `listText` contains multiple elements.
|
||||
* Otherwise, this function wouldn't be called - if there is
|
||||
* only one rule in the list, then the whole comment must be removed.
|
||||
*/
|
||||
|
||||
return directives.map(directive => {
|
||||
const { ruleId } = directive;
|
||||
|
||||
const regex = new RegExp(String.raw`(?:^|\s*,\s*)(?<quote>['"]?)${escapeRegExp(ruleId)}\k<quote>(?:\s*,\s*|$)`, "u");
|
||||
const match = regex.exec(listText);
|
||||
const matchedText = match[0];
|
||||
const matchStartOffset = listStartOffset + match.index;
|
||||
const matchEndOffset = matchStartOffset + matchedText.length;
|
||||
|
||||
const firstIndexOfComma = matchedText.indexOf(",");
|
||||
const lastIndexOfComma = matchedText.lastIndexOf(",");
|
||||
|
||||
let removalStartOffset, removalEndOffset;
|
||||
|
||||
if (firstIndexOfComma !== lastIndexOfComma) {
|
||||
|
||||
/*
|
||||
* Since there are two commas, this must one of the elements in the middle of the list.
|
||||
* Matched range starts where the previous rule name ends, and ends where the next rule name starts.
|
||||
*
|
||||
* // eslint-disable-line rule-one , rule-two , rule-three -- comment
|
||||
* ^^^^^^^^^^^^^^
|
||||
*
|
||||
* We want to remove only the content between the two commas, and also one of the commas.
|
||||
*
|
||||
* // eslint-disable-line rule-one , rule-two , rule-three -- comment
|
||||
* ^^^^^^^^^^^
|
||||
*/
|
||||
removalStartOffset = matchStartOffset + firstIndexOfComma;
|
||||
removalEndOffset = matchStartOffset + lastIndexOfComma;
|
||||
|
||||
} else {
|
||||
|
||||
/*
|
||||
* This is either the first element or the last element.
|
||||
*
|
||||
* If this is the first element, matched range starts where the first rule name starts
|
||||
* and ends where the second rule name starts. This is exactly the range we want
|
||||
* to remove so that the second rule name will start where the first one was starting
|
||||
* and thus preserve the original formatting.
|
||||
*
|
||||
* // eslint-disable-line rule-one , rule-two , rule-three -- comment
|
||||
* ^^^^^^^^^^^
|
||||
*
|
||||
* Similarly, if this is the last element, we've already matched the range we want to
|
||||
* remove. The previous rule name will end where the last one was ending, relative
|
||||
* to the content on the right side.
|
||||
*
|
||||
* // eslint-disable-line rule-one , rule-two , rule-three -- comment
|
||||
* ^^^^^^^^^^^^^
|
||||
*/
|
||||
removalStartOffset = matchStartOffset;
|
||||
removalEndOffset = matchEndOffset;
|
||||
}
|
||||
|
||||
return {
|
||||
description: `'${ruleId}'`,
|
||||
fix: {
|
||||
range: [
|
||||
commentValueStart + removalStartOffset,
|
||||
commentValueStart + removalEndOffset
|
||||
],
|
||||
text: ""
|
||||
},
|
||||
unprocessedDirective: directive.unprocessedDirective
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a description of deleting an entire unused disable comment.
|
||||
* @param {Directive[]} directives Unused directives to be removed.
|
||||
* @param {Token} commentToken The backing Comment token.
|
||||
* @returns {{ description, fix, unprocessedDirective }} Details for later creation of an output Problem.
|
||||
*/
|
||||
function createCommentRemoval(directives, commentToken) {
|
||||
const { range } = commentToken;
|
||||
const ruleIds = directives.filter(directive => directive.ruleId).map(directive => `'${directive.ruleId}'`);
|
||||
|
||||
return {
|
||||
description: ruleIds.length <= 2
|
||||
? ruleIds.join(" or ")
|
||||
: `${ruleIds.slice(0, ruleIds.length - 1).join(", ")}, or ${ruleIds[ruleIds.length - 1]}`,
|
||||
fix: {
|
||||
range,
|
||||
text: " "
|
||||
},
|
||||
unprocessedDirective: directives[0].unprocessedDirective
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses details from directives to create output Problems.
|
||||
* @param {Iterable<Directive>} allDirectives Unused directives to be removed.
|
||||
* @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
|
||||
*/
|
||||
function processUnusedDirectives(allDirectives) {
|
||||
const directiveGroups = groupByParentComment(allDirectives);
|
||||
|
||||
return directiveGroups.flatMap(
|
||||
directives => {
|
||||
const { parentComment } = directives[0].unprocessedDirective;
|
||||
const remainingRuleIds = new Set(parentComment.ruleIds);
|
||||
|
||||
for (const directive of directives) {
|
||||
remainingRuleIds.delete(directive.ruleId);
|
||||
}
|
||||
|
||||
return remainingRuleIds.size
|
||||
? createIndividualDirectivesRemoval(directives, parentComment.commentToken)
|
||||
: [createCommentRemoval(directives, parentComment.commentToken)];
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect eslint-enable comments that are removing suppressions by eslint-disable comments.
|
||||
* @param {Directive[]} directives The directives to check.
|
||||
* @returns {Set<Directive>} The used eslint-enable comments
|
||||
*/
|
||||
function collectUsedEnableDirectives(directives) {
|
||||
|
||||
/**
|
||||
* A Map of `eslint-enable` keyed by ruleIds that may be marked as used.
|
||||
* If `eslint-enable` does not have a ruleId, the key will be `null`.
|
||||
* @type {Map<string|null, Directive>}
|
||||
*/
|
||||
const enabledRules = new Map();
|
||||
|
||||
/**
|
||||
* A Set of `eslint-enable` marked as used.
|
||||
* It is also the return value of `collectUsedEnableDirectives` function.
|
||||
* @type {Set<Directive>}
|
||||
*/
|
||||
const usedEnableDirectives = new Set();
|
||||
|
||||
/*
|
||||
* Checks the directives backwards to see if the encountered `eslint-enable` is used by the previous `eslint-disable`,
|
||||
* and if so, stores the `eslint-enable` in `usedEnableDirectives`.
|
||||
*/
|
||||
for (let index = directives.length - 1; index >= 0; index--) {
|
||||
const directive = directives[index];
|
||||
|
||||
if (directive.type === "disable") {
|
||||
if (enabledRules.size === 0) {
|
||||
continue;
|
||||
}
|
||||
if (directive.ruleId === null) {
|
||||
|
||||
// If encounter `eslint-disable` without ruleId,
|
||||
// mark all `eslint-enable` currently held in enabledRules as used.
|
||||
// e.g.
|
||||
// /* eslint-disable */ <- current directive
|
||||
// /* eslint-enable rule-id1 */ <- used
|
||||
// /* eslint-enable rule-id2 */ <- used
|
||||
// /* eslint-enable */ <- used
|
||||
for (const enableDirective of enabledRules.values()) {
|
||||
usedEnableDirectives.add(enableDirective);
|
||||
}
|
||||
enabledRules.clear();
|
||||
} else {
|
||||
const enableDirective = enabledRules.get(directive.ruleId);
|
||||
|
||||
if (enableDirective) {
|
||||
|
||||
// If encounter `eslint-disable` with ruleId, and there is an `eslint-enable` with the same ruleId in enabledRules,
|
||||
// mark `eslint-enable` with ruleId as used.
|
||||
// e.g.
|
||||
// /* eslint-disable rule-id */ <- current directive
|
||||
// /* eslint-enable rule-id */ <- used
|
||||
usedEnableDirectives.add(enableDirective);
|
||||
} else {
|
||||
const enabledDirectiveWithoutRuleId = enabledRules.get(null);
|
||||
|
||||
if (enabledDirectiveWithoutRuleId) {
|
||||
|
||||
// If encounter `eslint-disable` with ruleId, and there is no `eslint-enable` with the same ruleId in enabledRules,
|
||||
// mark `eslint-enable` without ruleId as used.
|
||||
// e.g.
|
||||
// /* eslint-disable rule-id */ <- current directive
|
||||
// /* eslint-enable */ <- used
|
||||
usedEnableDirectives.add(enabledDirectiveWithoutRuleId);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (directive.type === "enable") {
|
||||
if (directive.ruleId === null) {
|
||||
|
||||
// If encounter `eslint-enable` without ruleId, the `eslint-enable` that follows it are unused.
|
||||
// So clear enabledRules.
|
||||
// e.g.
|
||||
// /* eslint-enable */ <- current directive
|
||||
// /* eslint-enable rule-id *// <- unused
|
||||
// /* eslint-enable */ <- unused
|
||||
enabledRules.clear();
|
||||
enabledRules.set(null, directive);
|
||||
} else {
|
||||
enabledRules.set(directive.ruleId, directive);
|
||||
}
|
||||
}
|
||||
}
|
||||
return usedEnableDirectives;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the same as the exported function, except that it
|
||||
* doesn't handle disable-line and disable-next-line directives, and it always reports unused
|
||||
* disable directives.
|
||||
* @param {Object} options options for applying directives. This is the same as the options
|
||||
* for the exported function, except that `reportUnusedDisableDirectives` is not supported
|
||||
* (this function always reports unused disable directives).
|
||||
* @returns {{problems: LintMessage[], unusedDirectives: LintMessage[]}} An object with a list
|
||||
* of problems (including suppressed ones) and unused eslint-disable directives
|
||||
*/
|
||||
function applyDirectives(options) {
|
||||
const problems = [];
|
||||
const usedDisableDirectives = new Set();
|
||||
|
||||
for (const problem of options.problems) {
|
||||
let disableDirectivesForProblem = [];
|
||||
let nextDirectiveIndex = 0;
|
||||
|
||||
while (
|
||||
nextDirectiveIndex < options.directives.length &&
|
||||
compareLocations(options.directives[nextDirectiveIndex], problem) <= 0
|
||||
) {
|
||||
const directive = options.directives[nextDirectiveIndex++];
|
||||
|
||||
if (directive.ruleId === null || directive.ruleId === problem.ruleId) {
|
||||
switch (directive.type) {
|
||||
case "disable":
|
||||
disableDirectivesForProblem.push(directive);
|
||||
break;
|
||||
|
||||
case "enable":
|
||||
disableDirectivesForProblem = [];
|
||||
break;
|
||||
|
||||
// no default
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (disableDirectivesForProblem.length > 0) {
|
||||
const suppressions = disableDirectivesForProblem.map(directive => ({
|
||||
kind: "directive",
|
||||
justification: directive.unprocessedDirective.justification
|
||||
}));
|
||||
|
||||
if (problem.suppressions) {
|
||||
problem.suppressions = problem.suppressions.concat(suppressions);
|
||||
} else {
|
||||
problem.suppressions = suppressions;
|
||||
usedDisableDirectives.add(disableDirectivesForProblem[disableDirectivesForProblem.length - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
problems.push(problem);
|
||||
}
|
||||
|
||||
const unusedDisableDirectivesToReport = options.directives
|
||||
.filter(directive => directive.type === "disable" && !usedDisableDirectives.has(directive));
|
||||
|
||||
|
||||
const unusedEnableDirectivesToReport = new Set(
|
||||
options.directives.filter(directive => directive.unprocessedDirective.type === "enable")
|
||||
);
|
||||
|
||||
/*
|
||||
* If directives has the eslint-enable directive,
|
||||
* check whether the eslint-enable comment is used.
|
||||
*/
|
||||
if (unusedEnableDirectivesToReport.size > 0) {
|
||||
for (const directive of collectUsedEnableDirectives(options.directives)) {
|
||||
unusedEnableDirectivesToReport.delete(directive);
|
||||
}
|
||||
}
|
||||
|
||||
const processed = processUnusedDirectives(unusedDisableDirectivesToReport)
|
||||
.concat(processUnusedDirectives(unusedEnableDirectivesToReport));
|
||||
|
||||
const unusedDirectives = processed
|
||||
.map(({ description, fix, unprocessedDirective }) => {
|
||||
const { parentComment, type, line, column } = unprocessedDirective;
|
||||
|
||||
let message;
|
||||
|
||||
if (type === "enable") {
|
||||
message = description
|
||||
? `Unused eslint-enable directive (no matching eslint-disable directives were found for ${description}).`
|
||||
: "Unused eslint-enable directive (no matching eslint-disable directives were found).";
|
||||
} else {
|
||||
message = description
|
||||
? `Unused eslint-disable directive (no problems were reported from ${description}).`
|
||||
: "Unused eslint-disable directive (no problems were reported).";
|
||||
}
|
||||
return {
|
||||
ruleId: null,
|
||||
message,
|
||||
line: type === "disable-next-line" ? parentComment.commentToken.loc.start.line : line,
|
||||
column: type === "disable-next-line" ? parentComment.commentToken.loc.start.column + 1 : column,
|
||||
severity: options.reportUnusedDisableDirectives === "warn" ? 1 : 2,
|
||||
nodeType: null,
|
||||
...options.disableFixes ? {} : { fix }
|
||||
};
|
||||
});
|
||||
|
||||
return { problems, unusedDirectives };
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of directive comments (i.e. metadata about eslint-disable and eslint-enable comments) and a list
|
||||
* of reported problems, adds the suppression information to the problems.
|
||||
* @param {Object} options Information about directives and problems
|
||||
* @param {{
|
||||
* type: ("disable"|"enable"|"disable-line"|"disable-next-line"),
|
||||
* ruleId: (string|null),
|
||||
* line: number,
|
||||
* column: number,
|
||||
* justification: string
|
||||
* }} options.directives Directive comments found in the file, with one-based columns.
|
||||
* Two directive comments can only have the same location if they also have the same type (e.g. a single eslint-disable
|
||||
* comment for two different rules is represented as two directives).
|
||||
* @param {{ruleId: (string|null), line: number, column: number}[]} options.problems
|
||||
* A list of problems reported by rules, sorted by increasing location in the file, with one-based columns.
|
||||
* @param {"off" | "warn" | "error"} options.reportUnusedDisableDirectives If `"warn"` or `"error"`, adds additional problems for unused directives
|
||||
* @param {boolean} options.disableFixes If true, it doesn't make `fix` properties.
|
||||
* @returns {{ruleId: (string|null), line: number, column: number, suppressions?: {kind: string, justification: string}}[]}
|
||||
* An object with a list of reported problems, the suppressed of which contain the suppression information.
|
||||
*/
|
||||
module.exports = ({ directives, disableFixes, problems, reportUnusedDisableDirectives = "off" }) => {
|
||||
const blockDirectives = directives
|
||||
.filter(directive => directive.type === "disable" || directive.type === "enable")
|
||||
.map(directive => Object.assign({}, directive, { unprocessedDirective: directive }))
|
||||
.sort(compareLocations);
|
||||
|
||||
const lineDirectives = directives.flatMap(directive => {
|
||||
switch (directive.type) {
|
||||
case "disable":
|
||||
case "enable":
|
||||
return [];
|
||||
|
||||
case "disable-line":
|
||||
return [
|
||||
{ type: "disable", line: directive.line, column: 1, ruleId: directive.ruleId, unprocessedDirective: directive },
|
||||
{ type: "enable", line: directive.line + 1, column: 0, ruleId: directive.ruleId, unprocessedDirective: directive }
|
||||
];
|
||||
|
||||
case "disable-next-line":
|
||||
return [
|
||||
{ type: "disable", line: directive.line + 1, column: 1, ruleId: directive.ruleId, unprocessedDirective: directive },
|
||||
{ type: "enable", line: directive.line + 2, column: 0, ruleId: directive.ruleId, unprocessedDirective: directive }
|
||||
];
|
||||
|
||||
default:
|
||||
throw new TypeError(`Unrecognized directive type '${directive.type}'`);
|
||||
}
|
||||
}).sort(compareLocations);
|
||||
|
||||
const blockDirectivesResult = applyDirectives({
|
||||
problems,
|
||||
directives: blockDirectives,
|
||||
disableFixes,
|
||||
reportUnusedDisableDirectives
|
||||
});
|
||||
const lineDirectivesResult = applyDirectives({
|
||||
problems: blockDirectivesResult.problems,
|
||||
directives: lineDirectives,
|
||||
disableFixes,
|
||||
reportUnusedDisableDirectives
|
||||
});
|
||||
|
||||
return reportUnusedDisableDirectives !== "off"
|
||||
? lineDirectivesResult.problems
|
||||
.concat(blockDirectivesResult.unusedDirectives)
|
||||
.concat(lineDirectivesResult.unusedDirectives)
|
||||
.sort(compareLocations)
|
||||
: lineDirectivesResult.problems;
|
||||
};
|
||||
852
qwen/nodejs/node_modules/eslint/lib/linter/code-path-analysis/code-path-analyzer.js
generated
vendored
Normal file
852
qwen/nodejs/node_modules/eslint/lib/linter/code-path-analysis/code-path-analyzer.js
generated
vendored
Normal file
@@ -0,0 +1,852 @@
|
||||
/**
|
||||
* @fileoverview A class of the code path analyzer.
|
||||
* @author Toru Nagashima
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const assert = require("assert"),
|
||||
{ breakableTypePattern } = require("../../shared/ast-utils"),
|
||||
CodePath = require("./code-path"),
|
||||
CodePathSegment = require("./code-path-segment"),
|
||||
IdGenerator = require("./id-generator"),
|
||||
debug = require("./debug-helpers");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks whether or not a given node is a `case` node (not `default` node).
|
||||
* @param {ASTNode} node A `SwitchCase` node to check.
|
||||
* @returns {boolean} `true` if the node is a `case` node (not `default` node).
|
||||
*/
|
||||
function isCaseNode(node) {
|
||||
return Boolean(node.test);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given node appears as the value of a PropertyDefinition node.
|
||||
* @param {ASTNode} node THe node to check.
|
||||
* @returns {boolean} `true` if the node is a PropertyDefinition value,
|
||||
* false if not.
|
||||
*/
|
||||
function isPropertyDefinitionValue(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
return parent && parent.type === "PropertyDefinition" && parent.value === node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given logical operator is taken into account for the code
|
||||
* path analysis.
|
||||
* @param {string} operator The operator found in the LogicalExpression node
|
||||
* @returns {boolean} `true` if the operator is "&&" or "||" or "??"
|
||||
*/
|
||||
function isHandledLogicalOperator(operator) {
|
||||
return operator === "&&" || operator === "||" || operator === "??";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given assignment operator is a logical assignment operator.
|
||||
* Logical assignments are taken into account for the code path analysis
|
||||
* because of their short-circuiting semantics.
|
||||
* @param {string} operator The operator found in the AssignmentExpression node
|
||||
* @returns {boolean} `true` if the operator is "&&=" or "||=" or "??="
|
||||
*/
|
||||
function isLogicalAssignmentOperator(operator) {
|
||||
return operator === "&&=" || operator === "||=" || operator === "??=";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the label if the parent node of a given node is a LabeledStatement.
|
||||
* @param {ASTNode} node A node to get.
|
||||
* @returns {string|null} The label or `null`.
|
||||
*/
|
||||
function getLabel(node) {
|
||||
if (node.parent.type === "LabeledStatement") {
|
||||
return node.parent.label.name;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a given logical expression node goes different path
|
||||
* between the `true` case and the `false` case.
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @returns {boolean} `true` if the node is a test of a choice statement.
|
||||
*/
|
||||
function isForkingByTrueOrFalse(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
switch (parent.type) {
|
||||
case "ConditionalExpression":
|
||||
case "IfStatement":
|
||||
case "WhileStatement":
|
||||
case "DoWhileStatement":
|
||||
case "ForStatement":
|
||||
return parent.test === node;
|
||||
|
||||
case "LogicalExpression":
|
||||
return isHandledLogicalOperator(parent.operator);
|
||||
|
||||
case "AssignmentExpression":
|
||||
return isLogicalAssignmentOperator(parent.operator);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the boolean value of a given literal node.
|
||||
*
|
||||
* This is used to detect infinity loops (e.g. `while (true) {}`).
|
||||
* Statements preceded by an infinity loop are unreachable if the loop didn't
|
||||
* have any `break` statement.
|
||||
* @param {ASTNode} node A node to get.
|
||||
* @returns {boolean|undefined} a boolean value if the node is a Literal node,
|
||||
* otherwise `undefined`.
|
||||
*/
|
||||
function getBooleanValueIfSimpleConstant(node) {
|
||||
if (node.type === "Literal") {
|
||||
return Boolean(node.value);
|
||||
}
|
||||
return void 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that a given identifier node is a reference or not.
|
||||
*
|
||||
* This is used to detect the first throwable node in a `try` block.
|
||||
* @param {ASTNode} node An Identifier node to check.
|
||||
* @returns {boolean} `true` if the node is a reference.
|
||||
*/
|
||||
function isIdentifierReference(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
switch (parent.type) {
|
||||
case "LabeledStatement":
|
||||
case "BreakStatement":
|
||||
case "ContinueStatement":
|
||||
case "ArrayPattern":
|
||||
case "RestElement":
|
||||
case "ImportSpecifier":
|
||||
case "ImportDefaultSpecifier":
|
||||
case "ImportNamespaceSpecifier":
|
||||
case "CatchClause":
|
||||
return false;
|
||||
|
||||
case "FunctionDeclaration":
|
||||
case "FunctionExpression":
|
||||
case "ArrowFunctionExpression":
|
||||
case "ClassDeclaration":
|
||||
case "ClassExpression":
|
||||
case "VariableDeclarator":
|
||||
return parent.id !== node;
|
||||
|
||||
case "Property":
|
||||
case "PropertyDefinition":
|
||||
case "MethodDefinition":
|
||||
return (
|
||||
parent.key !== node ||
|
||||
parent.computed ||
|
||||
parent.shorthand
|
||||
);
|
||||
|
||||
case "AssignmentPattern":
|
||||
return parent.key !== node;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current segment with the head segment.
|
||||
* This is similar to local branches and tracking branches of git.
|
||||
*
|
||||
* To separate the current and the head is in order to not make useless segments.
|
||||
*
|
||||
* In this process, both "onCodePathSegmentStart" and "onCodePathSegmentEnd"
|
||||
* events are fired.
|
||||
* @param {CodePathAnalyzer} analyzer The instance.
|
||||
* @param {ASTNode} node The current AST node.
|
||||
* @returns {void}
|
||||
*/
|
||||
function forwardCurrentToHead(analyzer, node) {
|
||||
const codePath = analyzer.codePath;
|
||||
const state = CodePath.getState(codePath);
|
||||
const currentSegments = state.currentSegments;
|
||||
const headSegments = state.headSegments;
|
||||
const end = Math.max(currentSegments.length, headSegments.length);
|
||||
let i, currentSegment, headSegment;
|
||||
|
||||
// Fires leaving events.
|
||||
for (i = 0; i < end; ++i) {
|
||||
currentSegment = currentSegments[i];
|
||||
headSegment = headSegments[i];
|
||||
|
||||
if (currentSegment !== headSegment && currentSegment) {
|
||||
|
||||
const eventName = currentSegment.reachable
|
||||
? "onCodePathSegmentEnd"
|
||||
: "onUnreachableCodePathSegmentEnd";
|
||||
|
||||
debug.dump(`${eventName} ${currentSegment.id}`);
|
||||
|
||||
analyzer.emitter.emit(
|
||||
eventName,
|
||||
currentSegment,
|
||||
node
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Update state.
|
||||
state.currentSegments = headSegments;
|
||||
|
||||
// Fires entering events.
|
||||
for (i = 0; i < end; ++i) {
|
||||
currentSegment = currentSegments[i];
|
||||
headSegment = headSegments[i];
|
||||
|
||||
if (currentSegment !== headSegment && headSegment) {
|
||||
|
||||
const eventName = headSegment.reachable
|
||||
? "onCodePathSegmentStart"
|
||||
: "onUnreachableCodePathSegmentStart";
|
||||
|
||||
debug.dump(`${eventName} ${headSegment.id}`);
|
||||
|
||||
CodePathSegment.markUsed(headSegment);
|
||||
analyzer.emitter.emit(
|
||||
eventName,
|
||||
headSegment,
|
||||
node
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current segment with empty.
|
||||
* This is called at the last of functions or the program.
|
||||
* @param {CodePathAnalyzer} analyzer The instance.
|
||||
* @param {ASTNode} node The current AST node.
|
||||
* @returns {void}
|
||||
*/
|
||||
function leaveFromCurrentSegment(analyzer, node) {
|
||||
const state = CodePath.getState(analyzer.codePath);
|
||||
const currentSegments = state.currentSegments;
|
||||
|
||||
for (let i = 0; i < currentSegments.length; ++i) {
|
||||
const currentSegment = currentSegments[i];
|
||||
const eventName = currentSegment.reachable
|
||||
? "onCodePathSegmentEnd"
|
||||
: "onUnreachableCodePathSegmentEnd";
|
||||
|
||||
debug.dump(`${eventName} ${currentSegment.id}`);
|
||||
|
||||
analyzer.emitter.emit(
|
||||
eventName,
|
||||
currentSegment,
|
||||
node
|
||||
);
|
||||
}
|
||||
|
||||
state.currentSegments = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the code path due to the position of a given node in the parent node
|
||||
* thereof.
|
||||
*
|
||||
* For example, if the node is `parent.consequent`, this creates a fork from the
|
||||
* current path.
|
||||
* @param {CodePathAnalyzer} analyzer The instance.
|
||||
* @param {ASTNode} node The current AST node.
|
||||
* @returns {void}
|
||||
*/
|
||||
function preprocess(analyzer, node) {
|
||||
const codePath = analyzer.codePath;
|
||||
const state = CodePath.getState(codePath);
|
||||
const parent = node.parent;
|
||||
|
||||
switch (parent.type) {
|
||||
|
||||
// The `arguments.length == 0` case is in `postprocess` function.
|
||||
case "CallExpression":
|
||||
if (parent.optional === true && parent.arguments.length >= 1 && parent.arguments[0] === node) {
|
||||
state.makeOptionalRight();
|
||||
}
|
||||
break;
|
||||
case "MemberExpression":
|
||||
if (parent.optional === true && parent.property === node) {
|
||||
state.makeOptionalRight();
|
||||
}
|
||||
break;
|
||||
|
||||
case "LogicalExpression":
|
||||
if (
|
||||
parent.right === node &&
|
||||
isHandledLogicalOperator(parent.operator)
|
||||
) {
|
||||
state.makeLogicalRight();
|
||||
}
|
||||
break;
|
||||
|
||||
case "AssignmentExpression":
|
||||
if (
|
||||
parent.right === node &&
|
||||
isLogicalAssignmentOperator(parent.operator)
|
||||
) {
|
||||
state.makeLogicalRight();
|
||||
}
|
||||
break;
|
||||
|
||||
case "ConditionalExpression":
|
||||
case "IfStatement":
|
||||
|
||||
/*
|
||||
* Fork if this node is at `consequent`/`alternate`.
|
||||
* `popForkContext()` exists at `IfStatement:exit` and
|
||||
* `ConditionalExpression:exit`.
|
||||
*/
|
||||
if (parent.consequent === node) {
|
||||
state.makeIfConsequent();
|
||||
} else if (parent.alternate === node) {
|
||||
state.makeIfAlternate();
|
||||
}
|
||||
break;
|
||||
|
||||
case "SwitchCase":
|
||||
if (parent.consequent[0] === node) {
|
||||
state.makeSwitchCaseBody(false, !parent.test);
|
||||
}
|
||||
break;
|
||||
|
||||
case "TryStatement":
|
||||
if (parent.handler === node) {
|
||||
state.makeCatchBlock();
|
||||
} else if (parent.finalizer === node) {
|
||||
state.makeFinallyBlock();
|
||||
}
|
||||
break;
|
||||
|
||||
case "WhileStatement":
|
||||
if (parent.test === node) {
|
||||
state.makeWhileTest(getBooleanValueIfSimpleConstant(node));
|
||||
} else {
|
||||
assert(parent.body === node);
|
||||
state.makeWhileBody();
|
||||
}
|
||||
break;
|
||||
|
||||
case "DoWhileStatement":
|
||||
if (parent.body === node) {
|
||||
state.makeDoWhileBody();
|
||||
} else {
|
||||
assert(parent.test === node);
|
||||
state.makeDoWhileTest(getBooleanValueIfSimpleConstant(node));
|
||||
}
|
||||
break;
|
||||
|
||||
case "ForStatement":
|
||||
if (parent.test === node) {
|
||||
state.makeForTest(getBooleanValueIfSimpleConstant(node));
|
||||
} else if (parent.update === node) {
|
||||
state.makeForUpdate();
|
||||
} else if (parent.body === node) {
|
||||
state.makeForBody();
|
||||
}
|
||||
break;
|
||||
|
||||
case "ForInStatement":
|
||||
case "ForOfStatement":
|
||||
if (parent.left === node) {
|
||||
state.makeForInOfLeft();
|
||||
} else if (parent.right === node) {
|
||||
state.makeForInOfRight();
|
||||
} else {
|
||||
assert(parent.body === node);
|
||||
state.makeForInOfBody();
|
||||
}
|
||||
break;
|
||||
|
||||
case "AssignmentPattern":
|
||||
|
||||
/*
|
||||
* Fork if this node is at `right`.
|
||||
* `left` is executed always, so it uses the current path.
|
||||
* `popForkContext()` exists at `AssignmentPattern:exit`.
|
||||
*/
|
||||
if (parent.right === node) {
|
||||
state.pushForkContext();
|
||||
state.forkBypassPath();
|
||||
state.forkPath();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the code path due to the type of a given node in entering.
|
||||
* @param {CodePathAnalyzer} analyzer The instance.
|
||||
* @param {ASTNode} node The current AST node.
|
||||
* @returns {void}
|
||||
*/
|
||||
function processCodePathToEnter(analyzer, node) {
|
||||
let codePath = analyzer.codePath;
|
||||
let state = codePath && CodePath.getState(codePath);
|
||||
const parent = node.parent;
|
||||
|
||||
/**
|
||||
* Creates a new code path and trigger the onCodePathStart event
|
||||
* based on the currently selected node.
|
||||
* @param {string} origin The reason the code path was started.
|
||||
* @returns {void}
|
||||
*/
|
||||
function startCodePath(origin) {
|
||||
if (codePath) {
|
||||
|
||||
// Emits onCodePathSegmentStart events if updated.
|
||||
forwardCurrentToHead(analyzer, node);
|
||||
debug.dumpState(node, state, false);
|
||||
}
|
||||
|
||||
// Create the code path of this scope.
|
||||
codePath = analyzer.codePath = new CodePath({
|
||||
id: analyzer.idGenerator.next(),
|
||||
origin,
|
||||
upper: codePath,
|
||||
onLooped: analyzer.onLooped
|
||||
});
|
||||
state = CodePath.getState(codePath);
|
||||
|
||||
// Emits onCodePathStart events.
|
||||
debug.dump(`onCodePathStart ${codePath.id}`);
|
||||
analyzer.emitter.emit("onCodePathStart", codePath, node);
|
||||
}
|
||||
|
||||
/*
|
||||
* Special case: The right side of class field initializer is considered
|
||||
* to be its own function, so we need to start a new code path in this
|
||||
* case.
|
||||
*/
|
||||
if (isPropertyDefinitionValue(node)) {
|
||||
startCodePath("class-field-initializer");
|
||||
|
||||
/*
|
||||
* Intentional fall through because `node` needs to also be
|
||||
* processed by the code below. For example, if we have:
|
||||
*
|
||||
* class Foo {
|
||||
* a = () => {}
|
||||
* }
|
||||
*
|
||||
* In this case, we also need start a second code path.
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
switch (node.type) {
|
||||
case "Program":
|
||||
startCodePath("program");
|
||||
break;
|
||||
|
||||
case "FunctionDeclaration":
|
||||
case "FunctionExpression":
|
||||
case "ArrowFunctionExpression":
|
||||
startCodePath("function");
|
||||
break;
|
||||
|
||||
case "StaticBlock":
|
||||
startCodePath("class-static-block");
|
||||
break;
|
||||
|
||||
case "ChainExpression":
|
||||
state.pushChainContext();
|
||||
break;
|
||||
case "CallExpression":
|
||||
if (node.optional === true) {
|
||||
state.makeOptionalNode();
|
||||
}
|
||||
break;
|
||||
case "MemberExpression":
|
||||
if (node.optional === true) {
|
||||
state.makeOptionalNode();
|
||||
}
|
||||
break;
|
||||
|
||||
case "LogicalExpression":
|
||||
if (isHandledLogicalOperator(node.operator)) {
|
||||
state.pushChoiceContext(
|
||||
node.operator,
|
||||
isForkingByTrueOrFalse(node)
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case "AssignmentExpression":
|
||||
if (isLogicalAssignmentOperator(node.operator)) {
|
||||
state.pushChoiceContext(
|
||||
node.operator.slice(0, -1), // removes `=` from the end
|
||||
isForkingByTrueOrFalse(node)
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case "ConditionalExpression":
|
||||
case "IfStatement":
|
||||
state.pushChoiceContext("test", false);
|
||||
break;
|
||||
|
||||
case "SwitchStatement":
|
||||
state.pushSwitchContext(
|
||||
node.cases.some(isCaseNode),
|
||||
getLabel(node)
|
||||
);
|
||||
break;
|
||||
|
||||
case "TryStatement":
|
||||
state.pushTryContext(Boolean(node.finalizer));
|
||||
break;
|
||||
|
||||
case "SwitchCase":
|
||||
|
||||
/*
|
||||
* Fork if this node is after the 2st node in `cases`.
|
||||
* It's similar to `else` blocks.
|
||||
* The next `test` node is processed in this path.
|
||||
*/
|
||||
if (parent.discriminant !== node && parent.cases[0] !== node) {
|
||||
state.forkPath();
|
||||
}
|
||||
break;
|
||||
|
||||
case "WhileStatement":
|
||||
case "DoWhileStatement":
|
||||
case "ForStatement":
|
||||
case "ForInStatement":
|
||||
case "ForOfStatement":
|
||||
state.pushLoopContext(node.type, getLabel(node));
|
||||
break;
|
||||
|
||||
case "LabeledStatement":
|
||||
if (!breakableTypePattern.test(node.body.type)) {
|
||||
state.pushBreakContext(false, node.label.name);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Emits onCodePathSegmentStart events if updated.
|
||||
forwardCurrentToHead(analyzer, node);
|
||||
debug.dumpState(node, state, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the code path due to the type of a given node in leaving.
|
||||
* @param {CodePathAnalyzer} analyzer The instance.
|
||||
* @param {ASTNode} node The current AST node.
|
||||
* @returns {void}
|
||||
*/
|
||||
function processCodePathToExit(analyzer, node) {
|
||||
|
||||
const codePath = analyzer.codePath;
|
||||
const state = CodePath.getState(codePath);
|
||||
let dontForward = false;
|
||||
|
||||
switch (node.type) {
|
||||
case "ChainExpression":
|
||||
state.popChainContext();
|
||||
break;
|
||||
|
||||
case "IfStatement":
|
||||
case "ConditionalExpression":
|
||||
state.popChoiceContext();
|
||||
break;
|
||||
|
||||
case "LogicalExpression":
|
||||
if (isHandledLogicalOperator(node.operator)) {
|
||||
state.popChoiceContext();
|
||||
}
|
||||
break;
|
||||
|
||||
case "AssignmentExpression":
|
||||
if (isLogicalAssignmentOperator(node.operator)) {
|
||||
state.popChoiceContext();
|
||||
}
|
||||
break;
|
||||
|
||||
case "SwitchStatement":
|
||||
state.popSwitchContext();
|
||||
break;
|
||||
|
||||
case "SwitchCase":
|
||||
|
||||
/*
|
||||
* This is the same as the process at the 1st `consequent` node in
|
||||
* `preprocess` function.
|
||||
* Must do if this `consequent` is empty.
|
||||
*/
|
||||
if (node.consequent.length === 0) {
|
||||
state.makeSwitchCaseBody(true, !node.test);
|
||||
}
|
||||
if (state.forkContext.reachable) {
|
||||
dontForward = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case "TryStatement":
|
||||
state.popTryContext();
|
||||
break;
|
||||
|
||||
case "BreakStatement":
|
||||
forwardCurrentToHead(analyzer, node);
|
||||
state.makeBreak(node.label && node.label.name);
|
||||
dontForward = true;
|
||||
break;
|
||||
|
||||
case "ContinueStatement":
|
||||
forwardCurrentToHead(analyzer, node);
|
||||
state.makeContinue(node.label && node.label.name);
|
||||
dontForward = true;
|
||||
break;
|
||||
|
||||
case "ReturnStatement":
|
||||
forwardCurrentToHead(analyzer, node);
|
||||
state.makeReturn();
|
||||
dontForward = true;
|
||||
break;
|
||||
|
||||
case "ThrowStatement":
|
||||
forwardCurrentToHead(analyzer, node);
|
||||
state.makeThrow();
|
||||
dontForward = true;
|
||||
break;
|
||||
|
||||
case "Identifier":
|
||||
if (isIdentifierReference(node)) {
|
||||
state.makeFirstThrowablePathInTryBlock();
|
||||
dontForward = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case "CallExpression":
|
||||
case "ImportExpression":
|
||||
case "MemberExpression":
|
||||
case "NewExpression":
|
||||
case "YieldExpression":
|
||||
state.makeFirstThrowablePathInTryBlock();
|
||||
break;
|
||||
|
||||
case "WhileStatement":
|
||||
case "DoWhileStatement":
|
||||
case "ForStatement":
|
||||
case "ForInStatement":
|
||||
case "ForOfStatement":
|
||||
state.popLoopContext();
|
||||
break;
|
||||
|
||||
case "AssignmentPattern":
|
||||
state.popForkContext();
|
||||
break;
|
||||
|
||||
case "LabeledStatement":
|
||||
if (!breakableTypePattern.test(node.body.type)) {
|
||||
state.popBreakContext();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Emits onCodePathSegmentStart events if updated.
|
||||
if (!dontForward) {
|
||||
forwardCurrentToHead(analyzer, node);
|
||||
}
|
||||
debug.dumpState(node, state, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the code path to finalize the current code path.
|
||||
* @param {CodePathAnalyzer} analyzer The instance.
|
||||
* @param {ASTNode} node The current AST node.
|
||||
* @returns {void}
|
||||
*/
|
||||
function postprocess(analyzer, node) {
|
||||
|
||||
/**
|
||||
* Ends the code path for the current node.
|
||||
* @returns {void}
|
||||
*/
|
||||
function endCodePath() {
|
||||
let codePath = analyzer.codePath;
|
||||
|
||||
// Mark the current path as the final node.
|
||||
CodePath.getState(codePath).makeFinal();
|
||||
|
||||
// Emits onCodePathSegmentEnd event of the current segments.
|
||||
leaveFromCurrentSegment(analyzer, node);
|
||||
|
||||
// Emits onCodePathEnd event of this code path.
|
||||
debug.dump(`onCodePathEnd ${codePath.id}`);
|
||||
analyzer.emitter.emit("onCodePathEnd", codePath, node);
|
||||
debug.dumpDot(codePath);
|
||||
|
||||
codePath = analyzer.codePath = analyzer.codePath.upper;
|
||||
if (codePath) {
|
||||
debug.dumpState(node, CodePath.getState(codePath), true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
switch (node.type) {
|
||||
case "Program":
|
||||
case "FunctionDeclaration":
|
||||
case "FunctionExpression":
|
||||
case "ArrowFunctionExpression":
|
||||
case "StaticBlock": {
|
||||
endCodePath();
|
||||
break;
|
||||
}
|
||||
|
||||
// The `arguments.length >= 1` case is in `preprocess` function.
|
||||
case "CallExpression":
|
||||
if (node.optional === true && node.arguments.length === 0) {
|
||||
CodePath.getState(analyzer.codePath).makeOptionalRight();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Special case: The right side of class field initializer is considered
|
||||
* to be its own function, so we need to end a code path in this
|
||||
* case.
|
||||
*
|
||||
* We need to check after the other checks in order to close the
|
||||
* code paths in the correct order for code like this:
|
||||
*
|
||||
*
|
||||
* class Foo {
|
||||
* a = () => {}
|
||||
* }
|
||||
*
|
||||
* In this case, The ArrowFunctionExpression code path is closed first
|
||||
* and then we need to close the code path for the PropertyDefinition
|
||||
* value.
|
||||
*/
|
||||
if (isPropertyDefinitionValue(node)) {
|
||||
endCodePath();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The class to analyze code paths.
|
||||
* This class implements the EventGenerator interface.
|
||||
*/
|
||||
class CodePathAnalyzer {
|
||||
|
||||
/**
|
||||
* @param {EventGenerator} eventGenerator An event generator to wrap.
|
||||
*/
|
||||
constructor(eventGenerator) {
|
||||
this.original = eventGenerator;
|
||||
this.emitter = eventGenerator.emitter;
|
||||
this.codePath = null;
|
||||
this.idGenerator = new IdGenerator("s");
|
||||
this.currentNode = null;
|
||||
this.onLooped = this.onLooped.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the process to enter a given AST node.
|
||||
* This updates state of analysis and calls `enterNode` of the wrapped.
|
||||
* @param {ASTNode} node A node which is entering.
|
||||
* @returns {void}
|
||||
*/
|
||||
enterNode(node) {
|
||||
this.currentNode = node;
|
||||
|
||||
// Updates the code path due to node's position in its parent node.
|
||||
if (node.parent) {
|
||||
preprocess(this, node);
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates the code path.
|
||||
* And emits onCodePathStart/onCodePathSegmentStart events.
|
||||
*/
|
||||
processCodePathToEnter(this, node);
|
||||
|
||||
// Emits node events.
|
||||
this.original.enterNode(node);
|
||||
|
||||
this.currentNode = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the process to leave a given AST node.
|
||||
* This updates state of analysis and calls `leaveNode` of the wrapped.
|
||||
* @param {ASTNode} node A node which is leaving.
|
||||
* @returns {void}
|
||||
*/
|
||||
leaveNode(node) {
|
||||
this.currentNode = node;
|
||||
|
||||
/*
|
||||
* Updates the code path.
|
||||
* And emits onCodePathStart/onCodePathSegmentStart events.
|
||||
*/
|
||||
processCodePathToExit(this, node);
|
||||
|
||||
// Emits node events.
|
||||
this.original.leaveNode(node);
|
||||
|
||||
// Emits the last onCodePathStart/onCodePathSegmentStart events.
|
||||
postprocess(this, node);
|
||||
|
||||
this.currentNode = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called on a code path looped.
|
||||
* Then this raises a looped event.
|
||||
* @param {CodePathSegment} fromSegment A segment of prev.
|
||||
* @param {CodePathSegment} toSegment A segment of next.
|
||||
* @returns {void}
|
||||
*/
|
||||
onLooped(fromSegment, toSegment) {
|
||||
if (fromSegment.reachable && toSegment.reachable) {
|
||||
debug.dump(`onCodePathSegmentLoop ${fromSegment.id} -> ${toSegment.id}`);
|
||||
this.emitter.emit(
|
||||
"onCodePathSegmentLoop",
|
||||
fromSegment,
|
||||
toSegment,
|
||||
this.currentNode
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CodePathAnalyzer;
|
||||
263
qwen/nodejs/node_modules/eslint/lib/linter/code-path-analysis/code-path-segment.js
generated
vendored
Normal file
263
qwen/nodejs/node_modules/eslint/lib/linter/code-path-analysis/code-path-segment.js
generated
vendored
Normal file
@@ -0,0 +1,263 @@
|
||||
/**
|
||||
* @fileoverview The CodePathSegment class.
|
||||
* @author Toru Nagashima
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const debug = require("./debug-helpers");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks whether or not a given segment is reachable.
|
||||
* @param {CodePathSegment} segment A segment to check.
|
||||
* @returns {boolean} `true` if the segment is reachable.
|
||||
*/
|
||||
function isReachable(segment) {
|
||||
return segment.reachable;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A code path segment.
|
||||
*
|
||||
* Each segment is arranged in a series of linked lists (implemented by arrays)
|
||||
* that keep track of the previous and next segments in a code path. In this way,
|
||||
* you can navigate between all segments in any code path so long as you have a
|
||||
* reference to any segment in that code path.
|
||||
*
|
||||
* When first created, the segment is in a detached state, meaning that it knows the
|
||||
* segments that came before it but those segments don't know that this new segment
|
||||
* follows it. Only when `CodePathSegment#markUsed()` is called on a segment does it
|
||||
* officially become part of the code path by updating the previous segments to know
|
||||
* that this new segment follows.
|
||||
*/
|
||||
class CodePathSegment {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {string} id An identifier.
|
||||
* @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
|
||||
* This array includes unreachable segments.
|
||||
* @param {boolean} reachable A flag which shows this is reachable.
|
||||
*/
|
||||
constructor(id, allPrevSegments, reachable) {
|
||||
|
||||
/**
|
||||
* The identifier of this code path.
|
||||
* Rules use it to store additional information of each rule.
|
||||
* @type {string}
|
||||
*/
|
||||
this.id = id;
|
||||
|
||||
/**
|
||||
* An array of the next reachable segments.
|
||||
* @type {CodePathSegment[]}
|
||||
*/
|
||||
this.nextSegments = [];
|
||||
|
||||
/**
|
||||
* An array of the previous reachable segments.
|
||||
* @type {CodePathSegment[]}
|
||||
*/
|
||||
this.prevSegments = allPrevSegments.filter(isReachable);
|
||||
|
||||
/**
|
||||
* An array of all next segments including reachable and unreachable.
|
||||
* @type {CodePathSegment[]}
|
||||
*/
|
||||
this.allNextSegments = [];
|
||||
|
||||
/**
|
||||
* An array of all previous segments including reachable and unreachable.
|
||||
* @type {CodePathSegment[]}
|
||||
*/
|
||||
this.allPrevSegments = allPrevSegments;
|
||||
|
||||
/**
|
||||
* A flag which shows this is reachable.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.reachable = reachable;
|
||||
|
||||
// Internal data.
|
||||
Object.defineProperty(this, "internal", {
|
||||
value: {
|
||||
|
||||
// determines if the segment has been attached to the code path
|
||||
used: false,
|
||||
|
||||
// array of previous segments coming from the end of a loop
|
||||
loopedPrevSegments: []
|
||||
}
|
||||
});
|
||||
|
||||
/* c8 ignore start */
|
||||
if (debug.enabled) {
|
||||
this.internal.nodes = [];
|
||||
}/* c8 ignore stop */
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a given previous segment is coming from the end of a loop.
|
||||
* @param {CodePathSegment} segment A previous segment to check.
|
||||
* @returns {boolean} `true` if the segment is coming from the end of a loop.
|
||||
*/
|
||||
isLoopedPrevSegment(segment) {
|
||||
return this.internal.loopedPrevSegments.includes(segment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the root segment.
|
||||
* @param {string} id An identifier.
|
||||
* @returns {CodePathSegment} The created segment.
|
||||
*/
|
||||
static newRoot(id) {
|
||||
return new CodePathSegment(id, [], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new segment and appends it after the given segments.
|
||||
* @param {string} id An identifier.
|
||||
* @param {CodePathSegment[]} allPrevSegments An array of the previous segments
|
||||
* to append to.
|
||||
* @returns {CodePathSegment} The created segment.
|
||||
*/
|
||||
static newNext(id, allPrevSegments) {
|
||||
return new CodePathSegment(
|
||||
id,
|
||||
CodePathSegment.flattenUnusedSegments(allPrevSegments),
|
||||
allPrevSegments.some(isReachable)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an unreachable segment and appends it after the given segments.
|
||||
* @param {string} id An identifier.
|
||||
* @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
|
||||
* @returns {CodePathSegment} The created segment.
|
||||
*/
|
||||
static newUnreachable(id, allPrevSegments) {
|
||||
const segment = new CodePathSegment(id, CodePathSegment.flattenUnusedSegments(allPrevSegments), false);
|
||||
|
||||
/*
|
||||
* In `if (a) return a; foo();` case, the unreachable segment preceded by
|
||||
* the return statement is not used but must not be removed.
|
||||
*/
|
||||
CodePathSegment.markUsed(segment);
|
||||
|
||||
return segment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a segment that follows given segments.
|
||||
* This factory method does not connect with `allPrevSegments`.
|
||||
* But this inherits `reachable` flag.
|
||||
* @param {string} id An identifier.
|
||||
* @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
|
||||
* @returns {CodePathSegment} The created segment.
|
||||
*/
|
||||
static newDisconnected(id, allPrevSegments) {
|
||||
return new CodePathSegment(id, [], allPrevSegments.some(isReachable));
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a given segment as used.
|
||||
*
|
||||
* And this function registers the segment into the previous segments as a next.
|
||||
* @param {CodePathSegment} segment A segment to mark.
|
||||
* @returns {void}
|
||||
*/
|
||||
static markUsed(segment) {
|
||||
if (segment.internal.used) {
|
||||
return;
|
||||
}
|
||||
segment.internal.used = true;
|
||||
|
||||
let i;
|
||||
|
||||
if (segment.reachable) {
|
||||
|
||||
/*
|
||||
* If the segment is reachable, then it's officially part of the
|
||||
* code path. This loops through all previous segments to update
|
||||
* their list of next segments. Because the segment is reachable,
|
||||
* it's added to both `nextSegments` and `allNextSegments`.
|
||||
*/
|
||||
for (i = 0; i < segment.allPrevSegments.length; ++i) {
|
||||
const prevSegment = segment.allPrevSegments[i];
|
||||
|
||||
prevSegment.allNextSegments.push(segment);
|
||||
prevSegment.nextSegments.push(segment);
|
||||
}
|
||||
} else {
|
||||
|
||||
/*
|
||||
* If the segment is not reachable, then it's not officially part of the
|
||||
* code path. This loops through all previous segments to update
|
||||
* their list of next segments. Because the segment is not reachable,
|
||||
* it's added only to `allNextSegments`.
|
||||
*/
|
||||
for (i = 0; i < segment.allPrevSegments.length; ++i) {
|
||||
segment.allPrevSegments[i].allNextSegments.push(segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a previous segment as looped.
|
||||
* @param {CodePathSegment} segment A segment.
|
||||
* @param {CodePathSegment} prevSegment A previous segment to mark.
|
||||
* @returns {void}
|
||||
*/
|
||||
static markPrevSegmentAsLooped(segment, prevSegment) {
|
||||
segment.internal.loopedPrevSegments.push(prevSegment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new array based on an array of segments. If any segment in the
|
||||
* array is unused, then it is replaced by all of its previous segments.
|
||||
* All used segments are returned as-is without replacement.
|
||||
* @param {CodePathSegment[]} segments The array of segments to flatten.
|
||||
* @returns {CodePathSegment[]} The flattened array.
|
||||
*/
|
||||
static flattenUnusedSegments(segments) {
|
||||
const done = new Set();
|
||||
|
||||
for (let i = 0; i < segments.length; ++i) {
|
||||
const segment = segments[i];
|
||||
|
||||
// Ignores duplicated.
|
||||
if (done.has(segment)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use previous segments if unused.
|
||||
if (!segment.internal.used) {
|
||||
for (let j = 0; j < segment.allPrevSegments.length; ++j) {
|
||||
const prevSegment = segment.allPrevSegments[j];
|
||||
|
||||
if (!done.has(prevSegment)) {
|
||||
done.add(prevSegment);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
done.add(segment);
|
||||
}
|
||||
}
|
||||
|
||||
return [...done];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CodePathSegment;
|
||||
2348
qwen/nodejs/node_modules/eslint/lib/linter/code-path-analysis/code-path-state.js
generated
vendored
Normal file
2348
qwen/nodejs/node_modules/eslint/lib/linter/code-path-analysis/code-path-state.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
342
qwen/nodejs/node_modules/eslint/lib/linter/code-path-analysis/code-path.js
generated
vendored
Normal file
342
qwen/nodejs/node_modules/eslint/lib/linter/code-path-analysis/code-path.js
generated
vendored
Normal file
@@ -0,0 +1,342 @@
|
||||
/**
|
||||
* @fileoverview A class of the code path.
|
||||
* @author Toru Nagashima
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const CodePathState = require("./code-path-state");
|
||||
const IdGenerator = require("./id-generator");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A code path.
|
||||
*/
|
||||
class CodePath {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {Object} options Options for the function (see below).
|
||||
* @param {string} options.id An identifier.
|
||||
* @param {string} options.origin The type of code path origin.
|
||||
* @param {CodePath|null} options.upper The code path of the upper function scope.
|
||||
* @param {Function} options.onLooped A callback function to notify looping.
|
||||
*/
|
||||
constructor({ id, origin, upper, onLooped }) {
|
||||
|
||||
/**
|
||||
* The identifier of this code path.
|
||||
* Rules use it to store additional information of each rule.
|
||||
* @type {string}
|
||||
*/
|
||||
this.id = id;
|
||||
|
||||
/**
|
||||
* The reason that this code path was started. May be "program",
|
||||
* "function", "class-field-initializer", or "class-static-block".
|
||||
* @type {string}
|
||||
*/
|
||||
this.origin = origin;
|
||||
|
||||
/**
|
||||
* The code path of the upper function scope.
|
||||
* @type {CodePath|null}
|
||||
*/
|
||||
this.upper = upper;
|
||||
|
||||
/**
|
||||
* The code paths of nested function scopes.
|
||||
* @type {CodePath[]}
|
||||
*/
|
||||
this.childCodePaths = [];
|
||||
|
||||
// Initializes internal state.
|
||||
Object.defineProperty(
|
||||
this,
|
||||
"internal",
|
||||
{ value: new CodePathState(new IdGenerator(`${id}_`), onLooped) }
|
||||
);
|
||||
|
||||
// Adds this into `childCodePaths` of `upper`.
|
||||
if (upper) {
|
||||
upper.childCodePaths.push(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state of a given code path.
|
||||
* @param {CodePath} codePath A code path to get.
|
||||
* @returns {CodePathState} The state of the code path.
|
||||
*/
|
||||
static getState(codePath) {
|
||||
return codePath.internal;
|
||||
}
|
||||
|
||||
/**
|
||||
* The initial code path segment. This is the segment that is at the head
|
||||
* of the code path.
|
||||
* This is a passthrough to the underlying `CodePathState`.
|
||||
* @type {CodePathSegment}
|
||||
*/
|
||||
get initialSegment() {
|
||||
return this.internal.initialSegment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Final code path segments. These are the terminal (tail) segments in the
|
||||
* code path, which is the combination of `returnedSegments` and `thrownSegments`.
|
||||
* All segments in this array are reachable.
|
||||
* This is a passthrough to the underlying `CodePathState`.
|
||||
* @type {CodePathSegment[]}
|
||||
*/
|
||||
get finalSegments() {
|
||||
return this.internal.finalSegments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Final code path segments that represent normal completion of the code path.
|
||||
* For functions, this means both explicit `return` statements and implicit returns,
|
||||
* such as the last reachable segment in a function that does not have an
|
||||
* explicit `return` as this implicitly returns `undefined`. For scripts,
|
||||
* modules, class field initializers, and class static blocks, this means
|
||||
* all lines of code have been executed.
|
||||
* These segments are also present in `finalSegments`.
|
||||
* This is a passthrough to the underlying `CodePathState`.
|
||||
* @type {CodePathSegment[]}
|
||||
*/
|
||||
get returnedSegments() {
|
||||
return this.internal.returnedForkContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Final code path segments that represent `throw` statements.
|
||||
* This is a passthrough to the underlying `CodePathState`.
|
||||
* These segments are also present in `finalSegments`.
|
||||
* @type {CodePathSegment[]}
|
||||
*/
|
||||
get thrownSegments() {
|
||||
return this.internal.thrownForkContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks the traversal of the code path through each segment. This array
|
||||
* starts empty and segments are added or removed as the code path is
|
||||
* traversed. This array always ends up empty at the end of a code path
|
||||
* traversal. The `CodePathState` uses this to track its progress through
|
||||
* the code path.
|
||||
* This is a passthrough to the underlying `CodePathState`.
|
||||
* @type {CodePathSegment[]}
|
||||
* @deprecated
|
||||
*/
|
||||
get currentSegments() {
|
||||
return this.internal.currentSegments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses all segments in this code path.
|
||||
*
|
||||
* codePath.traverseSegments((segment, controller) => {
|
||||
* // do something.
|
||||
* });
|
||||
*
|
||||
* This method enumerates segments in order from the head.
|
||||
*
|
||||
* The `controller` argument has two methods:
|
||||
*
|
||||
* - `skip()` - skips the following segments in this branch
|
||||
* - `break()` - skips all following segments in the traversal
|
||||
*
|
||||
* A note on the parameters: the `options` argument is optional. This means
|
||||
* the first argument might be an options object or the callback function.
|
||||
* @param {Object} [optionsOrCallback] Optional first and last segments to traverse.
|
||||
* @param {CodePathSegment} [optionsOrCallback.first] The first segment to traverse.
|
||||
* @param {CodePathSegment} [optionsOrCallback.last] The last segment to traverse.
|
||||
* @param {Function} callback A callback function.
|
||||
* @returns {void}
|
||||
*/
|
||||
traverseSegments(optionsOrCallback, callback) {
|
||||
|
||||
// normalize the arguments into a callback and options
|
||||
let resolvedOptions;
|
||||
let resolvedCallback;
|
||||
|
||||
if (typeof optionsOrCallback === "function") {
|
||||
resolvedCallback = optionsOrCallback;
|
||||
resolvedOptions = {};
|
||||
} else {
|
||||
resolvedOptions = optionsOrCallback || {};
|
||||
resolvedCallback = callback;
|
||||
}
|
||||
|
||||
// determine where to start traversing from based on the options
|
||||
const startSegment = resolvedOptions.first || this.internal.initialSegment;
|
||||
const lastSegment = resolvedOptions.last;
|
||||
|
||||
// set up initial location information
|
||||
let record = null;
|
||||
let index = 0;
|
||||
let end = 0;
|
||||
let segment = null;
|
||||
|
||||
// segments that have already been visited during traversal
|
||||
const visited = new Set();
|
||||
|
||||
// tracks the traversal steps
|
||||
const stack = [[startSegment, 0]];
|
||||
|
||||
// tracks the last skipped segment during traversal
|
||||
let skippedSegment = null;
|
||||
|
||||
// indicates if we exited early from the traversal
|
||||
let broken = false;
|
||||
|
||||
/**
|
||||
* Maintains traversal state.
|
||||
*/
|
||||
const controller = {
|
||||
|
||||
/**
|
||||
* Skip the following segments in this branch.
|
||||
* @returns {void}
|
||||
*/
|
||||
skip() {
|
||||
if (stack.length <= 1) {
|
||||
broken = true;
|
||||
} else {
|
||||
skippedSegment = stack[stack.length - 2][0];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Stop traversal completely - do not traverse to any
|
||||
* other segments.
|
||||
* @returns {void}
|
||||
*/
|
||||
break() {
|
||||
broken = true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a given previous segment has been visited.
|
||||
* @param {CodePathSegment} prevSegment A previous segment to check.
|
||||
* @returns {boolean} `true` if the segment has been visited.
|
||||
*/
|
||||
function isVisited(prevSegment) {
|
||||
return (
|
||||
visited.has(prevSegment) ||
|
||||
segment.isLoopedPrevSegment(prevSegment)
|
||||
);
|
||||
}
|
||||
|
||||
// the traversal
|
||||
while (stack.length > 0) {
|
||||
|
||||
/*
|
||||
* This isn't a pure stack. We use the top record all the time
|
||||
* but don't always pop it off. The record is popped only if
|
||||
* one of the following is true:
|
||||
*
|
||||
* 1) We have already visited the segment.
|
||||
* 2) We have not visited *all* of the previous segments.
|
||||
* 3) We have traversed past the available next segments.
|
||||
*
|
||||
* Otherwise, we just read the value and sometimes modify the
|
||||
* record as we traverse.
|
||||
*/
|
||||
record = stack[stack.length - 1];
|
||||
segment = record[0];
|
||||
index = record[1];
|
||||
|
||||
if (index === 0) {
|
||||
|
||||
// Skip if this segment has been visited already.
|
||||
if (visited.has(segment)) {
|
||||
stack.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if all previous segments have not been visited.
|
||||
if (segment !== startSegment &&
|
||||
segment.prevSegments.length > 0 &&
|
||||
!segment.prevSegments.every(isVisited)
|
||||
) {
|
||||
stack.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reset the skipping flag if all branches have been skipped.
|
||||
if (skippedSegment && segment.prevSegments.includes(skippedSegment)) {
|
||||
skippedSegment = null;
|
||||
}
|
||||
visited.add(segment);
|
||||
|
||||
/*
|
||||
* If the most recent segment hasn't been skipped, then we call
|
||||
* the callback, passing in the segment and the controller.
|
||||
*/
|
||||
if (!skippedSegment) {
|
||||
resolvedCallback.call(this, segment, controller);
|
||||
|
||||
// exit if we're at the last segment
|
||||
if (segment === lastSegment) {
|
||||
controller.skip();
|
||||
}
|
||||
|
||||
/*
|
||||
* If the previous statement was executed, or if the callback
|
||||
* called a method on the controller, we might need to exit the
|
||||
* loop, so check for that and break accordingly.
|
||||
*/
|
||||
if (broken) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the stack.
|
||||
end = segment.nextSegments.length - 1;
|
||||
if (index < end) {
|
||||
|
||||
/*
|
||||
* If we haven't yet visited all of the next segments, update
|
||||
* the current top record on the stack to the next index to visit
|
||||
* and then push a record for the current segment on top.
|
||||
*
|
||||
* Setting the current top record's index lets us know how many
|
||||
* times we've been here and ensures that the segment won't be
|
||||
* reprocessed (because we only process segments with an index
|
||||
* of 0).
|
||||
*/
|
||||
record[1] += 1;
|
||||
stack.push([segment.nextSegments[index], 0]);
|
||||
} else if (index === end) {
|
||||
|
||||
/*
|
||||
* If we are at the last next segment, then reset the top record
|
||||
* in the stack to next segment and set its index to 0 so it will
|
||||
* be processed next.
|
||||
*/
|
||||
record[0] = segment.nextSegments[index];
|
||||
record[1] = 0;
|
||||
} else {
|
||||
|
||||
/*
|
||||
* If index > end, that means we have no more segments that need
|
||||
* processing. So, we pop that record off of the stack in order to
|
||||
* continue traversing at the next level up.
|
||||
*/
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CodePath;
|
||||
203
qwen/nodejs/node_modules/eslint/lib/linter/code-path-analysis/debug-helpers.js
generated
vendored
Normal file
203
qwen/nodejs/node_modules/eslint/lib/linter/code-path-analysis/debug-helpers.js
generated
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
/**
|
||||
* @fileoverview Helpers to debug for code path analysis.
|
||||
* @author Toru Nagashima
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const debug = require("debug")("eslint:code-path");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Gets id of a given segment.
|
||||
* @param {CodePathSegment} segment A segment to get.
|
||||
* @returns {string} Id of the segment.
|
||||
*/
|
||||
/* c8 ignore next */
|
||||
function getId(segment) { // eslint-disable-line jsdoc/require-jsdoc -- Ignoring
|
||||
return segment.id + (segment.reachable ? "" : "!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string for the given node and operation.
|
||||
* @param {ASTNode} node The node to convert.
|
||||
* @param {"enter" | "exit" | undefined} label The operation label.
|
||||
* @returns {string} The string representation.
|
||||
*/
|
||||
function nodeToString(node, label) {
|
||||
const suffix = label ? `:${label}` : "";
|
||||
|
||||
switch (node.type) {
|
||||
case "Identifier": return `${node.type}${suffix} (${node.name})`;
|
||||
case "Literal": return `${node.type}${suffix} (${node.value})`;
|
||||
default: return `${node.type}${suffix}`;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
* A flag that debug dumping is enabled or not.
|
||||
* @type {boolean}
|
||||
*/
|
||||
enabled: debug.enabled,
|
||||
|
||||
/**
|
||||
* Dumps given objects.
|
||||
* @param {...any} args objects to dump.
|
||||
* @returns {void}
|
||||
*/
|
||||
dump: debug,
|
||||
|
||||
/**
|
||||
* Dumps the current analyzing state.
|
||||
* @param {ASTNode} node A node to dump.
|
||||
* @param {CodePathState} state A state to dump.
|
||||
* @param {boolean} leaving A flag whether or not it's leaving
|
||||
* @returns {void}
|
||||
*/
|
||||
dumpState: !debug.enabled ? debug : /* c8 ignore next */ function(node, state, leaving) {
|
||||
for (let i = 0; i < state.currentSegments.length; ++i) {
|
||||
const segInternal = state.currentSegments[i].internal;
|
||||
|
||||
if (leaving) {
|
||||
const last = segInternal.nodes.length - 1;
|
||||
|
||||
if (last >= 0 && segInternal.nodes[last] === nodeToString(node, "enter")) {
|
||||
segInternal.nodes[last] = nodeToString(node, void 0);
|
||||
} else {
|
||||
segInternal.nodes.push(nodeToString(node, "exit"));
|
||||
}
|
||||
} else {
|
||||
segInternal.nodes.push(nodeToString(node, "enter"));
|
||||
}
|
||||
}
|
||||
|
||||
debug([
|
||||
`${state.currentSegments.map(getId).join(",")})`,
|
||||
`${node.type}${leaving ? ":exit" : ""}`
|
||||
].join(" "));
|
||||
},
|
||||
|
||||
/**
|
||||
* Dumps a DOT code of a given code path.
|
||||
* The DOT code can be visualized with Graphvis.
|
||||
* @param {CodePath} codePath A code path to dump.
|
||||
* @returns {void}
|
||||
* @see http://www.graphviz.org
|
||||
* @see http://www.webgraphviz.com
|
||||
*/
|
||||
dumpDot: !debug.enabled ? debug : /* c8 ignore next */ function(codePath) {
|
||||
let text =
|
||||
"\n" +
|
||||
"digraph {\n" +
|
||||
"node[shape=box,style=\"rounded,filled\",fillcolor=white];\n" +
|
||||
"initial[label=\"\",shape=circle,style=filled,fillcolor=black,width=0.25,height=0.25];\n";
|
||||
|
||||
if (codePath.returnedSegments.length > 0) {
|
||||
text += "final[label=\"\",shape=doublecircle,style=filled,fillcolor=black,width=0.25,height=0.25];\n";
|
||||
}
|
||||
if (codePath.thrownSegments.length > 0) {
|
||||
text += "thrown[label=\"✘\",shape=circle,width=0.3,height=0.3,fixedsize=true];\n";
|
||||
}
|
||||
|
||||
const traceMap = Object.create(null);
|
||||
const arrows = this.makeDotArrows(codePath, traceMap);
|
||||
|
||||
for (const id in traceMap) { // eslint-disable-line guard-for-in -- Want ability to traverse prototype
|
||||
const segment = traceMap[id];
|
||||
|
||||
text += `${id}[`;
|
||||
|
||||
if (segment.reachable) {
|
||||
text += "label=\"";
|
||||
} else {
|
||||
text += "style=\"rounded,dashed,filled\",fillcolor=\"#FF9800\",label=\"<<unreachable>>\\n";
|
||||
}
|
||||
|
||||
if (segment.internal.nodes.length > 0) {
|
||||
text += segment.internal.nodes.join("\\n");
|
||||
} else {
|
||||
text += "????";
|
||||
}
|
||||
|
||||
text += "\"];\n";
|
||||
}
|
||||
|
||||
text += `${arrows}\n`;
|
||||
text += "}";
|
||||
debug("DOT", text);
|
||||
},
|
||||
|
||||
/**
|
||||
* Makes a DOT code of a given code path.
|
||||
* The DOT code can be visualized with Graphvis.
|
||||
* @param {CodePath} codePath A code path to make DOT.
|
||||
* @param {Object} traceMap Optional. A map to check whether or not segments had been done.
|
||||
* @returns {string} A DOT code of the code path.
|
||||
*/
|
||||
makeDotArrows(codePath, traceMap) {
|
||||
const stack = [[codePath.initialSegment, 0]];
|
||||
const done = traceMap || Object.create(null);
|
||||
let lastId = codePath.initialSegment.id;
|
||||
let text = `initial->${codePath.initialSegment.id}`;
|
||||
|
||||
while (stack.length > 0) {
|
||||
const item = stack.pop();
|
||||
const segment = item[0];
|
||||
const index = item[1];
|
||||
|
||||
if (done[segment.id] && index === 0) {
|
||||
continue;
|
||||
}
|
||||
done[segment.id] = segment;
|
||||
|
||||
const nextSegment = segment.allNextSegments[index];
|
||||
|
||||
if (!nextSegment) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lastId === segment.id) {
|
||||
text += `->${nextSegment.id}`;
|
||||
} else {
|
||||
text += `;\n${segment.id}->${nextSegment.id}`;
|
||||
}
|
||||
lastId = nextSegment.id;
|
||||
|
||||
stack.unshift([segment, 1 + index]);
|
||||
stack.push([nextSegment, 0]);
|
||||
}
|
||||
|
||||
codePath.returnedSegments.forEach(finalSegment => {
|
||||
if (lastId === finalSegment.id) {
|
||||
text += "->final";
|
||||
} else {
|
||||
text += `;\n${finalSegment.id}->final`;
|
||||
}
|
||||
lastId = null;
|
||||
});
|
||||
|
||||
codePath.thrownSegments.forEach(finalSegment => {
|
||||
if (lastId === finalSegment.id) {
|
||||
text += "->thrown";
|
||||
} else {
|
||||
text += `;\n${finalSegment.id}->thrown`;
|
||||
}
|
||||
lastId = null;
|
||||
});
|
||||
|
||||
return `${text};`;
|
||||
}
|
||||
};
|
||||
349
qwen/nodejs/node_modules/eslint/lib/linter/code-path-analysis/fork-context.js
generated
vendored
Normal file
349
qwen/nodejs/node_modules/eslint/lib/linter/code-path-analysis/fork-context.js
generated
vendored
Normal file
@@ -0,0 +1,349 @@
|
||||
/**
|
||||
* @fileoverview A class to operate forking.
|
||||
*
|
||||
* This is state of forking.
|
||||
* This has a fork list and manages it.
|
||||
*
|
||||
* @author Toru Nagashima
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const assert = require("assert"),
|
||||
CodePathSegment = require("./code-path-segment");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines whether or not a given segment is reachable.
|
||||
* @param {CodePathSegment} segment The segment to check.
|
||||
* @returns {boolean} `true` if the segment is reachable.
|
||||
*/
|
||||
function isReachable(segment) {
|
||||
return segment.reachable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new segment for each fork in the given context and appends it
|
||||
* to the end of the specified range of segments. Ultimately, this ends up calling
|
||||
* `new CodePathSegment()` for each of the forks using the `create` argument
|
||||
* as a wrapper around special behavior.
|
||||
*
|
||||
* The `startIndex` and `endIndex` arguments specify a range of segments in
|
||||
* `context` that should become `allPrevSegments` for the newly created
|
||||
* `CodePathSegment` objects.
|
||||
*
|
||||
* When `context.segmentsList` is `[[a, b], [c, d], [e, f]]`, `begin` is `0`, and
|
||||
* `end` is `-1`, this creates two new segments, `[g, h]`. This `g` is appended to
|
||||
* the end of the path from `a`, `c`, and `e`. This `h` is appended to the end of
|
||||
* `b`, `d`, and `f`.
|
||||
* @param {ForkContext} context An instance from which the previous segments
|
||||
* will be obtained.
|
||||
* @param {number} startIndex The index of the first segment in the context
|
||||
* that should be specified as previous segments for the newly created segments.
|
||||
* @param {number} endIndex The index of the last segment in the context
|
||||
* that should be specified as previous segments for the newly created segments.
|
||||
* @param {Function} create A function that creates new `CodePathSegment`
|
||||
* instances in a particular way. See the `CodePathSegment.new*` methods.
|
||||
* @returns {Array<CodePathSegment>} An array of the newly-created segments.
|
||||
*/
|
||||
function createSegments(context, startIndex, endIndex, create) {
|
||||
|
||||
/** @type {Array<Array<CodePathSegment>>} */
|
||||
const list = context.segmentsList;
|
||||
|
||||
/*
|
||||
* Both `startIndex` and `endIndex` work the same way: if the number is zero
|
||||
* or more, then the number is used as-is. If the number is negative,
|
||||
* then that number is added to the length of the segments list to
|
||||
* determine the index to use. That means -1 for either argument
|
||||
* is the last element, -2 is the second to last, and so on.
|
||||
*
|
||||
* So if `startIndex` is 0, `endIndex` is -1, and `list.length` is 3, the
|
||||
* effective `startIndex` is 0 and the effective `endIndex` is 2, so this function
|
||||
* will include items at indices 0, 1, and 2.
|
||||
*
|
||||
* Therefore, if `startIndex` is -1 and `endIndex` is -1, that means we'll only
|
||||
* be using the last segment in `list`.
|
||||
*/
|
||||
const normalizedBegin = startIndex >= 0 ? startIndex : list.length + startIndex;
|
||||
const normalizedEnd = endIndex >= 0 ? endIndex : list.length + endIndex;
|
||||
|
||||
/** @type {Array<CodePathSegment>} */
|
||||
const segments = [];
|
||||
|
||||
for (let i = 0; i < context.count; ++i) {
|
||||
|
||||
// this is passed into `new CodePathSegment` to add to code path.
|
||||
const allPrevSegments = [];
|
||||
|
||||
for (let j = normalizedBegin; j <= normalizedEnd; ++j) {
|
||||
allPrevSegments.push(list[j][i]);
|
||||
}
|
||||
|
||||
// note: `create` is just a wrapper that augments `new CodePathSegment`.
|
||||
segments.push(create(context.idGenerator.next(), allPrevSegments));
|
||||
}
|
||||
|
||||
return segments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inside of a `finally` block we end up with two parallel paths. If the code path
|
||||
* exits by a control statement (such as `break` or `continue`) from the `finally`
|
||||
* block, then we need to merge the remaining parallel paths back into one.
|
||||
* @param {ForkContext} context The fork context to work on.
|
||||
* @param {Array<CodePathSegment>} segments Segments to merge.
|
||||
* @returns {Array<CodePathSegment>} The merged segments.
|
||||
*/
|
||||
function mergeExtraSegments(context, segments) {
|
||||
let currentSegments = segments;
|
||||
|
||||
/*
|
||||
* We need to ensure that the array returned from this function contains no more
|
||||
* than the number of segments that the context allows. `context.count` indicates
|
||||
* how many items should be in the returned array to ensure that the new segment
|
||||
* entries will line up with the already existing segment entries.
|
||||
*/
|
||||
while (currentSegments.length > context.count) {
|
||||
const merged = [];
|
||||
|
||||
/*
|
||||
* Because `context.count` is a factor of 2 inside of a `finally` block,
|
||||
* we can divide the segment count by 2 to merge the paths together.
|
||||
* This loops through each segment in the list and creates a new `CodePathSegment`
|
||||
* that has the segment and the segment two slots away as previous segments.
|
||||
*
|
||||
* If `currentSegments` is [a,b,c,d], this will create new segments e and f, such
|
||||
* that:
|
||||
*
|
||||
* When `i` is 0:
|
||||
* a->e
|
||||
* c->e
|
||||
*
|
||||
* When `i` is 1:
|
||||
* b->f
|
||||
* d->f
|
||||
*/
|
||||
for (let i = 0, length = Math.floor(currentSegments.length / 2); i < length; ++i) {
|
||||
merged.push(CodePathSegment.newNext(
|
||||
context.idGenerator.next(),
|
||||
[currentSegments[i], currentSegments[i + length]]
|
||||
));
|
||||
}
|
||||
|
||||
/*
|
||||
* Go through the loop condition one more time to see if we have the
|
||||
* number of segments for the context. If not, we'll keep merging paths
|
||||
* of the merged segments until we get there.
|
||||
*/
|
||||
currentSegments = merged;
|
||||
}
|
||||
|
||||
return currentSegments;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Manages the forking of code paths.
|
||||
*/
|
||||
class ForkContext {
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param {IdGenerator} idGenerator An identifier generator for segments.
|
||||
* @param {ForkContext|null} upper The preceding fork context.
|
||||
* @param {number} count The number of parallel segments in each element
|
||||
* of `segmentsList`.
|
||||
*/
|
||||
constructor(idGenerator, upper, count) {
|
||||
|
||||
/**
|
||||
* The ID generator that will generate segment IDs for any new
|
||||
* segments that are created.
|
||||
* @type {IdGenerator}
|
||||
*/
|
||||
this.idGenerator = idGenerator;
|
||||
|
||||
/**
|
||||
* The preceding fork context.
|
||||
* @type {ForkContext|null}
|
||||
*/
|
||||
this.upper = upper;
|
||||
|
||||
/**
|
||||
* The number of elements in each element of `segmentsList`. In most
|
||||
* cases, this is 1 but can be 2 when there is a `finally` present,
|
||||
* which forks the code path outside of normal flow. In the case of nested
|
||||
* `finally` blocks, this can be a multiple of 2.
|
||||
* @type {number}
|
||||
*/
|
||||
this.count = count;
|
||||
|
||||
/**
|
||||
* The segments within this context. Each element in this array has
|
||||
* `count` elements that represent one step in each fork. For example,
|
||||
* when `segmentsList` is `[[a, b], [c, d], [e, f]]`, there is one path
|
||||
* a->c->e and one path b->d->f, and `count` is 2 because each element
|
||||
* is an array with two elements.
|
||||
* @type {Array<Array<CodePathSegment>>}
|
||||
*/
|
||||
this.segmentsList = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* The segments that begin this fork context.
|
||||
* @type {Array<CodePathSegment>}
|
||||
*/
|
||||
get head() {
|
||||
const list = this.segmentsList;
|
||||
|
||||
return list.length === 0 ? [] : list[list.length - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the context contains no segments.
|
||||
* @type {boolean}
|
||||
*/
|
||||
get empty() {
|
||||
return this.segmentsList.length === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if there are any segments that are reachable.
|
||||
* @type {boolean}
|
||||
*/
|
||||
get reachable() {
|
||||
const segments = this.head;
|
||||
|
||||
return segments.length > 0 && segments.some(isReachable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new segments in this context and appends them to the end of the
|
||||
* already existing `CodePathSegment`s specified by `startIndex` and
|
||||
* `endIndex`.
|
||||
* @param {number} startIndex The index of the first segment in the context
|
||||
* that should be specified as previous segments for the newly created segments.
|
||||
* @param {number} endIndex The index of the last segment in the context
|
||||
* that should be specified as previous segments for the newly created segments.
|
||||
* @returns {Array<CodePathSegment>} An array of the newly created segments.
|
||||
*/
|
||||
makeNext(startIndex, endIndex) {
|
||||
return createSegments(this, startIndex, endIndex, CodePathSegment.newNext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new unreachable segments in this context and appends them to the end of the
|
||||
* already existing `CodePathSegment`s specified by `startIndex` and
|
||||
* `endIndex`.
|
||||
* @param {number} startIndex The index of the first segment in the context
|
||||
* that should be specified as previous segments for the newly created segments.
|
||||
* @param {number} endIndex The index of the last segment in the context
|
||||
* that should be specified as previous segments for the newly created segments.
|
||||
* @returns {Array<CodePathSegment>} An array of the newly created segments.
|
||||
*/
|
||||
makeUnreachable(startIndex, endIndex) {
|
||||
return createSegments(this, startIndex, endIndex, CodePathSegment.newUnreachable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new segments in this context and does not append them to the end
|
||||
* of the already existing `CodePathSegment`s specified by `startIndex` and
|
||||
* `endIndex`. The `startIndex` and `endIndex` are only used to determine if
|
||||
* the new segments should be reachable. If any of the segments in this range
|
||||
* are reachable then the new segments are also reachable; otherwise, the new
|
||||
* segments are unreachable.
|
||||
* @param {number} startIndex The index of the first segment in the context
|
||||
* that should be considered for reachability.
|
||||
* @param {number} endIndex The index of the last segment in the context
|
||||
* that should be considered for reachability.
|
||||
* @returns {Array<CodePathSegment>} An array of the newly created segments.
|
||||
*/
|
||||
makeDisconnected(startIndex, endIndex) {
|
||||
return createSegments(this, startIndex, endIndex, CodePathSegment.newDisconnected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds segments to the head of this context.
|
||||
* @param {Array<CodePathSegment>} segments The segments to add.
|
||||
* @returns {void}
|
||||
*/
|
||||
add(segments) {
|
||||
assert(segments.length >= this.count, `${segments.length} >= ${this.count}`);
|
||||
this.segmentsList.push(mergeExtraSegments(this, segments));
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the head segments with the given segments.
|
||||
* The current head segments are removed.
|
||||
* @param {Array<CodePathSegment>} replacementHeadSegments The new head segments.
|
||||
* @returns {void}
|
||||
*/
|
||||
replaceHead(replacementHeadSegments) {
|
||||
assert(
|
||||
replacementHeadSegments.length >= this.count,
|
||||
`${replacementHeadSegments.length} >= ${this.count}`
|
||||
);
|
||||
this.segmentsList.splice(-1, 1, mergeExtraSegments(this, replacementHeadSegments));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all segments of a given fork context into this context.
|
||||
* @param {ForkContext} otherForkContext The fork context to add from.
|
||||
* @returns {void}
|
||||
*/
|
||||
addAll(otherForkContext) {
|
||||
assert(otherForkContext.count === this.count);
|
||||
this.segmentsList.push(...otherForkContext.segmentsList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all segments in this context.
|
||||
* @returns {void}
|
||||
*/
|
||||
clear() {
|
||||
this.segmentsList = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new root context, meaning that there are no parent
|
||||
* fork contexts.
|
||||
* @param {IdGenerator} idGenerator An identifier generator for segments.
|
||||
* @returns {ForkContext} New fork context.
|
||||
*/
|
||||
static newRoot(idGenerator) {
|
||||
const context = new ForkContext(idGenerator, null, 1);
|
||||
|
||||
context.add([CodePathSegment.newRoot(idGenerator.next())]);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty fork context preceded by a given context.
|
||||
* @param {ForkContext} parentContext The parent fork context.
|
||||
* @param {boolean} shouldForkLeavingPath Indicates that we are inside of
|
||||
* a `finally` block and should therefore fork the path that leaves
|
||||
* `finally`.
|
||||
* @returns {ForkContext} New fork context.
|
||||
*/
|
||||
static newEmpty(parentContext, shouldForkLeavingPath) {
|
||||
return new ForkContext(
|
||||
parentContext.idGenerator,
|
||||
parentContext,
|
||||
(shouldForkLeavingPath ? 2 : 1) * parentContext.count
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ForkContext;
|
||||
45
qwen/nodejs/node_modules/eslint/lib/linter/code-path-analysis/id-generator.js
generated
vendored
Normal file
45
qwen/nodejs/node_modules/eslint/lib/linter/code-path-analysis/id-generator.js
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* @fileoverview A class of identifiers generator for code path segments.
|
||||
*
|
||||
* Each rule uses the identifier of code path segments to store additional
|
||||
* information of the code path.
|
||||
*
|
||||
* @author Toru Nagashima
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A generator for unique ids.
|
||||
*/
|
||||
class IdGenerator {
|
||||
|
||||
/**
|
||||
* @param {string} prefix Optional. A prefix of generated ids.
|
||||
*/
|
||||
constructor(prefix) {
|
||||
this.prefix = String(prefix);
|
||||
this.n = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates id.
|
||||
* @returns {string} A generated id.
|
||||
*/
|
||||
next() {
|
||||
this.n = 1 + this.n | 0;
|
||||
|
||||
/* c8 ignore start */
|
||||
if (this.n < 0) {
|
||||
this.n = 1;
|
||||
}/* c8 ignore stop */
|
||||
|
||||
return this.prefix + this.n;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = IdGenerator;
|
||||
185
qwen/nodejs/node_modules/eslint/lib/linter/config-comment-parser.js
generated
vendored
Normal file
185
qwen/nodejs/node_modules/eslint/lib/linter/config-comment-parser.js
generated
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* @fileoverview Config Comment Parser
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
|
||||
/* eslint class-methods-use-this: off -- Methods desired on instance */
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const levn = require("levn"),
|
||||
{
|
||||
Legacy: {
|
||||
ConfigOps
|
||||
}
|
||||
} = require("@eslint/eslintrc/universal"),
|
||||
{
|
||||
directivesPattern
|
||||
} = require("../shared/directives");
|
||||
|
||||
const debug = require("debug")("eslint:config-comment-parser");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Typedefs
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @typedef {import("../shared/types").LintMessage} LintMessage */
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Object to parse ESLint configuration comments inside JavaScript files.
|
||||
* @name ConfigCommentParser
|
||||
*/
|
||||
module.exports = class ConfigCommentParser {
|
||||
|
||||
/**
|
||||
* Parses a list of "name:string_value" or/and "name" options divided by comma or
|
||||
* whitespace. Used for "global" and "exported" comments.
|
||||
* @param {string} string The string to parse.
|
||||
* @param {Comment} comment The comment node which has the string.
|
||||
* @returns {Object} Result map object of names and string values, or null values if no value was provided
|
||||
*/
|
||||
parseStringConfig(string, comment) {
|
||||
debug("Parsing String config");
|
||||
|
||||
const items = {};
|
||||
|
||||
// Collapse whitespace around `:` and `,` to make parsing easier
|
||||
const trimmedString = string.replace(/\s*([:,])\s*/gu, "$1");
|
||||
|
||||
trimmedString.split(/\s|,+/u).forEach(name => {
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
|
||||
// value defaults to null (if not provided), e.g: "foo" => ["foo", null]
|
||||
const [key, value = null] = name.split(":");
|
||||
|
||||
items[key] = { value, comment };
|
||||
});
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a JSON-like config.
|
||||
* @param {string} string The string to parse.
|
||||
* @param {Object} location Start line and column of comments for potential error message.
|
||||
* @returns {({success: true, config: Object}|{success: false, error: LintMessage})} Result map object
|
||||
*/
|
||||
parseJsonConfig(string, location) {
|
||||
debug("Parsing JSON config");
|
||||
|
||||
let items = {};
|
||||
|
||||
// Parses a JSON-like comment by the same way as parsing CLI option.
|
||||
try {
|
||||
items = levn.parse("Object", string) || {};
|
||||
|
||||
// Some tests say that it should ignore invalid comments such as `/*eslint no-alert:abc*/`.
|
||||
// Also, commaless notations have invalid severity:
|
||||
// "no-alert: 2 no-console: 2" --> {"no-alert": "2 no-console: 2"}
|
||||
// Should ignore that case as well.
|
||||
if (ConfigOps.isEverySeverityValid(items)) {
|
||||
return {
|
||||
success: true,
|
||||
config: items
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
|
||||
debug("Levn parsing failed; falling back to manual parsing.");
|
||||
|
||||
// ignore to parse the string by a fallback.
|
||||
}
|
||||
|
||||
/*
|
||||
* Optionator cannot parse commaless notations.
|
||||
* But we are supporting that. So this is a fallback for that.
|
||||
*/
|
||||
items = {};
|
||||
const normalizedString = string.replace(/([-a-zA-Z0-9/]+):/gu, "\"$1\":").replace(/(\]|[0-9])\s+(?=")/u, "$1,");
|
||||
|
||||
try {
|
||||
items = JSON.parse(`{${normalizedString}}`);
|
||||
} catch (ex) {
|
||||
debug("Manual parsing failed.");
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
ruleId: null,
|
||||
fatal: true,
|
||||
severity: 2,
|
||||
message: `Failed to parse JSON from '${normalizedString}': ${ex.message}`,
|
||||
line: location.start.line,
|
||||
column: location.start.column + 1,
|
||||
nodeType: null
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
config: items
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a config of values separated by comma.
|
||||
* @param {string} string The string to parse.
|
||||
* @returns {Object} Result map of values and true values
|
||||
*/
|
||||
parseListConfig(string) {
|
||||
debug("Parsing list config");
|
||||
|
||||
const items = {};
|
||||
|
||||
string.split(",").forEach(name => {
|
||||
const trimmedName = name.trim().replace(/^(?<quote>['"]?)(?<ruleId>.*)\k<quote>$/us, "$<ruleId>");
|
||||
|
||||
if (trimmedName) {
|
||||
items[trimmedName] = true;
|
||||
}
|
||||
});
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the directive and the justification from a given directive comment and trim them.
|
||||
* @param {string} value The comment text to extract.
|
||||
* @returns {{directivePart: string, justificationPart: string}} The extracted directive and justification.
|
||||
*/
|
||||
extractDirectiveComment(value) {
|
||||
const match = /\s-{2,}\s/u.exec(value);
|
||||
|
||||
if (!match) {
|
||||
return { directivePart: value.trim(), justificationPart: "" };
|
||||
}
|
||||
|
||||
const directive = value.slice(0, match.index).trim();
|
||||
const justification = value.slice(match.index + match[0].length).trim();
|
||||
|
||||
return { directivePart: directive, justificationPart: justification };
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a directive comment into directive text and value.
|
||||
* @param {Comment} comment The comment node with the directive to be parsed.
|
||||
* @returns {{directiveText: string, directiveValue: string}} The directive text and value.
|
||||
*/
|
||||
parseDirective(comment) {
|
||||
const { directivePart } = this.extractDirectiveComment(comment.value);
|
||||
const match = directivesPattern.exec(directivePart);
|
||||
const directiveText = match[1];
|
||||
const directiveValue = directivePart.slice(match.index + directiveText.length);
|
||||
|
||||
return { directiveText, directiveValue };
|
||||
}
|
||||
};
|
||||
13
qwen/nodejs/node_modules/eslint/lib/linter/index.js
generated
vendored
Normal file
13
qwen/nodejs/node_modules/eslint/lib/linter/index.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
"use strict";
|
||||
|
||||
const { Linter } = require("./linter");
|
||||
const interpolate = require("./interpolate");
|
||||
const SourceCodeFixer = require("./source-code-fixer");
|
||||
|
||||
module.exports = {
|
||||
Linter,
|
||||
|
||||
// For testers.
|
||||
SourceCodeFixer,
|
||||
interpolate
|
||||
};
|
||||
28
qwen/nodejs/node_modules/eslint/lib/linter/interpolate.js
generated
vendored
Normal file
28
qwen/nodejs/node_modules/eslint/lib/linter/interpolate.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* @fileoverview Interpolate keys from an object into a string with {{ }} markers.
|
||||
* @author Jed Fox
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = (text, data) => {
|
||||
if (!data) {
|
||||
return text;
|
||||
}
|
||||
|
||||
// Substitution content for any {{ }} markers.
|
||||
return text.replace(/\{\{([^{}]+?)\}\}/gu, (fullMatch, termWithWhitespace) => {
|
||||
const term = termWithWhitespace.trim();
|
||||
|
||||
if (term in data) {
|
||||
return data[term];
|
||||
}
|
||||
|
||||
// Preserve old behavior: If parameter name not provided, don't replace it.
|
||||
return fullMatch;
|
||||
});
|
||||
};
|
||||
2119
qwen/nodejs/node_modules/eslint/lib/linter/linter.js
generated
vendored
Normal file
2119
qwen/nodejs/node_modules/eslint/lib/linter/linter.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
354
qwen/nodejs/node_modules/eslint/lib/linter/node-event-generator.js
generated
vendored
Normal file
354
qwen/nodejs/node_modules/eslint/lib/linter/node-event-generator.js
generated
vendored
Normal file
@@ -0,0 +1,354 @@
|
||||
/**
|
||||
* @fileoverview The event generator for AST nodes.
|
||||
* @author Toru Nagashima
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const esquery = require("esquery");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Typedefs
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* An object describing an AST selector
|
||||
* @typedef {Object} ASTSelector
|
||||
* @property {string} rawSelector The string that was parsed into this selector
|
||||
* @property {boolean} isExit `true` if this should be emitted when exiting the node rather than when entering
|
||||
* @property {Object} parsedSelector An object (from esquery) describing the matching behavior of the selector
|
||||
* @property {string[]|null} listenerTypes A list of node types that could possibly cause the selector to match,
|
||||
* or `null` if all node types could cause a match
|
||||
* @property {number} attributeCount The total number of classes, pseudo-classes, and attribute queries in this selector
|
||||
* @property {number} identifierCount The total number of identifier queries in this selector
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Computes the union of one or more arrays
|
||||
* @param {...any[]} arrays One or more arrays to union
|
||||
* @returns {any[]} The union of the input arrays
|
||||
*/
|
||||
function union(...arrays) {
|
||||
return [...new Set(arrays.flat())];
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the intersection of one or more arrays
|
||||
* @param {...any[]} arrays One or more arrays to intersect
|
||||
* @returns {any[]} The intersection of the input arrays
|
||||
*/
|
||||
function intersection(...arrays) {
|
||||
if (arrays.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let result = [...new Set(arrays[0])];
|
||||
|
||||
for (const array of arrays.slice(1)) {
|
||||
result = result.filter(x => array.includes(x));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the possible types of a selector
|
||||
* @param {Object} parsedSelector An object (from esquery) describing the matching behavior of the selector
|
||||
* @returns {string[]|null} The node types that could possibly trigger this selector, or `null` if all node types could trigger it
|
||||
*/
|
||||
function getPossibleTypes(parsedSelector) {
|
||||
switch (parsedSelector.type) {
|
||||
case "identifier":
|
||||
return [parsedSelector.value];
|
||||
|
||||
case "matches": {
|
||||
const typesForComponents = parsedSelector.selectors.map(getPossibleTypes);
|
||||
|
||||
if (typesForComponents.every(Boolean)) {
|
||||
return union(...typesForComponents);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
case "compound": {
|
||||
const typesForComponents = parsedSelector.selectors.map(getPossibleTypes).filter(typesForComponent => typesForComponent);
|
||||
|
||||
// If all of the components could match any type, then the compound could also match any type.
|
||||
if (!typesForComponents.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* If at least one of the components could only match a particular type, the compound could only match
|
||||
* the intersection of those types.
|
||||
*/
|
||||
return intersection(...typesForComponents);
|
||||
}
|
||||
|
||||
case "child":
|
||||
case "descendant":
|
||||
case "sibling":
|
||||
case "adjacent":
|
||||
return getPossibleTypes(parsedSelector.right);
|
||||
|
||||
case "class":
|
||||
if (parsedSelector.name === "function") {
|
||||
return ["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"];
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
default:
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of class, pseudo-class, and attribute queries in this selector
|
||||
* @param {Object} parsedSelector An object (from esquery) describing the selector's matching behavior
|
||||
* @returns {number} The number of class, pseudo-class, and attribute queries in this selector
|
||||
*/
|
||||
function countClassAttributes(parsedSelector) {
|
||||
switch (parsedSelector.type) {
|
||||
case "child":
|
||||
case "descendant":
|
||||
case "sibling":
|
||||
case "adjacent":
|
||||
return countClassAttributes(parsedSelector.left) + countClassAttributes(parsedSelector.right);
|
||||
|
||||
case "compound":
|
||||
case "not":
|
||||
case "matches":
|
||||
return parsedSelector.selectors.reduce((sum, childSelector) => sum + countClassAttributes(childSelector), 0);
|
||||
|
||||
case "attribute":
|
||||
case "field":
|
||||
case "nth-child":
|
||||
case "nth-last-child":
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of identifier queries in this selector
|
||||
* @param {Object} parsedSelector An object (from esquery) describing the selector's matching behavior
|
||||
* @returns {number} The number of identifier queries
|
||||
*/
|
||||
function countIdentifiers(parsedSelector) {
|
||||
switch (parsedSelector.type) {
|
||||
case "child":
|
||||
case "descendant":
|
||||
case "sibling":
|
||||
case "adjacent":
|
||||
return countIdentifiers(parsedSelector.left) + countIdentifiers(parsedSelector.right);
|
||||
|
||||
case "compound":
|
||||
case "not":
|
||||
case "matches":
|
||||
return parsedSelector.selectors.reduce((sum, childSelector) => sum + countIdentifiers(childSelector), 0);
|
||||
|
||||
case "identifier":
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the specificity of two selector objects, with CSS-like rules.
|
||||
* @param {ASTSelector} selectorA An AST selector descriptor
|
||||
* @param {ASTSelector} selectorB Another AST selector descriptor
|
||||
* @returns {number}
|
||||
* a value less than 0 if selectorA is less specific than selectorB
|
||||
* a value greater than 0 if selectorA is more specific than selectorB
|
||||
* a value less than 0 if selectorA and selectorB have the same specificity, and selectorA <= selectorB alphabetically
|
||||
* a value greater than 0 if selectorA and selectorB have the same specificity, and selectorA > selectorB alphabetically
|
||||
*/
|
||||
function compareSpecificity(selectorA, selectorB) {
|
||||
return selectorA.attributeCount - selectorB.attributeCount ||
|
||||
selectorA.identifierCount - selectorB.identifierCount ||
|
||||
(selectorA.rawSelector <= selectorB.rawSelector ? -1 : 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a raw selector string, and throws a useful error if parsing fails.
|
||||
* @param {string} rawSelector A raw AST selector
|
||||
* @returns {Object} An object (from esquery) describing the matching behavior of this selector
|
||||
* @throws {Error} An error if the selector is invalid
|
||||
*/
|
||||
function tryParseSelector(rawSelector) {
|
||||
try {
|
||||
return esquery.parse(rawSelector.replace(/:exit$/u, ""));
|
||||
} catch (err) {
|
||||
if (err.location && err.location.start && typeof err.location.start.offset === "number") {
|
||||
throw new SyntaxError(`Syntax error in selector "${rawSelector}" at position ${err.location.start.offset}: ${err.message}`);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
const selectorCache = new Map();
|
||||
|
||||
/**
|
||||
* Parses a raw selector string, and returns the parsed selector along with specificity and type information.
|
||||
* @param {string} rawSelector A raw AST selector
|
||||
* @returns {ASTSelector} A selector descriptor
|
||||
*/
|
||||
function parseSelector(rawSelector) {
|
||||
if (selectorCache.has(rawSelector)) {
|
||||
return selectorCache.get(rawSelector);
|
||||
}
|
||||
|
||||
const parsedSelector = tryParseSelector(rawSelector);
|
||||
|
||||
const result = {
|
||||
rawSelector,
|
||||
isExit: rawSelector.endsWith(":exit"),
|
||||
parsedSelector,
|
||||
listenerTypes: getPossibleTypes(parsedSelector),
|
||||
attributeCount: countClassAttributes(parsedSelector),
|
||||
identifierCount: countIdentifiers(parsedSelector)
|
||||
};
|
||||
|
||||
selectorCache.set(rawSelector, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The event generator for AST nodes.
|
||||
* This implements below interface.
|
||||
*
|
||||
* ```ts
|
||||
* interface EventGenerator {
|
||||
* emitter: SafeEmitter;
|
||||
* enterNode(node: ASTNode): void;
|
||||
* leaveNode(node: ASTNode): void;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class NodeEventGenerator {
|
||||
|
||||
/**
|
||||
* @param {SafeEmitter} emitter
|
||||
* An SafeEmitter which is the destination of events. This emitter must already
|
||||
* have registered listeners for all of the events that it needs to listen for.
|
||||
* (See lib/linter/safe-emitter.js for more details on `SafeEmitter`.)
|
||||
* @param {ESQueryOptions} esqueryOptions `esquery` options for traversing custom nodes.
|
||||
* @returns {NodeEventGenerator} new instance
|
||||
*/
|
||||
constructor(emitter, esqueryOptions) {
|
||||
this.emitter = emitter;
|
||||
this.esqueryOptions = esqueryOptions;
|
||||
this.currentAncestry = [];
|
||||
this.enterSelectorsByNodeType = new Map();
|
||||
this.exitSelectorsByNodeType = new Map();
|
||||
this.anyTypeEnterSelectors = [];
|
||||
this.anyTypeExitSelectors = [];
|
||||
|
||||
emitter.eventNames().forEach(rawSelector => {
|
||||
const selector = parseSelector(rawSelector);
|
||||
|
||||
if (selector.listenerTypes) {
|
||||
const typeMap = selector.isExit ? this.exitSelectorsByNodeType : this.enterSelectorsByNodeType;
|
||||
|
||||
selector.listenerTypes.forEach(nodeType => {
|
||||
if (!typeMap.has(nodeType)) {
|
||||
typeMap.set(nodeType, []);
|
||||
}
|
||||
typeMap.get(nodeType).push(selector);
|
||||
});
|
||||
return;
|
||||
}
|
||||
const selectors = selector.isExit ? this.anyTypeExitSelectors : this.anyTypeEnterSelectors;
|
||||
|
||||
selectors.push(selector);
|
||||
});
|
||||
|
||||
this.anyTypeEnterSelectors.sort(compareSpecificity);
|
||||
this.anyTypeExitSelectors.sort(compareSpecificity);
|
||||
this.enterSelectorsByNodeType.forEach(selectorList => selectorList.sort(compareSpecificity));
|
||||
this.exitSelectorsByNodeType.forEach(selectorList => selectorList.sort(compareSpecificity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a selector against a node, and emits it if it matches
|
||||
* @param {ASTNode} node The node to check
|
||||
* @param {ASTSelector} selector An AST selector descriptor
|
||||
* @returns {void}
|
||||
*/
|
||||
applySelector(node, selector) {
|
||||
if (esquery.matches(node, selector.parsedSelector, this.currentAncestry, this.esqueryOptions)) {
|
||||
this.emitter.emit(selector.rawSelector, node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies all appropriate selectors to a node, in specificity order
|
||||
* @param {ASTNode} node The node to check
|
||||
* @param {boolean} isExit `false` if the node is currently being entered, `true` if it's currently being exited
|
||||
* @returns {void}
|
||||
*/
|
||||
applySelectors(node, isExit) {
|
||||
const selectorsByNodeType = (isExit ? this.exitSelectorsByNodeType : this.enterSelectorsByNodeType).get(node.type) || [];
|
||||
const anyTypeSelectors = isExit ? this.anyTypeExitSelectors : this.anyTypeEnterSelectors;
|
||||
|
||||
/*
|
||||
* selectorsByNodeType and anyTypeSelectors were already sorted by specificity in the constructor.
|
||||
* Iterate through each of them, applying selectors in the right order.
|
||||
*/
|
||||
let selectorsByTypeIndex = 0;
|
||||
let anyTypeSelectorsIndex = 0;
|
||||
|
||||
while (selectorsByTypeIndex < selectorsByNodeType.length || anyTypeSelectorsIndex < anyTypeSelectors.length) {
|
||||
if (
|
||||
selectorsByTypeIndex >= selectorsByNodeType.length ||
|
||||
anyTypeSelectorsIndex < anyTypeSelectors.length &&
|
||||
compareSpecificity(anyTypeSelectors[anyTypeSelectorsIndex], selectorsByNodeType[selectorsByTypeIndex]) < 0
|
||||
) {
|
||||
this.applySelector(node, anyTypeSelectors[anyTypeSelectorsIndex++]);
|
||||
} else {
|
||||
this.applySelector(node, selectorsByNodeType[selectorsByTypeIndex++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an event of entering AST node.
|
||||
* @param {ASTNode} node A node which was entered.
|
||||
* @returns {void}
|
||||
*/
|
||||
enterNode(node) {
|
||||
if (node.parent) {
|
||||
this.currentAncestry.unshift(node.parent);
|
||||
}
|
||||
this.applySelectors(node, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an event of leaving AST node.
|
||||
* @param {ASTNode} node A node which was left.
|
||||
* @returns {void}
|
||||
*/
|
||||
leaveNode(node) {
|
||||
this.applySelectors(node, true);
|
||||
this.currentAncestry.shift();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NodeEventGenerator;
|
||||
369
qwen/nodejs/node_modules/eslint/lib/linter/report-translator.js
generated
vendored
Normal file
369
qwen/nodejs/node_modules/eslint/lib/linter/report-translator.js
generated
vendored
Normal file
@@ -0,0 +1,369 @@
|
||||
/**
|
||||
* @fileoverview A helper that translates context.report() calls from the rule API into generic problem objects
|
||||
* @author Teddy Katz
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const assert = require("assert");
|
||||
const ruleFixer = require("./rule-fixer");
|
||||
const interpolate = require("./interpolate");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Typedefs
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @typedef {import("../shared/types").LintMessage} LintMessage */
|
||||
|
||||
/**
|
||||
* An error message description
|
||||
* @typedef {Object} MessageDescriptor
|
||||
* @property {ASTNode} [node] The reported node
|
||||
* @property {Location} loc The location of the problem.
|
||||
* @property {string} message The problem message.
|
||||
* @property {Object} [data] Optional data to use to fill in placeholders in the
|
||||
* message.
|
||||
* @property {Function} [fix] The function to call that creates a fix command.
|
||||
* @property {Array<{desc?: string, messageId?: string, fix: Function}>} suggest Suggestion descriptions and functions to create a the associated fixes.
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Module Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
/**
|
||||
* Translates a multi-argument context.report() call into a single object argument call
|
||||
* @param {...*} args A list of arguments passed to `context.report`
|
||||
* @returns {MessageDescriptor} A normalized object containing report information
|
||||
*/
|
||||
function normalizeMultiArgReportCall(...args) {
|
||||
|
||||
// If there is one argument, it is considered to be a new-style call already.
|
||||
if (args.length === 1) {
|
||||
|
||||
// Shallow clone the object to avoid surprises if reusing the descriptor
|
||||
return Object.assign({}, args[0]);
|
||||
}
|
||||
|
||||
// If the second argument is a string, the arguments are interpreted as [node, message, data, fix].
|
||||
if (typeof args[1] === "string") {
|
||||
return {
|
||||
node: args[0],
|
||||
message: args[1],
|
||||
data: args[2],
|
||||
fix: args[3]
|
||||
};
|
||||
}
|
||||
|
||||
// Otherwise, the arguments are interpreted as [node, loc, message, data, fix].
|
||||
return {
|
||||
node: args[0],
|
||||
loc: args[1],
|
||||
message: args[2],
|
||||
data: args[3],
|
||||
fix: args[4]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that either a loc or a node was provided, and the node is valid if it was provided.
|
||||
* @param {MessageDescriptor} descriptor A descriptor to validate
|
||||
* @returns {void}
|
||||
* @throws AssertionError if neither a node nor a loc was provided, or if the node is not an object
|
||||
*/
|
||||
function assertValidNodeInfo(descriptor) {
|
||||
if (descriptor.node) {
|
||||
assert(typeof descriptor.node === "object", "Node must be an object");
|
||||
} else {
|
||||
assert(descriptor.loc, "Node must be provided when reporting error if location is not provided");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a MessageDescriptor to always have a `loc` with `start` and `end` properties
|
||||
* @param {MessageDescriptor} descriptor A descriptor for the report from a rule.
|
||||
* @returns {{start: Location, end: (Location|null)}} An updated location that infers the `start` and `end` properties
|
||||
* from the `node` of the original descriptor, or infers the `start` from the `loc` of the original descriptor.
|
||||
*/
|
||||
function normalizeReportLoc(descriptor) {
|
||||
if (descriptor.loc) {
|
||||
if (descriptor.loc.start) {
|
||||
return descriptor.loc;
|
||||
}
|
||||
return { start: descriptor.loc, end: null };
|
||||
}
|
||||
return descriptor.node.loc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones the given fix object.
|
||||
* @param {Fix|null} fix The fix to clone.
|
||||
* @returns {Fix|null} Deep cloned fix object or `null` if `null` or `undefined` was passed in.
|
||||
*/
|
||||
function cloneFix(fix) {
|
||||
if (!fix) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
range: [fix.range[0], fix.range[1]],
|
||||
text: fix.text
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a fix has a valid range.
|
||||
* @param {Fix|null} fix The fix to validate.
|
||||
* @returns {void}
|
||||
*/
|
||||
function assertValidFix(fix) {
|
||||
if (fix) {
|
||||
assert(fix.range && typeof fix.range[0] === "number" && typeof fix.range[1] === "number", `Fix has invalid range: ${JSON.stringify(fix, null, 2)}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares items in a fixes array by range.
|
||||
* @param {Fix} a The first message.
|
||||
* @param {Fix} b The second message.
|
||||
* @returns {int} -1 if a comes before b, 1 if a comes after b, 0 if equal.
|
||||
* @private
|
||||
*/
|
||||
function compareFixesByRange(a, b) {
|
||||
return a.range[0] - b.range[0] || a.range[1] - b.range[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the given fixes array into one.
|
||||
* @param {Fix[]} fixes The fixes to merge.
|
||||
* @param {SourceCode} sourceCode The source code object to get the text between fixes.
|
||||
* @returns {{text: string, range: number[]}} The merged fixes
|
||||
*/
|
||||
function mergeFixes(fixes, sourceCode) {
|
||||
for (const fix of fixes) {
|
||||
assertValidFix(fix);
|
||||
}
|
||||
|
||||
if (fixes.length === 0) {
|
||||
return null;
|
||||
}
|
||||
if (fixes.length === 1) {
|
||||
return cloneFix(fixes[0]);
|
||||
}
|
||||
|
||||
fixes.sort(compareFixesByRange);
|
||||
|
||||
const originalText = sourceCode.text;
|
||||
const start = fixes[0].range[0];
|
||||
const end = fixes[fixes.length - 1].range[1];
|
||||
let text = "";
|
||||
let lastPos = Number.MIN_SAFE_INTEGER;
|
||||
|
||||
for (const fix of fixes) {
|
||||
assert(fix.range[0] >= lastPos, "Fix objects must not be overlapped in a report.");
|
||||
|
||||
if (fix.range[0] >= 0) {
|
||||
text += originalText.slice(Math.max(0, start, lastPos), fix.range[0]);
|
||||
}
|
||||
text += fix.text;
|
||||
lastPos = fix.range[1];
|
||||
}
|
||||
text += originalText.slice(Math.max(0, start, lastPos), end);
|
||||
|
||||
return { range: [start, end], text };
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets one fix object from the given descriptor.
|
||||
* If the descriptor retrieves multiple fixes, this merges those to one.
|
||||
* @param {MessageDescriptor} descriptor The report descriptor.
|
||||
* @param {SourceCode} sourceCode The source code object to get text between fixes.
|
||||
* @returns {({text: string, range: number[]}|null)} The fix for the descriptor
|
||||
*/
|
||||
function normalizeFixes(descriptor, sourceCode) {
|
||||
if (typeof descriptor.fix !== "function") {
|
||||
return null;
|
||||
}
|
||||
|
||||
// @type {null | Fix | Fix[] | IterableIterator<Fix>}
|
||||
const fix = descriptor.fix(ruleFixer);
|
||||
|
||||
// Merge to one.
|
||||
if (fix && Symbol.iterator in fix) {
|
||||
return mergeFixes(Array.from(fix), sourceCode);
|
||||
}
|
||||
|
||||
assertValidFix(fix);
|
||||
return cloneFix(fix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of suggestion objects from the given descriptor.
|
||||
* @param {MessageDescriptor} descriptor The report descriptor.
|
||||
* @param {SourceCode} sourceCode The source code object to get text between fixes.
|
||||
* @param {Object} messages Object of meta messages for the rule.
|
||||
* @returns {Array<SuggestionResult>} The suggestions for the descriptor
|
||||
*/
|
||||
function mapSuggestions(descriptor, sourceCode, messages) {
|
||||
if (!descriptor.suggest || !Array.isArray(descriptor.suggest)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return descriptor.suggest
|
||||
.map(suggestInfo => {
|
||||
const computedDesc = suggestInfo.desc || messages[suggestInfo.messageId];
|
||||
|
||||
return {
|
||||
...suggestInfo,
|
||||
desc: interpolate(computedDesc, suggestInfo.data),
|
||||
fix: normalizeFixes(suggestInfo, sourceCode)
|
||||
};
|
||||
})
|
||||
|
||||
// Remove suggestions that didn't provide a fix
|
||||
.filter(({ fix }) => fix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates information about the report from a descriptor
|
||||
* @param {Object} options Information about the problem
|
||||
* @param {string} options.ruleId Rule ID
|
||||
* @param {(0|1|2)} options.severity Rule severity
|
||||
* @param {(ASTNode|null)} options.node Node
|
||||
* @param {string} options.message Error message
|
||||
* @param {string} [options.messageId] The error message ID.
|
||||
* @param {{start: SourceLocation, end: (SourceLocation|null)}} options.loc Start and end location
|
||||
* @param {{text: string, range: (number[]|null)}} options.fix The fix object
|
||||
* @param {Array<{text: string, range: (number[]|null)}>} options.suggestions The array of suggestions objects
|
||||
* @returns {LintMessage} Information about the report
|
||||
*/
|
||||
function createProblem(options) {
|
||||
const problem = {
|
||||
ruleId: options.ruleId,
|
||||
severity: options.severity,
|
||||
message: options.message,
|
||||
line: options.loc.start.line,
|
||||
column: options.loc.start.column + 1,
|
||||
nodeType: options.node && options.node.type || null
|
||||
};
|
||||
|
||||
/*
|
||||
* If this isn’t in the conditional, some of the tests fail
|
||||
* because `messageId` is present in the problem object
|
||||
*/
|
||||
if (options.messageId) {
|
||||
problem.messageId = options.messageId;
|
||||
}
|
||||
|
||||
if (options.loc.end) {
|
||||
problem.endLine = options.loc.end.line;
|
||||
problem.endColumn = options.loc.end.column + 1;
|
||||
}
|
||||
|
||||
if (options.fix) {
|
||||
problem.fix = options.fix;
|
||||
}
|
||||
|
||||
if (options.suggestions && options.suggestions.length > 0) {
|
||||
problem.suggestions = options.suggestions;
|
||||
}
|
||||
|
||||
return problem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that suggestions are properly defined. Throws if an error is detected.
|
||||
* @param {Array<{ desc?: string, messageId?: string }>} suggest The incoming suggest data.
|
||||
* @param {Object} messages Object of meta messages for the rule.
|
||||
* @returns {void}
|
||||
*/
|
||||
function validateSuggestions(suggest, messages) {
|
||||
if (suggest && Array.isArray(suggest)) {
|
||||
suggest.forEach(suggestion => {
|
||||
if (suggestion.messageId) {
|
||||
const { messageId } = suggestion;
|
||||
|
||||
if (!messages) {
|
||||
throw new TypeError(`context.report() called with a suggest option with a messageId '${messageId}', but no messages were present in the rule metadata.`);
|
||||
}
|
||||
|
||||
if (!messages[messageId]) {
|
||||
throw new TypeError(`context.report() called with a suggest option with a messageId '${messageId}' which is not present in the 'messages' config: ${JSON.stringify(messages, null, 2)}`);
|
||||
}
|
||||
|
||||
if (suggestion.desc) {
|
||||
throw new TypeError("context.report() called with a suggest option that defines both a 'messageId' and an 'desc'. Please only pass one.");
|
||||
}
|
||||
} else if (!suggestion.desc) {
|
||||
throw new TypeError("context.report() called with a suggest option that doesn't have either a `desc` or `messageId`");
|
||||
}
|
||||
|
||||
if (typeof suggestion.fix !== "function") {
|
||||
throw new TypeError(`context.report() called with a suggest option without a fix function. See: ${suggestion}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a function that converts the arguments of a `context.report` call from a rule into a reported
|
||||
* problem for the Node.js API.
|
||||
* @param {{ruleId: string, severity: number, sourceCode: SourceCode, messageIds: Object, disableFixes: boolean}} metadata Metadata for the reported problem
|
||||
* @param {SourceCode} sourceCode The `SourceCode` instance for the text being linted
|
||||
* @returns {function(...args): LintMessage} Function that returns information about the report
|
||||
*/
|
||||
|
||||
module.exports = function createReportTranslator(metadata) {
|
||||
|
||||
/*
|
||||
* `createReportTranslator` gets called once per enabled rule per file. It needs to be very performant.
|
||||
* The report translator itself (i.e. the function that `createReportTranslator` returns) gets
|
||||
* called every time a rule reports a problem, which happens much less frequently (usually, the vast
|
||||
* majority of rules don't report any problems for a given file).
|
||||
*/
|
||||
return (...args) => {
|
||||
const descriptor = normalizeMultiArgReportCall(...args);
|
||||
const messages = metadata.messageIds;
|
||||
|
||||
assertValidNodeInfo(descriptor);
|
||||
|
||||
let computedMessage;
|
||||
|
||||
if (descriptor.messageId) {
|
||||
if (!messages) {
|
||||
throw new TypeError("context.report() called with a messageId, but no messages were present in the rule metadata.");
|
||||
}
|
||||
const id = descriptor.messageId;
|
||||
|
||||
if (descriptor.message) {
|
||||
throw new TypeError("context.report() called with a message and a messageId. Please only pass one.");
|
||||
}
|
||||
if (!messages || !Object.prototype.hasOwnProperty.call(messages, id)) {
|
||||
throw new TypeError(`context.report() called with a messageId of '${id}' which is not present in the 'messages' config: ${JSON.stringify(messages, null, 2)}`);
|
||||
}
|
||||
computedMessage = messages[id];
|
||||
} else if (descriptor.message) {
|
||||
computedMessage = descriptor.message;
|
||||
} else {
|
||||
throw new TypeError("Missing `message` property in report() call; add a message that describes the linting problem.");
|
||||
}
|
||||
|
||||
validateSuggestions(descriptor.suggest, messages);
|
||||
|
||||
return createProblem({
|
||||
ruleId: metadata.ruleId,
|
||||
severity: metadata.severity,
|
||||
node: descriptor.node,
|
||||
message: interpolate(computedMessage, descriptor.data),
|
||||
messageId: descriptor.messageId,
|
||||
loc: normalizeReportLoc(descriptor),
|
||||
fix: metadata.disableFixes ? null : normalizeFixes(descriptor, metadata.sourceCode),
|
||||
suggestions: metadata.disableFixes ? [] : mapSuggestions(descriptor, metadata.sourceCode, messages)
|
||||
});
|
||||
};
|
||||
};
|
||||
140
qwen/nodejs/node_modules/eslint/lib/linter/rule-fixer.js
generated
vendored
Normal file
140
qwen/nodejs/node_modules/eslint/lib/linter/rule-fixer.js
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* @fileoverview An object that creates fix commands for rules.
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// none!
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates a fix command that inserts text at the specified index in the source text.
|
||||
* @param {int} index The 0-based index at which to insert the new text.
|
||||
* @param {string} text The text to insert.
|
||||
* @returns {Object} The fix command.
|
||||
* @private
|
||||
*/
|
||||
function insertTextAt(index, text) {
|
||||
return {
|
||||
range: [index, index],
|
||||
text
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates code fixing commands for rules.
|
||||
*/
|
||||
|
||||
const ruleFixer = Object.freeze({
|
||||
|
||||
/**
|
||||
* Creates a fix command that inserts text after the given node or token.
|
||||
* The fix is not applied until applyFixes() is called.
|
||||
* @param {ASTNode|Token} nodeOrToken The node or token to insert after.
|
||||
* @param {string} text The text to insert.
|
||||
* @returns {Object} The fix command.
|
||||
*/
|
||||
insertTextAfter(nodeOrToken, text) {
|
||||
return this.insertTextAfterRange(nodeOrToken.range, text);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a fix command that inserts text after the specified range in the source text.
|
||||
* The fix is not applied until applyFixes() is called.
|
||||
* @param {int[]} range The range to replace, first item is start of range, second
|
||||
* is end of range.
|
||||
* @param {string} text The text to insert.
|
||||
* @returns {Object} The fix command.
|
||||
*/
|
||||
insertTextAfterRange(range, text) {
|
||||
return insertTextAt(range[1], text);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a fix command that inserts text before the given node or token.
|
||||
* The fix is not applied until applyFixes() is called.
|
||||
* @param {ASTNode|Token} nodeOrToken The node or token to insert before.
|
||||
* @param {string} text The text to insert.
|
||||
* @returns {Object} The fix command.
|
||||
*/
|
||||
insertTextBefore(nodeOrToken, text) {
|
||||
return this.insertTextBeforeRange(nodeOrToken.range, text);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a fix command that inserts text before the specified range in the source text.
|
||||
* The fix is not applied until applyFixes() is called.
|
||||
* @param {int[]} range The range to replace, first item is start of range, second
|
||||
* is end of range.
|
||||
* @param {string} text The text to insert.
|
||||
* @returns {Object} The fix command.
|
||||
*/
|
||||
insertTextBeforeRange(range, text) {
|
||||
return insertTextAt(range[0], text);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a fix command that replaces text at the node or token.
|
||||
* The fix is not applied until applyFixes() is called.
|
||||
* @param {ASTNode|Token} nodeOrToken The node or token to remove.
|
||||
* @param {string} text The text to insert.
|
||||
* @returns {Object} The fix command.
|
||||
*/
|
||||
replaceText(nodeOrToken, text) {
|
||||
return this.replaceTextRange(nodeOrToken.range, text);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a fix command that replaces text at the specified range in the source text.
|
||||
* The fix is not applied until applyFixes() is called.
|
||||
* @param {int[]} range The range to replace, first item is start of range, second
|
||||
* is end of range.
|
||||
* @param {string} text The text to insert.
|
||||
* @returns {Object} The fix command.
|
||||
*/
|
||||
replaceTextRange(range, text) {
|
||||
return {
|
||||
range,
|
||||
text
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a fix command that removes the node or token from the source.
|
||||
* The fix is not applied until applyFixes() is called.
|
||||
* @param {ASTNode|Token} nodeOrToken The node or token to remove.
|
||||
* @returns {Object} The fix command.
|
||||
*/
|
||||
remove(nodeOrToken) {
|
||||
return this.removeRange(nodeOrToken.range);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a fix command that removes the specified range of text from the source.
|
||||
* The fix is not applied until applyFixes() is called.
|
||||
* @param {int[]} range The range to remove, first item is start of range, second
|
||||
* is end of range.
|
||||
* @returns {Object} The fix command.
|
||||
*/
|
||||
removeRange(range) {
|
||||
return {
|
||||
range,
|
||||
text: ""
|
||||
};
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
module.exports = ruleFixer;
|
||||
80
qwen/nodejs/node_modules/eslint/lib/linter/rules.js
generated
vendored
Normal file
80
qwen/nodejs/node_modules/eslint/lib/linter/rules.js
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* @fileoverview Defines a storage for rules.
|
||||
* @author Nicholas C. Zakas
|
||||
* @author aladdin-add
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const builtInRules = require("../rules");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Normalizes a rule module to the new-style API
|
||||
* @param {(Function|{create: Function})} rule A rule object, which can either be a function
|
||||
* ("old-style") or an object with a `create` method ("new-style")
|
||||
* @returns {{create: Function}} A new-style rule.
|
||||
*/
|
||||
function normalizeRule(rule) {
|
||||
return typeof rule === "function" ? Object.assign({ create: rule }, rule) : rule;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* A storage for rules.
|
||||
*/
|
||||
class Rules {
|
||||
constructor() {
|
||||
this._rules = Object.create(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a rule module for rule id in storage.
|
||||
* @param {string} ruleId Rule id (file name).
|
||||
* @param {Function} ruleModule Rule handler.
|
||||
* @returns {void}
|
||||
*/
|
||||
define(ruleId, ruleModule) {
|
||||
this._rules[ruleId] = normalizeRule(ruleModule);
|
||||
}
|
||||
|
||||
/**
|
||||
* Access rule handler by id (file name).
|
||||
* @param {string} ruleId Rule id (file name).
|
||||
* @returns {{create: Function, schema: JsonSchema[]}}
|
||||
* A rule. This is normalized to always have the new-style shape with a `create` method.
|
||||
*/
|
||||
get(ruleId) {
|
||||
if (typeof this._rules[ruleId] === "string") {
|
||||
this.define(ruleId, require(this._rules[ruleId]));
|
||||
}
|
||||
if (this._rules[ruleId]) {
|
||||
return this._rules[ruleId];
|
||||
}
|
||||
if (builtInRules.has(ruleId)) {
|
||||
return builtInRules.get(ruleId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
*[Symbol.iterator]() {
|
||||
yield* builtInRules;
|
||||
|
||||
for (const ruleId of Object.keys(this._rules)) {
|
||||
yield [ruleId, this.get(ruleId)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Rules;
|
||||
52
qwen/nodejs/node_modules/eslint/lib/linter/safe-emitter.js
generated
vendored
Normal file
52
qwen/nodejs/node_modules/eslint/lib/linter/safe-emitter.js
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @fileoverview A variant of EventEmitter which does not give listeners information about each other
|
||||
* @author Teddy Katz
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Typedefs
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* An event emitter
|
||||
* @typedef {Object} SafeEmitter
|
||||
* @property {(eventName: string, listenerFunc: Function) => void} on Adds a listener for a given event name
|
||||
* @property {(eventName: string, arg1?: any, arg2?: any, arg3?: any) => void} emit Emits an event with a given name.
|
||||
* This calls all the listeners that were listening for that name, with `arg1`, `arg2`, and `arg3` as arguments.
|
||||
* @property {function(): string[]} eventNames Gets the list of event names that have registered listeners.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates an object which can listen for and emit events.
|
||||
* This is similar to the EventEmitter API in Node's standard library, but it has a few differences.
|
||||
* The goal is to allow multiple modules to attach arbitrary listeners to the same emitter, without
|
||||
* letting the modules know about each other at all.
|
||||
* 1. It has no special keys like `error` and `newListener`, which would allow modules to detect when
|
||||
* another module throws an error or registers a listener.
|
||||
* 2. It calls listener functions without any `this` value. (`EventEmitter` calls listeners with a
|
||||
* `this` value of the emitter instance, which would give listeners access to other listeners.)
|
||||
* @returns {SafeEmitter} An emitter
|
||||
*/
|
||||
module.exports = () => {
|
||||
const listeners = Object.create(null);
|
||||
|
||||
return Object.freeze({
|
||||
on(eventName, listener) {
|
||||
if (eventName in listeners) {
|
||||
listeners[eventName].push(listener);
|
||||
} else {
|
||||
listeners[eventName] = [listener];
|
||||
}
|
||||
},
|
||||
emit(eventName, ...args) {
|
||||
if (eventName in listeners) {
|
||||
listeners[eventName].forEach(listener => listener(...args));
|
||||
}
|
||||
},
|
||||
eventNames() {
|
||||
return Object.keys(listeners);
|
||||
}
|
||||
});
|
||||
};
|
||||
152
qwen/nodejs/node_modules/eslint/lib/linter/source-code-fixer.js
generated
vendored
Normal file
152
qwen/nodejs/node_modules/eslint/lib/linter/source-code-fixer.js
generated
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* @fileoverview An object that caches and applies source code fixes.
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const debug = require("debug")("eslint:source-code-fixer");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const BOM = "\uFEFF";
|
||||
|
||||
/**
|
||||
* Compares items in a messages array by range.
|
||||
* @param {Message} a The first message.
|
||||
* @param {Message} b The second message.
|
||||
* @returns {int} -1 if a comes before b, 1 if a comes after b, 0 if equal.
|
||||
* @private
|
||||
*/
|
||||
function compareMessagesByFixRange(a, b) {
|
||||
return a.fix.range[0] - b.fix.range[0] || a.fix.range[1] - b.fix.range[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares items in a messages array by line and column.
|
||||
* @param {Message} a The first message.
|
||||
* @param {Message} b The second message.
|
||||
* @returns {int} -1 if a comes before b, 1 if a comes after b, 0 if equal.
|
||||
* @private
|
||||
*/
|
||||
function compareMessagesByLocation(a, b) {
|
||||
return a.line - b.line || a.column - b.column;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Utility for apply fixes to source code.
|
||||
* @constructor
|
||||
*/
|
||||
function SourceCodeFixer() {
|
||||
Object.freeze(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the fixes specified by the messages to the given text. Tries to be
|
||||
* smart about the fixes and won't apply fixes over the same area in the text.
|
||||
* @param {string} sourceText The text to apply the changes to.
|
||||
* @param {Message[]} messages The array of messages reported by ESLint.
|
||||
* @param {boolean|Function} [shouldFix=true] Determines whether each message should be fixed
|
||||
* @returns {Object} An object containing the fixed text and any unfixed messages.
|
||||
*/
|
||||
SourceCodeFixer.applyFixes = function(sourceText, messages, shouldFix) {
|
||||
debug("Applying fixes");
|
||||
|
||||
if (shouldFix === false) {
|
||||
debug("shouldFix parameter was false, not attempting fixes");
|
||||
return {
|
||||
fixed: false,
|
||||
messages,
|
||||
output: sourceText
|
||||
};
|
||||
}
|
||||
|
||||
// clone the array
|
||||
const remainingMessages = [],
|
||||
fixes = [],
|
||||
bom = sourceText.startsWith(BOM) ? BOM : "",
|
||||
text = bom ? sourceText.slice(1) : sourceText;
|
||||
let lastPos = Number.NEGATIVE_INFINITY,
|
||||
output = bom;
|
||||
|
||||
/**
|
||||
* Try to use the 'fix' from a problem.
|
||||
* @param {Message} problem The message object to apply fixes from
|
||||
* @returns {boolean} Whether fix was successfully applied
|
||||
*/
|
||||
function attemptFix(problem) {
|
||||
const fix = problem.fix;
|
||||
const start = fix.range[0];
|
||||
const end = fix.range[1];
|
||||
|
||||
// Remain it as a problem if it's overlapped or it's a negative range
|
||||
if (lastPos >= start || start > end) {
|
||||
remainingMessages.push(problem);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove BOM.
|
||||
if ((start < 0 && end >= 0) || (start === 0 && fix.text.startsWith(BOM))) {
|
||||
output = "";
|
||||
}
|
||||
|
||||
// Make output to this fix.
|
||||
output += text.slice(Math.max(0, lastPos), Math.max(0, start));
|
||||
output += fix.text;
|
||||
lastPos = end;
|
||||
return true;
|
||||
}
|
||||
|
||||
messages.forEach(problem => {
|
||||
if (Object.prototype.hasOwnProperty.call(problem, "fix")) {
|
||||
fixes.push(problem);
|
||||
} else {
|
||||
remainingMessages.push(problem);
|
||||
}
|
||||
});
|
||||
|
||||
if (fixes.length) {
|
||||
debug("Found fixes to apply");
|
||||
let fixesWereApplied = false;
|
||||
|
||||
for (const problem of fixes.sort(compareMessagesByFixRange)) {
|
||||
if (typeof shouldFix !== "function" || shouldFix(problem)) {
|
||||
attemptFix(problem);
|
||||
|
||||
/*
|
||||
* The only time attemptFix will fail is if a previous fix was
|
||||
* applied which conflicts with it. So we can mark this as true.
|
||||
*/
|
||||
fixesWereApplied = true;
|
||||
} else {
|
||||
remainingMessages.push(problem);
|
||||
}
|
||||
}
|
||||
output += text.slice(Math.max(0, lastPos));
|
||||
|
||||
return {
|
||||
fixed: fixesWereApplied,
|
||||
messages: remainingMessages.sort(compareMessagesByLocation),
|
||||
output
|
||||
};
|
||||
}
|
||||
|
||||
debug("No fixes to apply");
|
||||
return {
|
||||
fixed: false,
|
||||
messages,
|
||||
output: bom + text
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
module.exports = SourceCodeFixer;
|
||||
161
qwen/nodejs/node_modules/eslint/lib/linter/timing.js
generated
vendored
Normal file
161
qwen/nodejs/node_modules/eslint/lib/linter/timing.js
generated
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* @fileoverview Tracks performance of individual rules.
|
||||
* @author Brandon Mills
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/* c8 ignore next */
|
||||
/**
|
||||
* Align the string to left
|
||||
* @param {string} str string to evaluate
|
||||
* @param {int} len length of the string
|
||||
* @param {string} ch delimiter character
|
||||
* @returns {string} modified string
|
||||
* @private
|
||||
*/
|
||||
function alignLeft(str, len, ch) {
|
||||
return str + new Array(len - str.length + 1).join(ch || " ");
|
||||
}
|
||||
|
||||
/* c8 ignore next */
|
||||
/**
|
||||
* Align the string to right
|
||||
* @param {string} str string to evaluate
|
||||
* @param {int} len length of the string
|
||||
* @param {string} ch delimiter character
|
||||
* @returns {string} modified string
|
||||
* @private
|
||||
*/
|
||||
function alignRight(str, len, ch) {
|
||||
return new Array(len - str.length + 1).join(ch || " ") + str;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Module definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const enabled = !!process.env.TIMING;
|
||||
|
||||
const HEADERS = ["Rule", "Time (ms)", "Relative"];
|
||||
const ALIGN = [alignLeft, alignRight, alignRight];
|
||||
|
||||
/**
|
||||
* Decide how many rules to show in the output list.
|
||||
* @returns {number} the number of rules to show
|
||||
*/
|
||||
function getListSize() {
|
||||
const MINIMUM_SIZE = 10;
|
||||
|
||||
if (typeof process.env.TIMING !== "string") {
|
||||
return MINIMUM_SIZE;
|
||||
}
|
||||
|
||||
if (process.env.TIMING.toLowerCase() === "all") {
|
||||
return Number.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
const TIMING_ENV_VAR_AS_INTEGER = Number.parseInt(process.env.TIMING, 10);
|
||||
|
||||
return TIMING_ENV_VAR_AS_INTEGER > 10 ? TIMING_ENV_VAR_AS_INTEGER : MINIMUM_SIZE;
|
||||
}
|
||||
|
||||
/* c8 ignore next */
|
||||
/**
|
||||
* display the data
|
||||
* @param {Object} data Data object to be displayed
|
||||
* @returns {void} prints modified string with console.log
|
||||
* @private
|
||||
*/
|
||||
function display(data) {
|
||||
let total = 0;
|
||||
const rows = Object.keys(data)
|
||||
.map(key => {
|
||||
const time = data[key];
|
||||
|
||||
total += time;
|
||||
return [key, time];
|
||||
})
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.slice(0, getListSize());
|
||||
|
||||
rows.forEach(row => {
|
||||
row.push(`${(row[1] * 100 / total).toFixed(1)}%`);
|
||||
row[1] = row[1].toFixed(3);
|
||||
});
|
||||
|
||||
rows.unshift(HEADERS);
|
||||
|
||||
const widths = [];
|
||||
|
||||
rows.forEach(row => {
|
||||
const len = row.length;
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
const n = row[i].length;
|
||||
|
||||
if (!widths[i] || n > widths[i]) {
|
||||
widths[i] = n;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const table = rows.map(row => (
|
||||
row
|
||||
.map((cell, index) => ALIGN[index](cell, widths[index]))
|
||||
.join(" | ")
|
||||
));
|
||||
|
||||
table.splice(1, 0, widths.map((width, index) => {
|
||||
const extraAlignment = index !== 0 && index !== widths.length - 1 ? 2 : 1;
|
||||
|
||||
return ALIGN[index](":", width + extraAlignment, "-");
|
||||
}).join("|"));
|
||||
|
||||
console.log(table.join("\n")); // eslint-disable-line no-console -- Debugging function
|
||||
}
|
||||
|
||||
/* c8 ignore next */
|
||||
module.exports = (function() {
|
||||
|
||||
const data = Object.create(null);
|
||||
|
||||
/**
|
||||
* Time the run
|
||||
* @param {any} key key from the data object
|
||||
* @param {Function} fn function to be called
|
||||
* @returns {Function} function to be executed
|
||||
* @private
|
||||
*/
|
||||
function time(key, fn) {
|
||||
if (typeof data[key] === "undefined") {
|
||||
data[key] = 0;
|
||||
}
|
||||
|
||||
return function(...args) {
|
||||
let t = process.hrtime();
|
||||
const result = fn(...args);
|
||||
|
||||
t = process.hrtime(t);
|
||||
data[key] += t[0] * 1e3 + t[1] / 1e6;
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
process.on("exit", () => {
|
||||
display(data);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
time,
|
||||
enabled,
|
||||
getListSize
|
||||
};
|
||||
|
||||
}());
|
||||
398
qwen/nodejs/node_modules/eslint/lib/options.js
generated
vendored
Normal file
398
qwen/nodejs/node_modules/eslint/lib/options.js
generated
vendored
Normal file
@@ -0,0 +1,398 @@
|
||||
/**
|
||||
* @fileoverview Options configuration for optionator.
|
||||
* @author George Zahariev
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const optionator = require("optionator");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Typedefs
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The options object parsed by Optionator.
|
||||
* @typedef {Object} ParsedCLIOptions
|
||||
* @property {boolean} cache Only check changed files
|
||||
* @property {string} cacheFile Path to the cache file. Deprecated: use --cache-location
|
||||
* @property {string} [cacheLocation] Path to the cache file or directory
|
||||
* @property {"metadata" | "content"} cacheStrategy Strategy to use for detecting changed files in the cache
|
||||
* @property {boolean} [color] Force enabling/disabling of color
|
||||
* @property {string} [config] Use this configuration, overriding .eslintrc.* config options if present
|
||||
* @property {boolean} debug Output debugging information
|
||||
* @property {string[]} [env] Specify environments
|
||||
* @property {boolean} envInfo Output execution environment information
|
||||
* @property {boolean} errorOnUnmatchedPattern Prevent errors when pattern is unmatched
|
||||
* @property {boolean} eslintrc Disable use of configuration from .eslintrc.*
|
||||
* @property {string[]} [ext] Specify JavaScript file extensions
|
||||
* @property {boolean} fix Automatically fix problems
|
||||
* @property {boolean} fixDryRun Automatically fix problems without saving the changes to the file system
|
||||
* @property {("directive" | "problem" | "suggestion" | "layout")[]} [fixType] Specify the types of fixes to apply (directive, problem, suggestion, layout)
|
||||
* @property {string} format Use a specific output format
|
||||
* @property {string[]} [global] Define global variables
|
||||
* @property {boolean} [help] Show help
|
||||
* @property {boolean} ignore Disable use of ignore files and patterns
|
||||
* @property {string} [ignorePath] Specify path of ignore file
|
||||
* @property {string[]} [ignorePattern] Pattern of files to ignore (in addition to those in .eslintignore)
|
||||
* @property {boolean} init Run config initialization wizard
|
||||
* @property {boolean} inlineConfig Prevent comments from changing config or rules
|
||||
* @property {number} maxWarnings Number of warnings to trigger nonzero exit code
|
||||
* @property {string} [outputFile] Specify file to write report to
|
||||
* @property {string} [parser] Specify the parser to be used
|
||||
* @property {Object} [parserOptions] Specify parser options
|
||||
* @property {string[]} [plugin] Specify plugins
|
||||
* @property {string} [printConfig] Print the configuration for the given file
|
||||
* @property {boolean | undefined} reportUnusedDisableDirectives Adds reported errors for unused eslint-disable and eslint-enable directives
|
||||
* @property {string | undefined} reportUnusedDisableDirectivesSeverity A severity string indicating if and how unused disable and enable directives should be tracked and reported.
|
||||
* @property {string} [resolvePluginsRelativeTo] A folder where plugins should be resolved from, CWD by default
|
||||
* @property {Object} [rule] Specify rules
|
||||
* @property {string[]} [rulesdir] Load additional rules from this directory. Deprecated: Use rules from plugins
|
||||
* @property {boolean} stdin Lint code provided on <STDIN>
|
||||
* @property {string} [stdinFilename] Specify filename to process STDIN as
|
||||
* @property {boolean} quiet Report errors only
|
||||
* @property {boolean} [version] Output the version number
|
||||
* @property {boolean} warnIgnored Show warnings when the file list includes ignored files
|
||||
* @property {string[]} _ Positional filenames or patterns
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Initialization and Public Interface
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// exports "parse(args)", "generateHelp()", and "generateHelpForOption(optionName)"
|
||||
|
||||
/**
|
||||
* Creates the CLI options for ESLint.
|
||||
* @param {boolean} usingFlatConfig Indicates if flat config is being used.
|
||||
* @returns {Object} The optionator instance.
|
||||
*/
|
||||
module.exports = function(usingFlatConfig) {
|
||||
|
||||
let lookupFlag;
|
||||
|
||||
if (usingFlatConfig) {
|
||||
lookupFlag = {
|
||||
option: "config-lookup",
|
||||
type: "Boolean",
|
||||
default: "true",
|
||||
description: "Disable look up for eslint.config.js"
|
||||
};
|
||||
} else {
|
||||
lookupFlag = {
|
||||
option: "eslintrc",
|
||||
type: "Boolean",
|
||||
default: "true",
|
||||
description: "Disable use of configuration from .eslintrc.*"
|
||||
};
|
||||
}
|
||||
|
||||
let envFlag;
|
||||
|
||||
if (!usingFlatConfig) {
|
||||
envFlag = {
|
||||
option: "env",
|
||||
type: "[String]",
|
||||
description: "Specify environments"
|
||||
};
|
||||
}
|
||||
|
||||
let extFlag;
|
||||
|
||||
if (!usingFlatConfig) {
|
||||
extFlag = {
|
||||
option: "ext",
|
||||
type: "[String]",
|
||||
description: "Specify JavaScript file extensions"
|
||||
};
|
||||
}
|
||||
|
||||
let resolvePluginsFlag;
|
||||
|
||||
if (!usingFlatConfig) {
|
||||
resolvePluginsFlag = {
|
||||
option: "resolve-plugins-relative-to",
|
||||
type: "path::String",
|
||||
description: "A folder where plugins should be resolved from, CWD by default"
|
||||
};
|
||||
}
|
||||
|
||||
let rulesDirFlag;
|
||||
|
||||
if (!usingFlatConfig) {
|
||||
rulesDirFlag = {
|
||||
option: "rulesdir",
|
||||
type: "[path::String]",
|
||||
description: "Load additional rules from this directory. Deprecated: Use rules from plugins"
|
||||
};
|
||||
}
|
||||
|
||||
let ignorePathFlag;
|
||||
|
||||
if (!usingFlatConfig) {
|
||||
ignorePathFlag = {
|
||||
option: "ignore-path",
|
||||
type: "path::String",
|
||||
description: "Specify path of ignore file"
|
||||
};
|
||||
}
|
||||
|
||||
let warnIgnoredFlag;
|
||||
|
||||
if (usingFlatConfig) {
|
||||
warnIgnoredFlag = {
|
||||
option: "warn-ignored",
|
||||
type: "Boolean",
|
||||
default: "true",
|
||||
description: "Suppress warnings when the file list includes ignored files"
|
||||
};
|
||||
}
|
||||
|
||||
return optionator({
|
||||
prepend: "eslint [options] file.js [file.js] [dir]",
|
||||
defaults: {
|
||||
concatRepeatedArrays: true,
|
||||
mergeRepeatedObjects: true
|
||||
},
|
||||
options: [
|
||||
{
|
||||
heading: "Basic configuration"
|
||||
},
|
||||
lookupFlag,
|
||||
{
|
||||
option: "config",
|
||||
alias: "c",
|
||||
type: "path::String",
|
||||
description: usingFlatConfig
|
||||
? "Use this configuration instead of eslint.config.js, eslint.config.mjs, or eslint.config.cjs"
|
||||
: "Use this configuration, overriding .eslintrc.* config options if present"
|
||||
},
|
||||
envFlag,
|
||||
extFlag,
|
||||
{
|
||||
option: "global",
|
||||
type: "[String]",
|
||||
description: "Define global variables"
|
||||
},
|
||||
{
|
||||
option: "parser",
|
||||
type: "String",
|
||||
description: "Specify the parser to be used"
|
||||
},
|
||||
{
|
||||
option: "parser-options",
|
||||
type: "Object",
|
||||
description: "Specify parser options"
|
||||
},
|
||||
resolvePluginsFlag,
|
||||
{
|
||||
heading: "Specify Rules and Plugins"
|
||||
},
|
||||
{
|
||||
option: "plugin",
|
||||
type: "[String]",
|
||||
description: "Specify plugins"
|
||||
},
|
||||
{
|
||||
option: "rule",
|
||||
type: "Object",
|
||||
description: "Specify rules"
|
||||
},
|
||||
rulesDirFlag,
|
||||
{
|
||||
heading: "Fix Problems"
|
||||
},
|
||||
{
|
||||
option: "fix",
|
||||
type: "Boolean",
|
||||
default: false,
|
||||
description: "Automatically fix problems"
|
||||
},
|
||||
{
|
||||
option: "fix-dry-run",
|
||||
type: "Boolean",
|
||||
default: false,
|
||||
description: "Automatically fix problems without saving the changes to the file system"
|
||||
},
|
||||
{
|
||||
option: "fix-type",
|
||||
type: "Array",
|
||||
description: "Specify the types of fixes to apply (directive, problem, suggestion, layout)"
|
||||
},
|
||||
{
|
||||
heading: "Ignore Files"
|
||||
},
|
||||
ignorePathFlag,
|
||||
{
|
||||
option: "ignore",
|
||||
type: "Boolean",
|
||||
default: "true",
|
||||
description: "Disable use of ignore files and patterns"
|
||||
},
|
||||
{
|
||||
option: "ignore-pattern",
|
||||
type: "[String]",
|
||||
description: "Pattern of files to ignore (in addition to those in .eslintignore)",
|
||||
concatRepeatedArrays: [true, {
|
||||
oneValuePerFlag: true
|
||||
}]
|
||||
},
|
||||
{
|
||||
heading: "Use stdin"
|
||||
},
|
||||
{
|
||||
option: "stdin",
|
||||
type: "Boolean",
|
||||
default: "false",
|
||||
description: "Lint code provided on <STDIN>"
|
||||
},
|
||||
{
|
||||
option: "stdin-filename",
|
||||
type: "String",
|
||||
description: "Specify filename to process STDIN as"
|
||||
},
|
||||
{
|
||||
heading: "Handle Warnings"
|
||||
},
|
||||
{
|
||||
option: "quiet",
|
||||
type: "Boolean",
|
||||
default: "false",
|
||||
description: "Report errors only"
|
||||
},
|
||||
{
|
||||
option: "max-warnings",
|
||||
type: "Int",
|
||||
default: "-1",
|
||||
description: "Number of warnings to trigger nonzero exit code"
|
||||
},
|
||||
{
|
||||
heading: "Output"
|
||||
},
|
||||
{
|
||||
option: "output-file",
|
||||
alias: "o",
|
||||
type: "path::String",
|
||||
description: "Specify file to write report to"
|
||||
},
|
||||
{
|
||||
option: "format",
|
||||
alias: "f",
|
||||
type: "String",
|
||||
default: "stylish",
|
||||
description: "Use a specific output format"
|
||||
},
|
||||
{
|
||||
option: "color",
|
||||
type: "Boolean",
|
||||
alias: "no-color",
|
||||
description: "Force enabling/disabling of color"
|
||||
},
|
||||
{
|
||||
heading: "Inline configuration comments"
|
||||
},
|
||||
{
|
||||
option: "inline-config",
|
||||
type: "Boolean",
|
||||
default: "true",
|
||||
description: "Prevent comments from changing config or rules"
|
||||
},
|
||||
{
|
||||
option: "report-unused-disable-directives",
|
||||
type: "Boolean",
|
||||
default: void 0,
|
||||
description: "Adds reported errors for unused eslint-disable and eslint-enable directives"
|
||||
},
|
||||
{
|
||||
option: "report-unused-disable-directives-severity",
|
||||
type: "String",
|
||||
default: void 0,
|
||||
description: "Chooses severity level for reporting unused eslint-disable and eslint-enable directives",
|
||||
enum: ["off", "warn", "error", "0", "1", "2"]
|
||||
},
|
||||
{
|
||||
heading: "Caching"
|
||||
},
|
||||
{
|
||||
option: "cache",
|
||||
type: "Boolean",
|
||||
default: "false",
|
||||
description: "Only check changed files"
|
||||
},
|
||||
{
|
||||
option: "cache-file",
|
||||
type: "path::String",
|
||||
default: ".eslintcache",
|
||||
description: "Path to the cache file. Deprecated: use --cache-location"
|
||||
},
|
||||
{
|
||||
option: "cache-location",
|
||||
type: "path::String",
|
||||
description: "Path to the cache file or directory"
|
||||
},
|
||||
{
|
||||
option: "cache-strategy",
|
||||
dependsOn: ["cache"],
|
||||
type: "String",
|
||||
default: "metadata",
|
||||
enum: ["metadata", "content"],
|
||||
description: "Strategy to use for detecting changed files in the cache"
|
||||
},
|
||||
{
|
||||
heading: "Miscellaneous"
|
||||
},
|
||||
{
|
||||
option: "init",
|
||||
type: "Boolean",
|
||||
default: "false",
|
||||
description: "Run config initialization wizard"
|
||||
},
|
||||
{
|
||||
option: "env-info",
|
||||
type: "Boolean",
|
||||
default: "false",
|
||||
description: "Output execution environment information"
|
||||
},
|
||||
{
|
||||
option: "error-on-unmatched-pattern",
|
||||
type: "Boolean",
|
||||
default: "true",
|
||||
description: "Prevent errors when pattern is unmatched"
|
||||
},
|
||||
{
|
||||
option: "exit-on-fatal-error",
|
||||
type: "Boolean",
|
||||
default: "false",
|
||||
description: "Exit with exit code 2 in case of fatal error"
|
||||
},
|
||||
warnIgnoredFlag,
|
||||
{
|
||||
option: "debug",
|
||||
type: "Boolean",
|
||||
default: false,
|
||||
description: "Output debugging information"
|
||||
},
|
||||
{
|
||||
option: "help",
|
||||
alias: "h",
|
||||
type: "Boolean",
|
||||
description: "Show help"
|
||||
},
|
||||
{
|
||||
option: "version",
|
||||
alias: "v",
|
||||
type: "Boolean",
|
||||
description: "Output the version number"
|
||||
},
|
||||
{
|
||||
option: "print-config",
|
||||
type: "path::String",
|
||||
description: "Print the configuration for the given file"
|
||||
}
|
||||
].filter(value => !!value)
|
||||
});
|
||||
};
|
||||
1131
qwen/nodejs/node_modules/eslint/lib/rule-tester/flat-rule-tester.js
generated
vendored
Normal file
1131
qwen/nodejs/node_modules/eslint/lib/rule-tester/flat-rule-tester.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5
qwen/nodejs/node_modules/eslint/lib/rule-tester/index.js
generated
vendored
Normal file
5
qwen/nodejs/node_modules/eslint/lib/rule-tester/index.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
RuleTester: require("./rule-tester")
|
||||
};
|
||||
1206
qwen/nodejs/node_modules/eslint/lib/rule-tester/rule-tester.js
generated
vendored
Normal file
1206
qwen/nodejs/node_modules/eslint/lib/rule-tester/rule-tester.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
346
qwen/nodejs/node_modules/eslint/lib/rules/accessor-pairs.js
generated
vendored
Normal file
346
qwen/nodejs/node_modules/eslint/lib/rules/accessor-pairs.js
generated
vendored
Normal file
@@ -0,0 +1,346 @@
|
||||
/**
|
||||
* @fileoverview Rule to enforce getter and setter pairs in objects and classes.
|
||||
* @author Gyandeep Singh
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Typedefs
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Property name if it can be computed statically, otherwise the list of the tokens of the key node.
|
||||
* @typedef {string|Token[]} Key
|
||||
*/
|
||||
|
||||
/**
|
||||
* Accessor nodes with the same key.
|
||||
* @typedef {Object} AccessorData
|
||||
* @property {Key} key Accessor's key
|
||||
* @property {ASTNode[]} getters List of getter nodes.
|
||||
* @property {ASTNode[]} setters List of setter nodes.
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks whether or not the given lists represent the equal tokens in the same order.
|
||||
* Tokens are compared by their properties, not by instance.
|
||||
* @param {Token[]} left First list of tokens.
|
||||
* @param {Token[]} right Second list of tokens.
|
||||
* @returns {boolean} `true` if the lists have same tokens.
|
||||
*/
|
||||
function areEqualTokenLists(left, right) {
|
||||
if (left.length !== right.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < left.length; i++) {
|
||||
const leftToken = left[i],
|
||||
rightToken = right[i];
|
||||
|
||||
if (leftToken.type !== rightToken.type || leftToken.value !== rightToken.value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not the given keys are equal.
|
||||
* @param {Key} left First key.
|
||||
* @param {Key} right Second key.
|
||||
* @returns {boolean} `true` if the keys are equal.
|
||||
*/
|
||||
function areEqualKeys(left, right) {
|
||||
if (typeof left === "string" && typeof right === "string") {
|
||||
|
||||
// Statically computed names.
|
||||
return left === right;
|
||||
}
|
||||
if (Array.isArray(left) && Array.isArray(right)) {
|
||||
|
||||
// Token lists.
|
||||
return areEqualTokenLists(left, right);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a given node is of an accessor kind ('get' or 'set').
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @returns {boolean} `true` if the node is of an accessor kind.
|
||||
*/
|
||||
function isAccessorKind(node) {
|
||||
return node.kind === "get" || node.kind === "set";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a given node is an argument of a specified method call.
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @param {number} index An expected index of the node in arguments.
|
||||
* @param {string} object An expected name of the object of the method.
|
||||
* @param {string} property An expected name of the method.
|
||||
* @returns {boolean} `true` if the node is an argument of the specified method call.
|
||||
*/
|
||||
function isArgumentOfMethodCall(node, index, object, property) {
|
||||
const parent = node.parent;
|
||||
|
||||
return (
|
||||
parent.type === "CallExpression" &&
|
||||
astUtils.isSpecificMemberAccess(parent.callee, object, property) &&
|
||||
parent.arguments[index] === node
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a given node is a property descriptor.
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @returns {boolean} `true` if the node is a property descriptor.
|
||||
*/
|
||||
function isPropertyDescriptor(node) {
|
||||
|
||||
// Object.defineProperty(obj, "foo", {set: ...})
|
||||
if (isArgumentOfMethodCall(node, 2, "Object", "defineProperty") ||
|
||||
isArgumentOfMethodCall(node, 2, "Reflect", "defineProperty")
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Object.defineProperties(obj, {foo: {set: ...}})
|
||||
* Object.create(proto, {foo: {set: ...}})
|
||||
*/
|
||||
const grandparent = node.parent.parent;
|
||||
|
||||
return grandparent.type === "ObjectExpression" && (
|
||||
isArgumentOfMethodCall(grandparent, 1, "Object", "create") ||
|
||||
isArgumentOfMethodCall(grandparent, 1, "Object", "defineProperties")
|
||||
);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Enforce getter and setter pairs in objects and classes",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/accessor-pairs"
|
||||
},
|
||||
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
getWithoutSet: {
|
||||
type: "boolean",
|
||||
default: false
|
||||
},
|
||||
setWithoutGet: {
|
||||
type: "boolean",
|
||||
default: true
|
||||
},
|
||||
enforceForClassMembers: {
|
||||
type: "boolean",
|
||||
default: true
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
|
||||
messages: {
|
||||
missingGetterInPropertyDescriptor: "Getter is not present in property descriptor.",
|
||||
missingSetterInPropertyDescriptor: "Setter is not present in property descriptor.",
|
||||
missingGetterInObjectLiteral: "Getter is not present for {{ name }}.",
|
||||
missingSetterInObjectLiteral: "Setter is not present for {{ name }}.",
|
||||
missingGetterInClass: "Getter is not present for class {{ name }}.",
|
||||
missingSetterInClass: "Setter is not present for class {{ name }}."
|
||||
}
|
||||
},
|
||||
create(context) {
|
||||
const config = context.options[0] || {};
|
||||
const checkGetWithoutSet = config.getWithoutSet === true;
|
||||
const checkSetWithoutGet = config.setWithoutGet !== false;
|
||||
const enforceForClassMembers = config.enforceForClassMembers !== false;
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Reports the given node.
|
||||
* @param {ASTNode} node The node to report.
|
||||
* @param {string} messageKind "missingGetter" or "missingSetter".
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function report(node, messageKind) {
|
||||
if (node.type === "Property") {
|
||||
context.report({
|
||||
node,
|
||||
messageId: `${messageKind}InObjectLiteral`,
|
||||
loc: astUtils.getFunctionHeadLoc(node.value, sourceCode),
|
||||
data: { name: astUtils.getFunctionNameWithKind(node.value) }
|
||||
});
|
||||
} else if (node.type === "MethodDefinition") {
|
||||
context.report({
|
||||
node,
|
||||
messageId: `${messageKind}InClass`,
|
||||
loc: astUtils.getFunctionHeadLoc(node.value, sourceCode),
|
||||
data: { name: astUtils.getFunctionNameWithKind(node.value) }
|
||||
});
|
||||
} else {
|
||||
context.report({
|
||||
node,
|
||||
messageId: `${messageKind}InPropertyDescriptor`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports each of the nodes in the given list using the same messageId.
|
||||
* @param {ASTNode[]} nodes Nodes to report.
|
||||
* @param {string} messageKind "missingGetter" or "missingSetter".
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function reportList(nodes, messageKind) {
|
||||
for (const node of nodes) {
|
||||
report(node, messageKind);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks accessor pairs in the given list of nodes.
|
||||
* @param {ASTNode[]} nodes The list to check.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function checkList(nodes) {
|
||||
const accessors = [];
|
||||
let found = false;
|
||||
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const node = nodes[i];
|
||||
|
||||
if (isAccessorKind(node)) {
|
||||
|
||||
// Creates a new `AccessorData` object for the given getter or setter node.
|
||||
const name = astUtils.getStaticPropertyName(node);
|
||||
const key = (name !== null) ? name : sourceCode.getTokens(node.key);
|
||||
|
||||
// Merges the given `AccessorData` object into the given accessors list.
|
||||
for (let j = 0; j < accessors.length; j++) {
|
||||
const accessor = accessors[j];
|
||||
|
||||
if (areEqualKeys(accessor.key, key)) {
|
||||
accessor.getters.push(...node.kind === "get" ? [node] : []);
|
||||
accessor.setters.push(...node.kind === "set" ? [node] : []);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
accessors.push({
|
||||
key,
|
||||
getters: node.kind === "get" ? [node] : [],
|
||||
setters: node.kind === "set" ? [node] : []
|
||||
});
|
||||
}
|
||||
found = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const { getters, setters } of accessors) {
|
||||
if (checkSetWithoutGet && setters.length && !getters.length) {
|
||||
reportList(setters, "missingGetter");
|
||||
}
|
||||
if (checkGetWithoutSet && getters.length && !setters.length) {
|
||||
reportList(getters, "missingSetter");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks accessor pairs in an object literal.
|
||||
* @param {ASTNode} node `ObjectExpression` node to check.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function checkObjectLiteral(node) {
|
||||
checkList(node.properties.filter(p => p.type === "Property"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks accessor pairs in a property descriptor.
|
||||
* @param {ASTNode} node Property descriptor `ObjectExpression` node to check.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function checkPropertyDescriptor(node) {
|
||||
const namesToCheck = new Set(node.properties
|
||||
.filter(p => p.type === "Property" && p.kind === "init" && !p.computed)
|
||||
.map(({ key }) => key.name));
|
||||
|
||||
const hasGetter = namesToCheck.has("get");
|
||||
const hasSetter = namesToCheck.has("set");
|
||||
|
||||
if (checkSetWithoutGet && hasSetter && !hasGetter) {
|
||||
report(node, "missingGetter");
|
||||
}
|
||||
if (checkGetWithoutSet && hasGetter && !hasSetter) {
|
||||
report(node, "missingSetter");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the given object expression as an object literal and as a possible property descriptor.
|
||||
* @param {ASTNode} node `ObjectExpression` node to check.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function checkObjectExpression(node) {
|
||||
checkObjectLiteral(node);
|
||||
if (isPropertyDescriptor(node)) {
|
||||
checkPropertyDescriptor(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the given class body.
|
||||
* @param {ASTNode} node `ClassBody` node to check.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function checkClassBody(node) {
|
||||
const methodDefinitions = node.body.filter(m => m.type === "MethodDefinition");
|
||||
|
||||
checkList(methodDefinitions.filter(m => m.static));
|
||||
checkList(methodDefinitions.filter(m => !m.static));
|
||||
}
|
||||
|
||||
const listeners = {};
|
||||
|
||||
if (checkSetWithoutGet || checkGetWithoutSet) {
|
||||
listeners.ObjectExpression = checkObjectExpression;
|
||||
if (enforceForClassMembers) {
|
||||
listeners.ClassBody = checkClassBody;
|
||||
}
|
||||
}
|
||||
|
||||
return listeners;
|
||||
}
|
||||
};
|
||||
261
qwen/nodejs/node_modules/eslint/lib/rules/array-bracket-newline.js
generated
vendored
Normal file
261
qwen/nodejs/node_modules/eslint/lib/rules/array-bracket-newline.js
generated
vendored
Normal file
@@ -0,0 +1,261 @@
|
||||
/**
|
||||
* @fileoverview Rule to enforce linebreaks after open and before close array brackets
|
||||
* @author Jan Peer Stöcklmair <https://github.com/JPeer264>
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
replacedBy: [],
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description: "Enforce linebreaks after opening and before closing array brackets",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/array-bracket-newline"
|
||||
},
|
||||
|
||||
fixable: "whitespace",
|
||||
|
||||
schema: [
|
||||
{
|
||||
oneOf: [
|
||||
{
|
||||
enum: ["always", "never", "consistent"]
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
multiline: {
|
||||
type: "boolean"
|
||||
},
|
||||
minItems: {
|
||||
type: ["integer", "null"],
|
||||
minimum: 0
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
messages: {
|
||||
unexpectedOpeningLinebreak: "There should be no linebreak after '['.",
|
||||
unexpectedClosingLinebreak: "There should be no linebreak before ']'.",
|
||||
missingOpeningLinebreak: "A linebreak is required after '['.",
|
||||
missingClosingLinebreak: "A linebreak is required before ']'."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Helpers
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Normalizes a given option value.
|
||||
* @param {string|Object|undefined} option An option value to parse.
|
||||
* @returns {{multiline: boolean, minItems: number}} Normalized option object.
|
||||
*/
|
||||
function normalizeOptionValue(option) {
|
||||
let consistent = false;
|
||||
let multiline = false;
|
||||
let minItems = 0;
|
||||
|
||||
if (option) {
|
||||
if (option === "consistent") {
|
||||
consistent = true;
|
||||
minItems = Number.POSITIVE_INFINITY;
|
||||
} else if (option === "always" || option.minItems === 0) {
|
||||
minItems = 0;
|
||||
} else if (option === "never") {
|
||||
minItems = Number.POSITIVE_INFINITY;
|
||||
} else {
|
||||
multiline = Boolean(option.multiline);
|
||||
minItems = option.minItems || Number.POSITIVE_INFINITY;
|
||||
}
|
||||
} else {
|
||||
consistent = false;
|
||||
multiline = true;
|
||||
minItems = Number.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
return { consistent, multiline, minItems };
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a given option value.
|
||||
* @param {string|Object|undefined} options An option value to parse.
|
||||
* @returns {{ArrayExpression: {multiline: boolean, minItems: number}, ArrayPattern: {multiline: boolean, minItems: number}}} Normalized option object.
|
||||
*/
|
||||
function normalizeOptions(options) {
|
||||
const value = normalizeOptionValue(options);
|
||||
|
||||
return { ArrayExpression: value, ArrayPattern: value };
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that there shouldn't be a linebreak after the first token
|
||||
* @param {ASTNode} node The node to report in the event of an error.
|
||||
* @param {Token} token The token to use for the report.
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportNoBeginningLinebreak(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc,
|
||||
messageId: "unexpectedOpeningLinebreak",
|
||||
fix(fixer) {
|
||||
const nextToken = sourceCode.getTokenAfter(token, { includeComments: true });
|
||||
|
||||
if (astUtils.isCommentToken(nextToken)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fixer.removeRange([token.range[1], nextToken.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that there shouldn't be a linebreak before the last token
|
||||
* @param {ASTNode} node The node to report in the event of an error.
|
||||
* @param {Token} token The token to use for the report.
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportNoEndingLinebreak(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc,
|
||||
messageId: "unexpectedClosingLinebreak",
|
||||
fix(fixer) {
|
||||
const previousToken = sourceCode.getTokenBefore(token, { includeComments: true });
|
||||
|
||||
if (astUtils.isCommentToken(previousToken)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fixer.removeRange([previousToken.range[1], token.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that there should be a linebreak after the first token
|
||||
* @param {ASTNode} node The node to report in the event of an error.
|
||||
* @param {Token} token The token to use for the report.
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportRequiredBeginningLinebreak(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc,
|
||||
messageId: "missingOpeningLinebreak",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(token, "\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that there should be a linebreak before the last token
|
||||
* @param {ASTNode} node The node to report in the event of an error.
|
||||
* @param {Token} token The token to use for the report.
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportRequiredEndingLinebreak(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc,
|
||||
messageId: "missingClosingLinebreak",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(token, "\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports a given node if it violated this rule.
|
||||
* @param {ASTNode} node A node to check. This is an ArrayExpression node or an ArrayPattern node.
|
||||
* @returns {void}
|
||||
*/
|
||||
function check(node) {
|
||||
const elements = node.elements;
|
||||
const normalizedOptions = normalizeOptions(context.options[0]);
|
||||
const options = normalizedOptions[node.type];
|
||||
const openBracket = sourceCode.getFirstToken(node);
|
||||
const closeBracket = sourceCode.getLastToken(node);
|
||||
const firstIncComment = sourceCode.getTokenAfter(openBracket, { includeComments: true });
|
||||
const lastIncComment = sourceCode.getTokenBefore(closeBracket, { includeComments: true });
|
||||
const first = sourceCode.getTokenAfter(openBracket);
|
||||
const last = sourceCode.getTokenBefore(closeBracket);
|
||||
|
||||
const needsLinebreaks = (
|
||||
elements.length >= options.minItems ||
|
||||
(
|
||||
options.multiline &&
|
||||
elements.length > 0 &&
|
||||
firstIncComment.loc.start.line !== lastIncComment.loc.end.line
|
||||
) ||
|
||||
(
|
||||
elements.length === 0 &&
|
||||
firstIncComment.type === "Block" &&
|
||||
firstIncComment.loc.start.line !== lastIncComment.loc.end.line &&
|
||||
firstIncComment === lastIncComment
|
||||
) ||
|
||||
(
|
||||
options.consistent &&
|
||||
openBracket.loc.end.line !== first.loc.start.line
|
||||
)
|
||||
);
|
||||
|
||||
/*
|
||||
* Use tokens or comments to check multiline or not.
|
||||
* But use only tokens to check whether linebreaks are needed.
|
||||
* This allows:
|
||||
* var arr = [ // eslint-disable-line foo
|
||||
* 'a'
|
||||
* ]
|
||||
*/
|
||||
|
||||
if (needsLinebreaks) {
|
||||
if (astUtils.isTokenOnSameLine(openBracket, first)) {
|
||||
reportRequiredBeginningLinebreak(node, openBracket);
|
||||
}
|
||||
if (astUtils.isTokenOnSameLine(last, closeBracket)) {
|
||||
reportRequiredEndingLinebreak(node, closeBracket);
|
||||
}
|
||||
} else {
|
||||
if (!astUtils.isTokenOnSameLine(openBracket, first)) {
|
||||
reportNoBeginningLinebreak(node, openBracket);
|
||||
}
|
||||
if (!astUtils.isTokenOnSameLine(last, closeBracket)) {
|
||||
reportNoEndingLinebreak(node, closeBracket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Public
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
ArrayPattern: check,
|
||||
ArrayExpression: check
|
||||
};
|
||||
}
|
||||
};
|
||||
244
qwen/nodejs/node_modules/eslint/lib/rules/array-bracket-spacing.js
generated
vendored
Normal file
244
qwen/nodejs/node_modules/eslint/lib/rules/array-bracket-spacing.js
generated
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
/**
|
||||
* @fileoverview Disallows or enforces spaces inside of array brackets.
|
||||
* @author Jamund Ferguson
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
replacedBy: [],
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description: "Enforce consistent spacing inside array brackets",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/array-bracket-spacing"
|
||||
},
|
||||
|
||||
fixable: "whitespace",
|
||||
|
||||
schema: [
|
||||
{
|
||||
enum: ["always", "never"]
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
singleValue: {
|
||||
type: "boolean"
|
||||
},
|
||||
objectsInArrays: {
|
||||
type: "boolean"
|
||||
},
|
||||
arraysInArrays: {
|
||||
type: "boolean"
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
],
|
||||
|
||||
messages: {
|
||||
unexpectedSpaceAfter: "There should be no space after '{{tokenValue}}'.",
|
||||
unexpectedSpaceBefore: "There should be no space before '{{tokenValue}}'.",
|
||||
missingSpaceAfter: "A space is required after '{{tokenValue}}'.",
|
||||
missingSpaceBefore: "A space is required before '{{tokenValue}}'."
|
||||
}
|
||||
},
|
||||
create(context) {
|
||||
const spaced = context.options[0] === "always",
|
||||
sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Determines whether an option is set, relative to the spacing option.
|
||||
* If spaced is "always", then check whether option is set to false.
|
||||
* If spaced is "never", then check whether option is set to true.
|
||||
* @param {Object} option The option to exclude.
|
||||
* @returns {boolean} Whether or not the property is excluded.
|
||||
*/
|
||||
function isOptionSet(option) {
|
||||
return context.options[1] ? context.options[1][option] === !spaced : false;
|
||||
}
|
||||
|
||||
const options = {
|
||||
spaced,
|
||||
singleElementException: isOptionSet("singleValue"),
|
||||
objectsInArraysException: isOptionSet("objectsInArrays"),
|
||||
arraysInArraysException: isOptionSet("arraysInArrays")
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Reports that there shouldn't be a space after the first token
|
||||
* @param {ASTNode} node The node to report in the event of an error.
|
||||
* @param {Token} token The token to use for the report.
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportNoBeginningSpace(node, token) {
|
||||
const nextToken = sourceCode.getTokenAfter(token);
|
||||
|
||||
context.report({
|
||||
node,
|
||||
loc: { start: token.loc.end, end: nextToken.loc.start },
|
||||
messageId: "unexpectedSpaceAfter",
|
||||
data: {
|
||||
tokenValue: token.value
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([token.range[1], nextToken.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that there shouldn't be a space before the last token
|
||||
* @param {ASTNode} node The node to report in the event of an error.
|
||||
* @param {Token} token The token to use for the report.
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportNoEndingSpace(node, token) {
|
||||
const previousToken = sourceCode.getTokenBefore(token);
|
||||
|
||||
context.report({
|
||||
node,
|
||||
loc: { start: previousToken.loc.end, end: token.loc.start },
|
||||
messageId: "unexpectedSpaceBefore",
|
||||
data: {
|
||||
tokenValue: token.value
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([previousToken.range[1], token.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that there should be a space after the first token
|
||||
* @param {ASTNode} node The node to report in the event of an error.
|
||||
* @param {Token} token The token to use for the report.
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportRequiredBeginningSpace(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc,
|
||||
messageId: "missingSpaceAfter",
|
||||
data: {
|
||||
tokenValue: token.value
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(token, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that there should be a space before the last token
|
||||
* @param {ASTNode} node The node to report in the event of an error.
|
||||
* @param {Token} token The token to use for the report.
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportRequiredEndingSpace(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc,
|
||||
messageId: "missingSpaceBefore",
|
||||
data: {
|
||||
tokenValue: token.value
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(token, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a node is an object type
|
||||
* @param {ASTNode} node The node to check.
|
||||
* @returns {boolean} Whether or not the node is an object type.
|
||||
*/
|
||||
function isObjectType(node) {
|
||||
return node && (node.type === "ObjectExpression" || node.type === "ObjectPattern");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a node is an array type
|
||||
* @param {ASTNode} node The node to check.
|
||||
* @returns {boolean} Whether or not the node is an array type.
|
||||
*/
|
||||
function isArrayType(node) {
|
||||
return node && (node.type === "ArrayExpression" || node.type === "ArrayPattern");
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the spacing around array brackets
|
||||
* @param {ASTNode} node The node we're checking for spacing
|
||||
* @returns {void}
|
||||
*/
|
||||
function validateArraySpacing(node) {
|
||||
if (options.spaced && node.elements.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const first = sourceCode.getFirstToken(node),
|
||||
second = sourceCode.getFirstToken(node, 1),
|
||||
last = node.typeAnnotation
|
||||
? sourceCode.getTokenBefore(node.typeAnnotation)
|
||||
: sourceCode.getLastToken(node),
|
||||
penultimate = sourceCode.getTokenBefore(last),
|
||||
firstElement = node.elements[0],
|
||||
lastElement = node.elements[node.elements.length - 1];
|
||||
|
||||
const openingBracketMustBeSpaced =
|
||||
options.objectsInArraysException && isObjectType(firstElement) ||
|
||||
options.arraysInArraysException && isArrayType(firstElement) ||
|
||||
options.singleElementException && node.elements.length === 1
|
||||
? !options.spaced : options.spaced;
|
||||
|
||||
const closingBracketMustBeSpaced =
|
||||
options.objectsInArraysException && isObjectType(lastElement) ||
|
||||
options.arraysInArraysException && isArrayType(lastElement) ||
|
||||
options.singleElementException && node.elements.length === 1
|
||||
? !options.spaced : options.spaced;
|
||||
|
||||
if (astUtils.isTokenOnSameLine(first, second)) {
|
||||
if (openingBracketMustBeSpaced && !sourceCode.isSpaceBetweenTokens(first, second)) {
|
||||
reportRequiredBeginningSpace(node, first);
|
||||
}
|
||||
if (!openingBracketMustBeSpaced && sourceCode.isSpaceBetweenTokens(first, second)) {
|
||||
reportNoBeginningSpace(node, first);
|
||||
}
|
||||
}
|
||||
|
||||
if (first !== penultimate && astUtils.isTokenOnSameLine(penultimate, last)) {
|
||||
if (closingBracketMustBeSpaced && !sourceCode.isSpaceBetweenTokens(penultimate, last)) {
|
||||
reportRequiredEndingSpace(node, last);
|
||||
}
|
||||
if (!closingBracketMustBeSpaced && sourceCode.isSpaceBetweenTokens(penultimate, last)) {
|
||||
reportNoEndingSpace(node, last);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
ArrayPattern: validateArraySpacing,
|
||||
ArrayExpression: validateArraySpacing
|
||||
};
|
||||
}
|
||||
};
|
||||
446
qwen/nodejs/node_modules/eslint/lib/rules/array-callback-return.js
generated
vendored
Normal file
446
qwen/nodejs/node_modules/eslint/lib/rules/array-callback-return.js
generated
vendored
Normal file
@@ -0,0 +1,446 @@
|
||||
/**
|
||||
* @fileoverview Rule to enforce return statements in callbacks of array's methods
|
||||
* @author Toru Nagashima
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const TARGET_NODE_TYPE = /^(?:Arrow)?FunctionExpression$/u;
|
||||
const TARGET_METHODS = /^(?:every|filter|find(?:Last)?(?:Index)?|flatMap|forEach|map|reduce(?:Right)?|some|sort|toSorted)$/u;
|
||||
|
||||
/**
|
||||
* Checks a given node is a member access which has the specified name's
|
||||
* property.
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @returns {boolean} `true` if the node is a member access which has
|
||||
* the specified name's property. The node may be a `(Chain|Member)Expression` node.
|
||||
*/
|
||||
function isTargetMethod(node) {
|
||||
return astUtils.isSpecificMemberAccess(node, null, TARGET_METHODS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks all segments in a set and returns true if any are reachable.
|
||||
* @param {Set<CodePathSegment>} segments The segments to check.
|
||||
* @returns {boolean} True if any segment is reachable; false otherwise.
|
||||
*/
|
||||
function isAnySegmentReachable(segments) {
|
||||
|
||||
for (const segment of segments) {
|
||||
if (segment.reachable) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a human-legible description of an array method
|
||||
* @param {string} arrayMethodName A method name to fully qualify
|
||||
* @returns {string} the method name prefixed with `Array.` if it is a class method,
|
||||
* or else `Array.prototype.` if it is an instance method.
|
||||
*/
|
||||
function fullMethodName(arrayMethodName) {
|
||||
if (["from", "of", "isArray"].includes(arrayMethodName)) {
|
||||
return "Array.".concat(arrayMethodName);
|
||||
}
|
||||
return "Array.prototype.".concat(arrayMethodName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a given node is a function expression which is the
|
||||
* callback of an array method, returning the method name.
|
||||
* @param {ASTNode} node A node to check. This is one of
|
||||
* FunctionExpression or ArrowFunctionExpression.
|
||||
* @returns {string} The method name if the node is a callback method,
|
||||
* null otherwise.
|
||||
*/
|
||||
function getArrayMethodName(node) {
|
||||
let currentNode = node;
|
||||
|
||||
while (currentNode) {
|
||||
const parent = currentNode.parent;
|
||||
|
||||
switch (parent.type) {
|
||||
|
||||
/*
|
||||
* Looks up the destination. e.g.,
|
||||
* foo.every(nativeFoo || function foo() { ... });
|
||||
*/
|
||||
case "LogicalExpression":
|
||||
case "ConditionalExpression":
|
||||
case "ChainExpression":
|
||||
currentNode = parent;
|
||||
break;
|
||||
|
||||
/*
|
||||
* If the upper function is IIFE, checks the destination of the return value.
|
||||
* e.g.
|
||||
* foo.every((function() {
|
||||
* // setup...
|
||||
* return function callback() { ... };
|
||||
* })());
|
||||
*/
|
||||
case "ReturnStatement": {
|
||||
const func = astUtils.getUpperFunction(parent);
|
||||
|
||||
if (func === null || !astUtils.isCallee(func)) {
|
||||
return null;
|
||||
}
|
||||
currentNode = func.parent;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* e.g.
|
||||
* Array.from([], function() {});
|
||||
* list.every(function() {});
|
||||
*/
|
||||
case "CallExpression":
|
||||
if (astUtils.isArrayFromMethod(parent.callee)) {
|
||||
if (
|
||||
parent.arguments.length >= 2 &&
|
||||
parent.arguments[1] === currentNode
|
||||
) {
|
||||
return "from";
|
||||
}
|
||||
}
|
||||
if (isTargetMethod(parent.callee)) {
|
||||
if (
|
||||
parent.arguments.length >= 1 &&
|
||||
parent.arguments[0] === currentNode
|
||||
) {
|
||||
return astUtils.getStaticPropertyName(parent.callee);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
// Otherwise this node is not target.
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/* c8 ignore next */
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given node is a void expression.
|
||||
* @param {ASTNode} node The node to check.
|
||||
* @returns {boolean} - `true` if the node is a void expression
|
||||
*/
|
||||
function isExpressionVoid(node) {
|
||||
return node.type === "UnaryExpression" && node.operator === "void";
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes the linting error by prepending "void " to the given node
|
||||
* @param {Object} sourceCode context given by context.sourceCode
|
||||
* @param {ASTNode} node The node to fix.
|
||||
* @param {Object} fixer The fixer object provided by ESLint.
|
||||
* @returns {Array<Object>} - An array of fix objects to apply to the node.
|
||||
*/
|
||||
function voidPrependFixer(sourceCode, node, fixer) {
|
||||
|
||||
const requiresParens =
|
||||
|
||||
// prepending `void ` will fail if the node has a lower precedence than void
|
||||
astUtils.getPrecedence(node) < astUtils.getPrecedence({ type: "UnaryExpression", operator: "void" }) &&
|
||||
|
||||
// check if there are parentheses around the node to avoid redundant parentheses
|
||||
!astUtils.isParenthesised(sourceCode, node);
|
||||
|
||||
// avoid parentheses issues
|
||||
const returnOrArrowToken = sourceCode.getTokenBefore(
|
||||
node,
|
||||
node.parent.type === "ArrowFunctionExpression"
|
||||
? astUtils.isArrowToken
|
||||
|
||||
// isReturnToken
|
||||
: token => token.type === "Keyword" && token.value === "return"
|
||||
);
|
||||
|
||||
const firstToken = sourceCode.getTokenAfter(returnOrArrowToken);
|
||||
|
||||
const prependSpace =
|
||||
|
||||
// is return token, as => allows void to be adjacent
|
||||
returnOrArrowToken.value === "return" &&
|
||||
|
||||
// If two tokens (return and "(") are adjacent
|
||||
returnOrArrowToken.range[1] === firstToken.range[0];
|
||||
|
||||
return [
|
||||
fixer.insertTextBefore(firstToken, `${prependSpace ? " " : ""}void ${requiresParens ? "(" : ""}`),
|
||||
fixer.insertTextAfter(node, requiresParens ? ")" : "")
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes the linting error by `wrapping {}` around the given node's body.
|
||||
* @param {Object} sourceCode context given by context.sourceCode
|
||||
* @param {ASTNode} node The node to fix.
|
||||
* @param {Object} fixer The fixer object provided by ESLint.
|
||||
* @returns {Array<Object>} - An array of fix objects to apply to the node.
|
||||
*/
|
||||
function curlyWrapFixer(sourceCode, node, fixer) {
|
||||
const arrowToken = sourceCode.getTokenBefore(node.body, astUtils.isArrowToken);
|
||||
const firstToken = sourceCode.getTokenAfter(arrowToken);
|
||||
const lastToken = sourceCode.getLastToken(node);
|
||||
|
||||
return [
|
||||
fixer.insertTextBefore(firstToken, "{"),
|
||||
fixer.insertTextAfter(lastToken, "}")
|
||||
];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "problem",
|
||||
|
||||
docs: {
|
||||
description: "Enforce `return` statements in callbacks of array methods",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/array-callback-return"
|
||||
},
|
||||
|
||||
// eslint-disable-next-line eslint-plugin/require-meta-has-suggestions -- false positive
|
||||
hasSuggestions: true,
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
allowImplicit: {
|
||||
type: "boolean",
|
||||
default: false
|
||||
},
|
||||
checkForEach: {
|
||||
type: "boolean",
|
||||
default: false
|
||||
},
|
||||
allowVoid: {
|
||||
type: "boolean",
|
||||
default: false
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
],
|
||||
|
||||
messages: {
|
||||
expectedAtEnd: "{{arrayMethodName}}() expects a value to be returned at the end of {{name}}.",
|
||||
expectedInside: "{{arrayMethodName}}() expects a return value from {{name}}.",
|
||||
expectedReturnValue: "{{arrayMethodName}}() expects a return value from {{name}}.",
|
||||
expectedNoReturnValue: "{{arrayMethodName}}() expects no useless return value from {{name}}.",
|
||||
wrapBraces: "Wrap the expression in `{}`.",
|
||||
prependVoid: "Prepend `void` to the expression."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
|
||||
const options = context.options[0] || { allowImplicit: false, checkForEach: false, allowVoid: false };
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
let funcInfo = {
|
||||
arrayMethodName: null,
|
||||
upper: null,
|
||||
codePath: null,
|
||||
hasReturn: false,
|
||||
shouldCheck: false,
|
||||
node: null
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether or not the last code path segment is reachable.
|
||||
* Then reports this function if the segment is reachable.
|
||||
*
|
||||
* If the last code path segment is reachable, there are paths which are not
|
||||
* returned or thrown.
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkLastSegment(node) {
|
||||
|
||||
if (!funcInfo.shouldCheck) {
|
||||
return;
|
||||
}
|
||||
|
||||
const messageAndSuggestions = { messageId: "", suggest: [] };
|
||||
|
||||
if (funcInfo.arrayMethodName === "forEach") {
|
||||
if (options.checkForEach && node.type === "ArrowFunctionExpression" && node.expression) {
|
||||
|
||||
if (options.allowVoid) {
|
||||
if (isExpressionVoid(node.body)) {
|
||||
return;
|
||||
}
|
||||
|
||||
messageAndSuggestions.messageId = "expectedNoReturnValue";
|
||||
messageAndSuggestions.suggest = [
|
||||
{
|
||||
messageId: "wrapBraces",
|
||||
fix(fixer) {
|
||||
return curlyWrapFixer(sourceCode, node, fixer);
|
||||
}
|
||||
},
|
||||
{
|
||||
messageId: "prependVoid",
|
||||
fix(fixer) {
|
||||
return voidPrependFixer(sourceCode, node.body, fixer);
|
||||
}
|
||||
}
|
||||
];
|
||||
} else {
|
||||
messageAndSuggestions.messageId = "expectedNoReturnValue";
|
||||
messageAndSuggestions.suggest = [{
|
||||
messageId: "wrapBraces",
|
||||
fix(fixer) {
|
||||
return curlyWrapFixer(sourceCode, node, fixer);
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (node.body.type === "BlockStatement" && isAnySegmentReachable(funcInfo.currentSegments)) {
|
||||
messageAndSuggestions.messageId = funcInfo.hasReturn ? "expectedAtEnd" : "expectedInside";
|
||||
}
|
||||
}
|
||||
|
||||
if (messageAndSuggestions.messageId) {
|
||||
const name = astUtils.getFunctionNameWithKind(node);
|
||||
|
||||
context.report({
|
||||
node,
|
||||
loc: astUtils.getFunctionHeadLoc(node, sourceCode),
|
||||
messageId: messageAndSuggestions.messageId,
|
||||
data: { name, arrayMethodName: fullMethodName(funcInfo.arrayMethodName) },
|
||||
suggest: messageAndSuggestions.suggest.length !== 0 ? messageAndSuggestions.suggest : null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
// Stacks this function's information.
|
||||
onCodePathStart(codePath, node) {
|
||||
|
||||
let methodName = null;
|
||||
|
||||
if (TARGET_NODE_TYPE.test(node.type)) {
|
||||
methodName = getArrayMethodName(node);
|
||||
}
|
||||
|
||||
funcInfo = {
|
||||
arrayMethodName: methodName,
|
||||
upper: funcInfo,
|
||||
codePath,
|
||||
hasReturn: false,
|
||||
shouldCheck:
|
||||
methodName &&
|
||||
!node.async &&
|
||||
!node.generator,
|
||||
node,
|
||||
currentSegments: new Set()
|
||||
};
|
||||
},
|
||||
|
||||
// Pops this function's information.
|
||||
onCodePathEnd() {
|
||||
funcInfo = funcInfo.upper;
|
||||
},
|
||||
|
||||
onUnreachableCodePathSegmentStart(segment) {
|
||||
funcInfo.currentSegments.add(segment);
|
||||
},
|
||||
|
||||
onUnreachableCodePathSegmentEnd(segment) {
|
||||
funcInfo.currentSegments.delete(segment);
|
||||
},
|
||||
|
||||
onCodePathSegmentStart(segment) {
|
||||
funcInfo.currentSegments.add(segment);
|
||||
},
|
||||
|
||||
onCodePathSegmentEnd(segment) {
|
||||
funcInfo.currentSegments.delete(segment);
|
||||
},
|
||||
|
||||
|
||||
// Checks the return statement is valid.
|
||||
ReturnStatement(node) {
|
||||
|
||||
if (!funcInfo.shouldCheck) {
|
||||
return;
|
||||
}
|
||||
|
||||
funcInfo.hasReturn = true;
|
||||
|
||||
const messageAndSuggestions = { messageId: "", suggest: [] };
|
||||
|
||||
if (funcInfo.arrayMethodName === "forEach") {
|
||||
|
||||
// if checkForEach: true, returning a value at any path inside a forEach is not allowed
|
||||
if (options.checkForEach && node.argument) {
|
||||
|
||||
if (options.allowVoid) {
|
||||
if (isExpressionVoid(node.argument)) {
|
||||
return;
|
||||
}
|
||||
|
||||
messageAndSuggestions.messageId = "expectedNoReturnValue";
|
||||
messageAndSuggestions.suggest = [{
|
||||
messageId: "prependVoid",
|
||||
fix(fixer) {
|
||||
return voidPrependFixer(sourceCode, node.argument, fixer);
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
messageAndSuggestions.messageId = "expectedNoReturnValue";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// if allowImplicit: false, should also check node.argument
|
||||
if (!options.allowImplicit && !node.argument) {
|
||||
messageAndSuggestions.messageId = "expectedReturnValue";
|
||||
}
|
||||
}
|
||||
|
||||
if (messageAndSuggestions.messageId) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: messageAndSuggestions.messageId,
|
||||
data: {
|
||||
name: astUtils.getFunctionNameWithKind(funcInfo.node),
|
||||
arrayMethodName: fullMethodName(funcInfo.arrayMethodName)
|
||||
},
|
||||
suggest: messageAndSuggestions.suggest.length !== 0 ? messageAndSuggestions.suggest : null
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Reports a given function if the last path is reachable.
|
||||
"FunctionExpression:exit": checkLastSegment,
|
||||
"ArrowFunctionExpression:exit": checkLastSegment
|
||||
};
|
||||
}
|
||||
};
|
||||
311
qwen/nodejs/node_modules/eslint/lib/rules/array-element-newline.js
generated
vendored
Normal file
311
qwen/nodejs/node_modules/eslint/lib/rules/array-element-newline.js
generated
vendored
Normal file
@@ -0,0 +1,311 @@
|
||||
/**
|
||||
* @fileoverview Rule to enforce line breaks after each array element
|
||||
* @author Jan Peer Stöcklmair <https://github.com/JPeer264>
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
replacedBy: [],
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description: "Enforce line breaks after each array element",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/array-element-newline"
|
||||
},
|
||||
|
||||
fixable: "whitespace",
|
||||
|
||||
schema: {
|
||||
definitions: {
|
||||
basicConfig: {
|
||||
oneOf: [
|
||||
{
|
||||
enum: ["always", "never", "consistent"]
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
multiline: {
|
||||
type: "boolean"
|
||||
},
|
||||
minItems: {
|
||||
type: ["integer", "null"],
|
||||
minimum: 0
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
type: "array",
|
||||
items: [
|
||||
{
|
||||
oneOf: [
|
||||
{
|
||||
$ref: "#/definitions/basicConfig"
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
ArrayExpression: {
|
||||
$ref: "#/definitions/basicConfig"
|
||||
},
|
||||
ArrayPattern: {
|
||||
$ref: "#/definitions/basicConfig"
|
||||
}
|
||||
},
|
||||
additionalProperties: false,
|
||||
minProperties: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
messages: {
|
||||
unexpectedLineBreak: "There should be no linebreak here.",
|
||||
missingLineBreak: "There should be a linebreak after this element."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Helpers
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Normalizes a given option value.
|
||||
* @param {string|Object|undefined} providedOption An option value to parse.
|
||||
* @returns {{multiline: boolean, minItems: number}} Normalized option object.
|
||||
*/
|
||||
function normalizeOptionValue(providedOption) {
|
||||
let consistent = false;
|
||||
let multiline = false;
|
||||
let minItems;
|
||||
|
||||
const option = providedOption || "always";
|
||||
|
||||
if (!option || option === "always" || option.minItems === 0) {
|
||||
minItems = 0;
|
||||
} else if (option === "never") {
|
||||
minItems = Number.POSITIVE_INFINITY;
|
||||
} else if (option === "consistent") {
|
||||
consistent = true;
|
||||
minItems = Number.POSITIVE_INFINITY;
|
||||
} else {
|
||||
multiline = Boolean(option.multiline);
|
||||
minItems = option.minItems || Number.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
return { consistent, multiline, minItems };
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a given option value.
|
||||
* @param {string|Object|undefined} options An option value to parse.
|
||||
* @returns {{ArrayExpression: {multiline: boolean, minItems: number}, ArrayPattern: {multiline: boolean, minItems: number}}} Normalized option object.
|
||||
*/
|
||||
function normalizeOptions(options) {
|
||||
if (options && (options.ArrayExpression || options.ArrayPattern)) {
|
||||
let expressionOptions, patternOptions;
|
||||
|
||||
if (options.ArrayExpression) {
|
||||
expressionOptions = normalizeOptionValue(options.ArrayExpression);
|
||||
}
|
||||
|
||||
if (options.ArrayPattern) {
|
||||
patternOptions = normalizeOptionValue(options.ArrayPattern);
|
||||
}
|
||||
|
||||
return { ArrayExpression: expressionOptions, ArrayPattern: patternOptions };
|
||||
}
|
||||
|
||||
const value = normalizeOptionValue(options);
|
||||
|
||||
return { ArrayExpression: value, ArrayPattern: value };
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that there shouldn't be a line break after the first token
|
||||
* @param {Token} token The token to use for the report.
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportNoLineBreak(token) {
|
||||
const tokenBefore = sourceCode.getTokenBefore(token, { includeComments: true });
|
||||
|
||||
context.report({
|
||||
loc: {
|
||||
start: tokenBefore.loc.end,
|
||||
end: token.loc.start
|
||||
},
|
||||
messageId: "unexpectedLineBreak",
|
||||
fix(fixer) {
|
||||
if (astUtils.isCommentToken(tokenBefore)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!astUtils.isTokenOnSameLine(tokenBefore, token)) {
|
||||
return fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], " ");
|
||||
}
|
||||
|
||||
/*
|
||||
* This will check if the comma is on the same line as the next element
|
||||
* Following array:
|
||||
* [
|
||||
* 1
|
||||
* , 2
|
||||
* , 3
|
||||
* ]
|
||||
*
|
||||
* will be fixed to:
|
||||
* [
|
||||
* 1, 2, 3
|
||||
* ]
|
||||
*/
|
||||
const twoTokensBefore = sourceCode.getTokenBefore(tokenBefore, { includeComments: true });
|
||||
|
||||
if (astUtils.isCommentToken(twoTokensBefore)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fixer.replaceTextRange([twoTokensBefore.range[1], tokenBefore.range[0]], "");
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that there should be a line break after the first token
|
||||
* @param {Token} token The token to use for the report.
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportRequiredLineBreak(token) {
|
||||
const tokenBefore = sourceCode.getTokenBefore(token, { includeComments: true });
|
||||
|
||||
context.report({
|
||||
loc: {
|
||||
start: tokenBefore.loc.end,
|
||||
end: token.loc.start
|
||||
},
|
||||
messageId: "missingLineBreak",
|
||||
fix(fixer) {
|
||||
return fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], "\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports a given node if it violated this rule.
|
||||
* @param {ASTNode} node A node to check. This is an ObjectExpression node or an ObjectPattern node.
|
||||
* @returns {void}
|
||||
*/
|
||||
function check(node) {
|
||||
const elements = node.elements;
|
||||
const normalizedOptions = normalizeOptions(context.options[0]);
|
||||
const options = normalizedOptions[node.type];
|
||||
|
||||
if (!options) {
|
||||
return;
|
||||
}
|
||||
|
||||
let elementBreak = false;
|
||||
|
||||
/*
|
||||
* MULTILINE: true
|
||||
* loop through every element and check
|
||||
* if at least one element has linebreaks inside
|
||||
* this ensures that following is not valid (due to elements are on the same line):
|
||||
*
|
||||
* [
|
||||
* 1,
|
||||
* 2,
|
||||
* 3
|
||||
* ]
|
||||
*/
|
||||
if (options.multiline) {
|
||||
elementBreak = elements
|
||||
.filter(element => element !== null)
|
||||
.some(element => element.loc.start.line !== element.loc.end.line);
|
||||
}
|
||||
|
||||
let linebreaksCount = 0;
|
||||
|
||||
for (let i = 0; i < node.elements.length; i++) {
|
||||
const element = node.elements[i];
|
||||
|
||||
const previousElement = elements[i - 1];
|
||||
|
||||
if (i === 0 || element === null || previousElement === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const commaToken = sourceCode.getFirstTokenBetween(previousElement, element, astUtils.isCommaToken);
|
||||
const lastTokenOfPreviousElement = sourceCode.getTokenBefore(commaToken);
|
||||
const firstTokenOfCurrentElement = sourceCode.getTokenAfter(commaToken);
|
||||
|
||||
if (!astUtils.isTokenOnSameLine(lastTokenOfPreviousElement, firstTokenOfCurrentElement)) {
|
||||
linebreaksCount++;
|
||||
}
|
||||
}
|
||||
|
||||
const needsLinebreaks = (
|
||||
elements.length >= options.minItems ||
|
||||
(
|
||||
options.multiline &&
|
||||
elementBreak
|
||||
) ||
|
||||
(
|
||||
options.consistent &&
|
||||
linebreaksCount > 0 &&
|
||||
linebreaksCount < node.elements.length
|
||||
)
|
||||
);
|
||||
|
||||
elements.forEach((element, i) => {
|
||||
const previousElement = elements[i - 1];
|
||||
|
||||
if (i === 0 || element === null || previousElement === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const commaToken = sourceCode.getFirstTokenBetween(previousElement, element, astUtils.isCommaToken);
|
||||
const lastTokenOfPreviousElement = sourceCode.getTokenBefore(commaToken);
|
||||
const firstTokenOfCurrentElement = sourceCode.getTokenAfter(commaToken);
|
||||
|
||||
if (needsLinebreaks) {
|
||||
if (astUtils.isTokenOnSameLine(lastTokenOfPreviousElement, firstTokenOfCurrentElement)) {
|
||||
reportRequiredLineBreak(firstTokenOfCurrentElement);
|
||||
}
|
||||
} else {
|
||||
if (!astUtils.isTokenOnSameLine(lastTokenOfPreviousElement, firstTokenOfCurrentElement)) {
|
||||
reportNoLineBreak(firstTokenOfCurrentElement);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Public
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
ArrayPattern: check,
|
||||
ArrayExpression: check
|
||||
};
|
||||
}
|
||||
};
|
||||
296
qwen/nodejs/node_modules/eslint/lib/rules/arrow-body-style.js
generated
vendored
Normal file
296
qwen/nodejs/node_modules/eslint/lib/rules/arrow-body-style.js
generated
vendored
Normal file
@@ -0,0 +1,296 @@
|
||||
/**
|
||||
* @fileoverview Rule to require braces in arrow function body.
|
||||
* @author Alberto Rodríguez
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Require braces around arrow function bodies",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/arrow-body-style"
|
||||
},
|
||||
|
||||
schema: {
|
||||
anyOf: [
|
||||
{
|
||||
type: "array",
|
||||
items: [
|
||||
{
|
||||
enum: ["always", "never"]
|
||||
}
|
||||
],
|
||||
minItems: 0,
|
||||
maxItems: 1
|
||||
},
|
||||
{
|
||||
type: "array",
|
||||
items: [
|
||||
{
|
||||
enum: ["as-needed"]
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
requireReturnForObjectLiteral: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
],
|
||||
minItems: 0,
|
||||
maxItems: 2
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
fixable: "code",
|
||||
|
||||
messages: {
|
||||
unexpectedOtherBlock: "Unexpected block statement surrounding arrow body.",
|
||||
unexpectedEmptyBlock: "Unexpected block statement surrounding arrow body; put a value of `undefined` immediately after the `=>`.",
|
||||
unexpectedObjectBlock: "Unexpected block statement surrounding arrow body; parenthesize the returned value and move it immediately after the `=>`.",
|
||||
unexpectedSingleBlock: "Unexpected block statement surrounding arrow body; move the returned value immediately after the `=>`.",
|
||||
expectedBlock: "Expected block statement surrounding arrow body."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const options = context.options;
|
||||
const always = options[0] === "always";
|
||||
const asNeeded = !options[0] || options[0] === "as-needed";
|
||||
const never = options[0] === "never";
|
||||
const requireReturnForObjectLiteral = options[1] && options[1].requireReturnForObjectLiteral;
|
||||
const sourceCode = context.sourceCode;
|
||||
let funcInfo = null;
|
||||
|
||||
/**
|
||||
* Checks whether the given node has ASI problem or not.
|
||||
* @param {Token} token The token to check.
|
||||
* @returns {boolean} `true` if it changes semantics if `;` or `}` followed by the token are removed.
|
||||
*/
|
||||
function hasASIProblem(token) {
|
||||
return token && token.type === "Punctuator" && /^[([/`+-]/u.test(token.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the closing parenthesis by the given node.
|
||||
* @param {ASTNode} node first node after an opening parenthesis.
|
||||
* @returns {Token} The found closing parenthesis token.
|
||||
*/
|
||||
function findClosingParen(node) {
|
||||
let nodeToCheck = node;
|
||||
|
||||
while (!astUtils.isParenthesised(sourceCode, nodeToCheck)) {
|
||||
nodeToCheck = nodeToCheck.parent;
|
||||
}
|
||||
return sourceCode.getTokenAfter(nodeToCheck);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the node is inside of a for loop's init
|
||||
* @param {ASTNode} node node is inside for loop
|
||||
* @returns {boolean} `true` if the node is inside of a for loop, else `false`
|
||||
*/
|
||||
function isInsideForLoopInitializer(node) {
|
||||
if (node && node.parent) {
|
||||
if (node.parent.type === "ForStatement" && node.parent.init === node) {
|
||||
return true;
|
||||
}
|
||||
return isInsideForLoopInitializer(node.parent);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a arrow function body needs braces
|
||||
* @param {ASTNode} node The arrow function node.
|
||||
* @returns {void}
|
||||
*/
|
||||
function validate(node) {
|
||||
const arrowBody = node.body;
|
||||
|
||||
if (arrowBody.type === "BlockStatement") {
|
||||
const blockBody = arrowBody.body;
|
||||
|
||||
if (blockBody.length !== 1 && !never) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (asNeeded && requireReturnForObjectLiteral && blockBody[0].type === "ReturnStatement" &&
|
||||
blockBody[0].argument && blockBody[0].argument.type === "ObjectExpression") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (never || asNeeded && blockBody[0].type === "ReturnStatement") {
|
||||
let messageId;
|
||||
|
||||
if (blockBody.length === 0) {
|
||||
messageId = "unexpectedEmptyBlock";
|
||||
} else if (blockBody.length > 1) {
|
||||
messageId = "unexpectedOtherBlock";
|
||||
} else if (blockBody[0].argument === null) {
|
||||
messageId = "unexpectedSingleBlock";
|
||||
} else if (astUtils.isOpeningBraceToken(sourceCode.getFirstToken(blockBody[0], { skip: 1 }))) {
|
||||
messageId = "unexpectedObjectBlock";
|
||||
} else {
|
||||
messageId = "unexpectedSingleBlock";
|
||||
}
|
||||
|
||||
context.report({
|
||||
node,
|
||||
loc: arrowBody.loc,
|
||||
messageId,
|
||||
fix(fixer) {
|
||||
const fixes = [];
|
||||
|
||||
if (blockBody.length !== 1 ||
|
||||
blockBody[0].type !== "ReturnStatement" ||
|
||||
!blockBody[0].argument ||
|
||||
hasASIProblem(sourceCode.getTokenAfter(arrowBody))
|
||||
) {
|
||||
return fixes;
|
||||
}
|
||||
|
||||
const openingBrace = sourceCode.getFirstToken(arrowBody);
|
||||
const closingBrace = sourceCode.getLastToken(arrowBody);
|
||||
const firstValueToken = sourceCode.getFirstToken(blockBody[0], 1);
|
||||
const lastValueToken = sourceCode.getLastToken(blockBody[0]);
|
||||
const commentsExist =
|
||||
sourceCode.commentsExistBetween(openingBrace, firstValueToken) ||
|
||||
sourceCode.commentsExistBetween(lastValueToken, closingBrace);
|
||||
|
||||
/*
|
||||
* Remove tokens around the return value.
|
||||
* If comments don't exist, remove extra spaces as well.
|
||||
*/
|
||||
if (commentsExist) {
|
||||
fixes.push(
|
||||
fixer.remove(openingBrace),
|
||||
fixer.remove(closingBrace),
|
||||
fixer.remove(sourceCode.getTokenAfter(openingBrace)) // return keyword
|
||||
);
|
||||
} else {
|
||||
fixes.push(
|
||||
fixer.removeRange([openingBrace.range[0], firstValueToken.range[0]]),
|
||||
fixer.removeRange([lastValueToken.range[1], closingBrace.range[1]])
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the first token of the return value is `{` or the return value is a sequence expression,
|
||||
* enclose the return value by parentheses to avoid syntax error.
|
||||
*/
|
||||
if (astUtils.isOpeningBraceToken(firstValueToken) || blockBody[0].argument.type === "SequenceExpression" || (funcInfo.hasInOperator && isInsideForLoopInitializer(node))) {
|
||||
if (!astUtils.isParenthesised(sourceCode, blockBody[0].argument)) {
|
||||
fixes.push(
|
||||
fixer.insertTextBefore(firstValueToken, "("),
|
||||
fixer.insertTextAfter(lastValueToken, ")")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the last token of the return statement is semicolon, remove it.
|
||||
* Non-block arrow body is an expression, not a statement.
|
||||
*/
|
||||
if (astUtils.isSemicolonToken(lastValueToken)) {
|
||||
fixes.push(fixer.remove(lastValueToken));
|
||||
}
|
||||
|
||||
return fixes;
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (always || (asNeeded && requireReturnForObjectLiteral && arrowBody.type === "ObjectExpression")) {
|
||||
context.report({
|
||||
node,
|
||||
loc: arrowBody.loc,
|
||||
messageId: "expectedBlock",
|
||||
fix(fixer) {
|
||||
const fixes = [];
|
||||
const arrowToken = sourceCode.getTokenBefore(arrowBody, astUtils.isArrowToken);
|
||||
const [firstTokenAfterArrow, secondTokenAfterArrow] = sourceCode.getTokensAfter(arrowToken, { count: 2 });
|
||||
const lastToken = sourceCode.getLastToken(node);
|
||||
|
||||
let parenthesisedObjectLiteral = null;
|
||||
|
||||
if (
|
||||
astUtils.isOpeningParenToken(firstTokenAfterArrow) &&
|
||||
astUtils.isOpeningBraceToken(secondTokenAfterArrow)
|
||||
) {
|
||||
const braceNode = sourceCode.getNodeByRangeIndex(secondTokenAfterArrow.range[0]);
|
||||
|
||||
if (braceNode.type === "ObjectExpression") {
|
||||
parenthesisedObjectLiteral = braceNode;
|
||||
}
|
||||
}
|
||||
|
||||
// If the value is object literal, remove parentheses which were forced by syntax.
|
||||
if (parenthesisedObjectLiteral) {
|
||||
const openingParenToken = firstTokenAfterArrow;
|
||||
const openingBraceToken = secondTokenAfterArrow;
|
||||
|
||||
if (astUtils.isTokenOnSameLine(openingParenToken, openingBraceToken)) {
|
||||
fixes.push(fixer.replaceText(openingParenToken, "{return "));
|
||||
} else {
|
||||
|
||||
// Avoid ASI
|
||||
fixes.push(
|
||||
fixer.replaceText(openingParenToken, "{"),
|
||||
fixer.insertTextBefore(openingBraceToken, "return ")
|
||||
);
|
||||
}
|
||||
|
||||
// Closing paren for the object doesn't have to be lastToken, e.g.: () => ({}).foo()
|
||||
fixes.push(fixer.remove(findClosingParen(parenthesisedObjectLiteral)));
|
||||
fixes.push(fixer.insertTextAfter(lastToken, "}"));
|
||||
|
||||
} else {
|
||||
fixes.push(fixer.insertTextBefore(firstTokenAfterArrow, "{return "));
|
||||
fixes.push(fixer.insertTextAfter(lastToken, "}"));
|
||||
}
|
||||
|
||||
return fixes;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
"BinaryExpression[operator='in']"() {
|
||||
let info = funcInfo;
|
||||
|
||||
while (info) {
|
||||
info.hasInOperator = true;
|
||||
info = info.upper;
|
||||
}
|
||||
},
|
||||
ArrowFunctionExpression() {
|
||||
funcInfo = {
|
||||
upper: funcInfo,
|
||||
hasInOperator: false
|
||||
};
|
||||
},
|
||||
"ArrowFunctionExpression:exit"(node) {
|
||||
validate(node);
|
||||
funcInfo = funcInfo.upper;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
186
qwen/nodejs/node_modules/eslint/lib/rules/arrow-parens.js
generated
vendored
Normal file
186
qwen/nodejs/node_modules/eslint/lib/rules/arrow-parens.js
generated
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* @fileoverview Rule to require parens in arrow function arguments.
|
||||
* @author Jxck
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines if the given arrow function has block body.
|
||||
* @param {ASTNode} node `ArrowFunctionExpression` node.
|
||||
* @returns {boolean} `true` if the function has block body.
|
||||
*/
|
||||
function hasBlockBody(node) {
|
||||
return node.body.type === "BlockStatement";
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
replacedBy: [],
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description: "Require parentheses around arrow function arguments",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/arrow-parens"
|
||||
},
|
||||
|
||||
fixable: "code",
|
||||
|
||||
schema: [
|
||||
{
|
||||
enum: ["always", "as-needed"]
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
requireForBlockBody: {
|
||||
type: "boolean",
|
||||
default: false
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
],
|
||||
|
||||
messages: {
|
||||
unexpectedParens: "Unexpected parentheses around single function argument.",
|
||||
expectedParens: "Expected parentheses around arrow function argument.",
|
||||
|
||||
unexpectedParensInline: "Unexpected parentheses around single function argument having a body with no curly braces.",
|
||||
expectedParensBlock: "Expected parentheses around arrow function argument having a body with curly braces."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const asNeeded = context.options[0] === "as-needed";
|
||||
const requireForBlockBody = asNeeded && context.options[1] && context.options[1].requireForBlockBody === true;
|
||||
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Finds opening paren of parameters for the given arrow function, if it exists.
|
||||
* It is assumed that the given arrow function has exactly one parameter.
|
||||
* @param {ASTNode} node `ArrowFunctionExpression` node.
|
||||
* @returns {Token|null} the opening paren, or `null` if the given arrow function doesn't have parens of parameters.
|
||||
*/
|
||||
function findOpeningParenOfParams(node) {
|
||||
const tokenBeforeParams = sourceCode.getTokenBefore(node.params[0]);
|
||||
|
||||
if (
|
||||
tokenBeforeParams &&
|
||||
astUtils.isOpeningParenToken(tokenBeforeParams) &&
|
||||
node.range[0] <= tokenBeforeParams.range[0]
|
||||
) {
|
||||
return tokenBeforeParams;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds closing paren of parameters for the given arrow function.
|
||||
* It is assumed that the given arrow function has parens of parameters and that it has exactly one parameter.
|
||||
* @param {ASTNode} node `ArrowFunctionExpression` node.
|
||||
* @returns {Token} the closing paren of parameters.
|
||||
*/
|
||||
function getClosingParenOfParams(node) {
|
||||
return sourceCode.getTokenAfter(node.params[0], astUtils.isClosingParenToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given arrow function has comments inside parens of parameters.
|
||||
* It is assumed that the given arrow function has parens of parameters.
|
||||
* @param {ASTNode} node `ArrowFunctionExpression` node.
|
||||
* @param {Token} openingParen Opening paren of parameters.
|
||||
* @returns {boolean} `true` if the function has at least one comment inside of parens of parameters.
|
||||
*/
|
||||
function hasCommentsInParensOfParams(node, openingParen) {
|
||||
return sourceCode.commentsExistBetween(openingParen, getClosingParenOfParams(node));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given arrow function has unexpected tokens before opening paren of parameters,
|
||||
* in which case it will be assumed that the existing parens of parameters are necessary.
|
||||
* Only tokens within the range of the arrow function (tokens that are part of the arrow function) are taken into account.
|
||||
* Example: <T>(a) => b
|
||||
* @param {ASTNode} node `ArrowFunctionExpression` node.
|
||||
* @param {Token} openingParen Opening paren of parameters.
|
||||
* @returns {boolean} `true` if the function has at least one unexpected token.
|
||||
*/
|
||||
function hasUnexpectedTokensBeforeOpeningParen(node, openingParen) {
|
||||
const expectedCount = node.async ? 1 : 0;
|
||||
|
||||
return sourceCode.getFirstToken(node, { skip: expectedCount }) !== openingParen;
|
||||
}
|
||||
|
||||
return {
|
||||
"ArrowFunctionExpression[params.length=1]"(node) {
|
||||
const shouldHaveParens = !asNeeded || requireForBlockBody && hasBlockBody(node);
|
||||
const openingParen = findOpeningParenOfParams(node);
|
||||
const hasParens = openingParen !== null;
|
||||
const [param] = node.params;
|
||||
|
||||
if (shouldHaveParens && !hasParens) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: requireForBlockBody ? "expectedParensBlock" : "expectedParens",
|
||||
loc: param.loc,
|
||||
*fix(fixer) {
|
||||
yield fixer.insertTextBefore(param, "(");
|
||||
yield fixer.insertTextAfter(param, ")");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
!shouldHaveParens &&
|
||||
hasParens &&
|
||||
param.type === "Identifier" &&
|
||||
!param.typeAnnotation &&
|
||||
!node.returnType &&
|
||||
!hasCommentsInParensOfParams(node, openingParen) &&
|
||||
!hasUnexpectedTokensBeforeOpeningParen(node, openingParen)
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: requireForBlockBody ? "unexpectedParensInline" : "unexpectedParens",
|
||||
loc: param.loc,
|
||||
*fix(fixer) {
|
||||
const tokenBeforeOpeningParen = sourceCode.getTokenBefore(openingParen);
|
||||
const closingParen = getClosingParenOfParams(node);
|
||||
|
||||
if (
|
||||
tokenBeforeOpeningParen &&
|
||||
tokenBeforeOpeningParen.range[1] === openingParen.range[0] &&
|
||||
!astUtils.canTokensBeAdjacent(tokenBeforeOpeningParen, sourceCode.getFirstToken(param))
|
||||
) {
|
||||
yield fixer.insertTextBefore(openingParen, " ");
|
||||
}
|
||||
|
||||
// remove parens, whitespace inside parens, and possible trailing comma
|
||||
yield fixer.removeRange([openingParen.range[0], param.range[0]]);
|
||||
yield fixer.removeRange([param.range[1], closingParen.range[1]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
164
qwen/nodejs/node_modules/eslint/lib/rules/arrow-spacing.js
generated
vendored
Normal file
164
qwen/nodejs/node_modules/eslint/lib/rules/arrow-spacing.js
generated
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* @fileoverview Rule to define spacing before/after arrow function's arrow.
|
||||
* @author Jxck
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
replacedBy: [],
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description: "Enforce consistent spacing before and after the arrow in arrow functions",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/arrow-spacing"
|
||||
},
|
||||
|
||||
fixable: "whitespace",
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
before: {
|
||||
type: "boolean",
|
||||
default: true
|
||||
},
|
||||
after: {
|
||||
type: "boolean",
|
||||
default: true
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
],
|
||||
|
||||
messages: {
|
||||
expectedBefore: "Missing space before =>.",
|
||||
unexpectedBefore: "Unexpected space before =>.",
|
||||
|
||||
expectedAfter: "Missing space after =>.",
|
||||
unexpectedAfter: "Unexpected space after =>."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
|
||||
// merge rules with default
|
||||
const rule = Object.assign({}, context.options[0]);
|
||||
|
||||
rule.before = rule.before !== false;
|
||||
rule.after = rule.after !== false;
|
||||
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Get tokens of arrow(`=>`) and before/after arrow.
|
||||
* @param {ASTNode} node The arrow function node.
|
||||
* @returns {Object} Tokens of arrow and before/after arrow.
|
||||
*/
|
||||
function getTokens(node) {
|
||||
const arrow = sourceCode.getTokenBefore(node.body, astUtils.isArrowToken);
|
||||
|
||||
return {
|
||||
before: sourceCode.getTokenBefore(arrow),
|
||||
arrow,
|
||||
after: sourceCode.getTokenAfter(arrow)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Count spaces before/after arrow(`=>`) token.
|
||||
* @param {Object} tokens Tokens before/after arrow.
|
||||
* @returns {Object} count of space before/after arrow.
|
||||
*/
|
||||
function countSpaces(tokens) {
|
||||
const before = tokens.arrow.range[0] - tokens.before.range[1];
|
||||
const after = tokens.after.range[0] - tokens.arrow.range[1];
|
||||
|
||||
return { before, after };
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether space(s) before after arrow(`=>`) is satisfy rule.
|
||||
* if before/after value is `true`, there should be space(s).
|
||||
* if before/after value is `false`, there should be no space.
|
||||
* @param {ASTNode} node The arrow function node.
|
||||
* @returns {void}
|
||||
*/
|
||||
function spaces(node) {
|
||||
const tokens = getTokens(node);
|
||||
const countSpace = countSpaces(tokens);
|
||||
|
||||
if (rule.before) {
|
||||
|
||||
// should be space(s) before arrow
|
||||
if (countSpace.before === 0) {
|
||||
context.report({
|
||||
node: tokens.before,
|
||||
messageId: "expectedBefore",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(tokens.arrow, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
||||
// should be no space before arrow
|
||||
if (countSpace.before > 0) {
|
||||
context.report({
|
||||
node: tokens.before,
|
||||
messageId: "unexpectedBefore",
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([tokens.before.range[1], tokens.arrow.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (rule.after) {
|
||||
|
||||
// should be space(s) after arrow
|
||||
if (countSpace.after === 0) {
|
||||
context.report({
|
||||
node: tokens.after,
|
||||
messageId: "expectedAfter",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(tokens.arrow, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
||||
// should be no space after arrow
|
||||
if (countSpace.after > 0) {
|
||||
context.report({
|
||||
node: tokens.after,
|
||||
messageId: "unexpectedAfter",
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([tokens.arrow.range[1], tokens.after.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
ArrowFunctionExpression: spaces
|
||||
};
|
||||
}
|
||||
};
|
||||
135
qwen/nodejs/node_modules/eslint/lib/rules/block-scoped-var.js
generated
vendored
Normal file
135
qwen/nodejs/node_modules/eslint/lib/rules/block-scoped-var.js
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* @fileoverview Rule to check for "block scoped" variables by binding context
|
||||
* @author Matt DuVall <http://www.mattduvall.com>
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Enforce the use of variables within the scope they are defined",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/block-scoped-var"
|
||||
},
|
||||
|
||||
schema: [],
|
||||
|
||||
messages: {
|
||||
outOfScope: "'{{name}}' declared on line {{definitionLine}} column {{definitionColumn}} is used outside of binding context."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
let stack = [];
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Makes a block scope.
|
||||
* @param {ASTNode} node A node of a scope.
|
||||
* @returns {void}
|
||||
*/
|
||||
function enterScope(node) {
|
||||
stack.push(node.range);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pops the last block scope.
|
||||
* @returns {void}
|
||||
*/
|
||||
function exitScope() {
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports a given reference.
|
||||
* @param {eslint-scope.Reference} reference A reference to report.
|
||||
* @param {eslint-scope.Definition} definition A definition for which to report reference.
|
||||
* @returns {void}
|
||||
*/
|
||||
function report(reference, definition) {
|
||||
const identifier = reference.identifier;
|
||||
const definitionPosition = definition.name.loc.start;
|
||||
|
||||
context.report({
|
||||
node: identifier,
|
||||
messageId: "outOfScope",
|
||||
data: {
|
||||
name: identifier.name,
|
||||
definitionLine: definitionPosition.line,
|
||||
definitionColumn: definitionPosition.column + 1
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and reports references which are outside of valid scopes.
|
||||
* @param {ASTNode} node A node to get variables.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkForVariables(node) {
|
||||
if (node.kind !== "var") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Defines a predicate to check whether or not a given reference is outside of valid scope.
|
||||
const scopeRange = stack[stack.length - 1];
|
||||
|
||||
/**
|
||||
* Check if a reference is out of scope
|
||||
* @param {ASTNode} reference node to examine
|
||||
* @returns {boolean} True is its outside the scope
|
||||
* @private
|
||||
*/
|
||||
function isOutsideOfScope(reference) {
|
||||
const idRange = reference.identifier.range;
|
||||
|
||||
return idRange[0] < scopeRange[0] || idRange[1] > scopeRange[1];
|
||||
}
|
||||
|
||||
// Gets declared variables, and checks its references.
|
||||
const variables = sourceCode.getDeclaredVariables(node);
|
||||
|
||||
for (let i = 0; i < variables.length; ++i) {
|
||||
|
||||
// Reports.
|
||||
variables[i]
|
||||
.references
|
||||
.filter(isOutsideOfScope)
|
||||
.forEach(ref => report(ref, variables[i].defs.find(def => def.parent === node)));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
Program(node) {
|
||||
stack = [node.range];
|
||||
},
|
||||
|
||||
// Manages scopes.
|
||||
BlockStatement: enterScope,
|
||||
"BlockStatement:exit": exitScope,
|
||||
ForStatement: enterScope,
|
||||
"ForStatement:exit": exitScope,
|
||||
ForInStatement: enterScope,
|
||||
"ForInStatement:exit": exitScope,
|
||||
ForOfStatement: enterScope,
|
||||
"ForOfStatement:exit": exitScope,
|
||||
SwitchStatement: enterScope,
|
||||
"SwitchStatement:exit": exitScope,
|
||||
CatchClause: enterScope,
|
||||
"CatchClause:exit": exitScope,
|
||||
StaticBlock: enterScope,
|
||||
"StaticBlock:exit": exitScope,
|
||||
|
||||
// Finds and reports references which are outside of valid scope.
|
||||
VariableDeclaration: checkForVariables
|
||||
};
|
||||
|
||||
}
|
||||
};
|
||||
174
qwen/nodejs/node_modules/eslint/lib/rules/block-spacing.js
generated
vendored
Normal file
174
qwen/nodejs/node_modules/eslint/lib/rules/block-spacing.js
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* @fileoverview A rule to disallow or enforce spaces inside of single line blocks.
|
||||
* @author Toru Nagashima
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const util = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
replacedBy: [],
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description: "Disallow or enforce spaces inside of blocks after opening block and before closing block",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/block-spacing"
|
||||
},
|
||||
|
||||
fixable: "whitespace",
|
||||
|
||||
schema: [
|
||||
{ enum: ["always", "never"] }
|
||||
],
|
||||
|
||||
messages: {
|
||||
missing: "Requires a space {{location}} '{{token}}'.",
|
||||
extra: "Unexpected space(s) {{location}} '{{token}}'."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const always = (context.options[0] !== "never"),
|
||||
messageId = always ? "missing" : "extra",
|
||||
sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Gets the open brace token from a given node.
|
||||
* @param {ASTNode} node A BlockStatement/StaticBlock/SwitchStatement node to get.
|
||||
* @returns {Token} The token of the open brace.
|
||||
*/
|
||||
function getOpenBrace(node) {
|
||||
if (node.type === "SwitchStatement") {
|
||||
if (node.cases.length > 0) {
|
||||
return sourceCode.getTokenBefore(node.cases[0]);
|
||||
}
|
||||
return sourceCode.getLastToken(node, 1);
|
||||
}
|
||||
|
||||
if (node.type === "StaticBlock") {
|
||||
return sourceCode.getFirstToken(node, { skip: 1 }); // skip the `static` token
|
||||
}
|
||||
|
||||
// "BlockStatement"
|
||||
return sourceCode.getFirstToken(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not:
|
||||
* - given tokens are on same line.
|
||||
* - there is/isn't a space between given tokens.
|
||||
* @param {Token} left A token to check.
|
||||
* @param {Token} right The token which is next to `left`.
|
||||
* @returns {boolean}
|
||||
* When the option is `"always"`, `true` if there are one or more spaces between given tokens.
|
||||
* When the option is `"never"`, `true` if there are not any spaces between given tokens.
|
||||
* If given tokens are not on same line, it's always `true`.
|
||||
*/
|
||||
function isValid(left, right) {
|
||||
return (
|
||||
!util.isTokenOnSameLine(left, right) ||
|
||||
sourceCode.isSpaceBetweenTokens(left, right) === always
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks and reports invalid spacing style inside braces.
|
||||
* @param {ASTNode} node A BlockStatement/StaticBlock/SwitchStatement node to check.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkSpacingInsideBraces(node) {
|
||||
|
||||
// Gets braces and the first/last token of content.
|
||||
const openBrace = getOpenBrace(node);
|
||||
const closeBrace = sourceCode.getLastToken(node);
|
||||
const firstToken = sourceCode.getTokenAfter(openBrace, { includeComments: true });
|
||||
const lastToken = sourceCode.getTokenBefore(closeBrace, { includeComments: true });
|
||||
|
||||
// Skip if the node is invalid or empty.
|
||||
if (openBrace.type !== "Punctuator" ||
|
||||
openBrace.value !== "{" ||
|
||||
closeBrace.type !== "Punctuator" ||
|
||||
closeBrace.value !== "}" ||
|
||||
firstToken === closeBrace
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip line comments for option never
|
||||
if (!always && firstToken.type === "Line") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check.
|
||||
if (!isValid(openBrace, firstToken)) {
|
||||
let loc = openBrace.loc;
|
||||
|
||||
if (messageId === "extra") {
|
||||
loc = {
|
||||
start: openBrace.loc.end,
|
||||
end: firstToken.loc.start
|
||||
};
|
||||
}
|
||||
|
||||
context.report({
|
||||
node,
|
||||
loc,
|
||||
messageId,
|
||||
data: {
|
||||
location: "after",
|
||||
token: openBrace.value
|
||||
},
|
||||
fix(fixer) {
|
||||
if (always) {
|
||||
return fixer.insertTextBefore(firstToken, " ");
|
||||
}
|
||||
|
||||
return fixer.removeRange([openBrace.range[1], firstToken.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!isValid(lastToken, closeBrace)) {
|
||||
let loc = closeBrace.loc;
|
||||
|
||||
if (messageId === "extra") {
|
||||
loc = {
|
||||
start: lastToken.loc.end,
|
||||
end: closeBrace.loc.start
|
||||
};
|
||||
}
|
||||
context.report({
|
||||
node,
|
||||
loc,
|
||||
messageId,
|
||||
data: {
|
||||
location: "before",
|
||||
token: closeBrace.value
|
||||
},
|
||||
fix(fixer) {
|
||||
if (always) {
|
||||
return fixer.insertTextAfter(lastToken, " ");
|
||||
}
|
||||
|
||||
return fixer.removeRange([lastToken.range[1], closeBrace.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
BlockStatement: checkSpacingInsideBraces,
|
||||
StaticBlock: checkSpacingInsideBraces,
|
||||
SwitchStatement: checkSpacingInsideBraces
|
||||
};
|
||||
}
|
||||
};
|
||||
197
qwen/nodejs/node_modules/eslint/lib/rules/brace-style.js
generated
vendored
Normal file
197
qwen/nodejs/node_modules/eslint/lib/rules/brace-style.js
generated
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
/**
|
||||
* @fileoverview Rule to flag block statements that do not use the one true brace style
|
||||
* @author Ian Christian Myers
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
replacedBy: [],
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description: "Enforce consistent brace style for blocks",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/brace-style"
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
enum: ["1tbs", "stroustrup", "allman"]
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
allowSingleLine: {
|
||||
type: "boolean",
|
||||
default: false
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
],
|
||||
|
||||
fixable: "whitespace",
|
||||
|
||||
messages: {
|
||||
nextLineOpen: "Opening curly brace does not appear on the same line as controlling statement.",
|
||||
sameLineOpen: "Opening curly brace appears on the same line as controlling statement.",
|
||||
blockSameLine: "Statement inside of curly braces should be on next line.",
|
||||
nextLineClose: "Closing curly brace does not appear on the same line as the subsequent block.",
|
||||
singleLineClose: "Closing curly brace should be on the same line as opening curly brace or on the line after the previous block.",
|
||||
sameLineClose: "Closing curly brace appears on the same line as the subsequent block."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const style = context.options[0] || "1tbs",
|
||||
params = context.options[1] || {},
|
||||
sourceCode = context.sourceCode;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Fixes a place where a newline unexpectedly appears
|
||||
* @param {Token} firstToken The token before the unexpected newline
|
||||
* @param {Token} secondToken The token after the unexpected newline
|
||||
* @returns {Function} A fixer function to remove the newlines between the tokens
|
||||
*/
|
||||
function removeNewlineBetween(firstToken, secondToken) {
|
||||
const textRange = [firstToken.range[1], secondToken.range[0]];
|
||||
const textBetween = sourceCode.text.slice(textRange[0], textRange[1]);
|
||||
|
||||
// Don't do a fix if there is a comment between the tokens
|
||||
if (textBetween.trim()) {
|
||||
return null;
|
||||
}
|
||||
return fixer => fixer.replaceTextRange(textRange, " ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a pair of curly brackets based on the user's config
|
||||
* @param {Token} openingCurly The opening curly bracket
|
||||
* @param {Token} closingCurly The closing curly bracket
|
||||
* @returns {void}
|
||||
*/
|
||||
function validateCurlyPair(openingCurly, closingCurly) {
|
||||
const tokenBeforeOpeningCurly = sourceCode.getTokenBefore(openingCurly);
|
||||
const tokenAfterOpeningCurly = sourceCode.getTokenAfter(openingCurly);
|
||||
const tokenBeforeClosingCurly = sourceCode.getTokenBefore(closingCurly);
|
||||
const singleLineException = params.allowSingleLine && astUtils.isTokenOnSameLine(openingCurly, closingCurly);
|
||||
|
||||
if (style !== "allman" && !astUtils.isTokenOnSameLine(tokenBeforeOpeningCurly, openingCurly)) {
|
||||
context.report({
|
||||
node: openingCurly,
|
||||
messageId: "nextLineOpen",
|
||||
fix: removeNewlineBetween(tokenBeforeOpeningCurly, openingCurly)
|
||||
});
|
||||
}
|
||||
|
||||
if (style === "allman" && astUtils.isTokenOnSameLine(tokenBeforeOpeningCurly, openingCurly) && !singleLineException) {
|
||||
context.report({
|
||||
node: openingCurly,
|
||||
messageId: "sameLineOpen",
|
||||
fix: fixer => fixer.insertTextBefore(openingCurly, "\n")
|
||||
});
|
||||
}
|
||||
|
||||
if (astUtils.isTokenOnSameLine(openingCurly, tokenAfterOpeningCurly) && tokenAfterOpeningCurly !== closingCurly && !singleLineException) {
|
||||
context.report({
|
||||
node: openingCurly,
|
||||
messageId: "blockSameLine",
|
||||
fix: fixer => fixer.insertTextAfter(openingCurly, "\n")
|
||||
});
|
||||
}
|
||||
|
||||
if (tokenBeforeClosingCurly !== openingCurly && !singleLineException && astUtils.isTokenOnSameLine(tokenBeforeClosingCurly, closingCurly)) {
|
||||
context.report({
|
||||
node: closingCurly,
|
||||
messageId: "singleLineClose",
|
||||
fix: fixer => fixer.insertTextBefore(closingCurly, "\n")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the location of a token that appears before a keyword (e.g. a newline before `else`)
|
||||
* @param {Token} curlyToken The closing curly token. This is assumed to precede a keyword token (such as `else` or `finally`).
|
||||
* @returns {void}
|
||||
*/
|
||||
function validateCurlyBeforeKeyword(curlyToken) {
|
||||
const keywordToken = sourceCode.getTokenAfter(curlyToken);
|
||||
|
||||
if (style === "1tbs" && !astUtils.isTokenOnSameLine(curlyToken, keywordToken)) {
|
||||
context.report({
|
||||
node: curlyToken,
|
||||
messageId: "nextLineClose",
|
||||
fix: removeNewlineBetween(curlyToken, keywordToken)
|
||||
});
|
||||
}
|
||||
|
||||
if (style !== "1tbs" && astUtils.isTokenOnSameLine(curlyToken, keywordToken)) {
|
||||
context.report({
|
||||
node: curlyToken,
|
||||
messageId: "sameLineClose",
|
||||
fix: fixer => fixer.insertTextAfter(curlyToken, "\n")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public API
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
BlockStatement(node) {
|
||||
if (!astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type)) {
|
||||
validateCurlyPair(sourceCode.getFirstToken(node), sourceCode.getLastToken(node));
|
||||
}
|
||||
},
|
||||
StaticBlock(node) {
|
||||
validateCurlyPair(
|
||||
sourceCode.getFirstToken(node, { skip: 1 }), // skip the `static` token
|
||||
sourceCode.getLastToken(node)
|
||||
);
|
||||
},
|
||||
ClassBody(node) {
|
||||
validateCurlyPair(sourceCode.getFirstToken(node), sourceCode.getLastToken(node));
|
||||
},
|
||||
SwitchStatement(node) {
|
||||
const closingCurly = sourceCode.getLastToken(node);
|
||||
const openingCurly = sourceCode.getTokenBefore(node.cases.length ? node.cases[0] : closingCurly);
|
||||
|
||||
validateCurlyPair(openingCurly, closingCurly);
|
||||
},
|
||||
IfStatement(node) {
|
||||
if (node.consequent.type === "BlockStatement" && node.alternate) {
|
||||
|
||||
// Handle the keyword after the `if` block (before `else`)
|
||||
validateCurlyBeforeKeyword(sourceCode.getLastToken(node.consequent));
|
||||
}
|
||||
},
|
||||
TryStatement(node) {
|
||||
|
||||
// Handle the keyword after the `try` block (before `catch` or `finally`)
|
||||
validateCurlyBeforeKeyword(sourceCode.getLastToken(node.block));
|
||||
|
||||
if (node.handler && node.finalizer) {
|
||||
|
||||
// Handle the keyword after the `catch` block (before `finally`)
|
||||
validateCurlyBeforeKeyword(sourceCode.getLastToken(node.handler.body));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
187
qwen/nodejs/node_modules/eslint/lib/rules/callback-return.js
generated
vendored
Normal file
187
qwen/nodejs/node_modules/eslint/lib/rules/callback-return.js
generated
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* @fileoverview Enforce return after a callback.
|
||||
* @author Jamund Ferguson
|
||||
* @deprecated in ESLint v7.0.0
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
|
||||
replacedBy: [],
|
||||
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Require `return` statements after callbacks",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/callback-return"
|
||||
},
|
||||
|
||||
schema: [{
|
||||
type: "array",
|
||||
items: { type: "string" }
|
||||
}],
|
||||
|
||||
messages: {
|
||||
missingReturn: "Expected return with your callback function."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
|
||||
const callbacks = context.options[0] || ["callback", "cb", "next"],
|
||||
sourceCode = context.sourceCode;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Find the closest parent matching a list of types.
|
||||
* @param {ASTNode} node The node whose parents we are searching
|
||||
* @param {Array} types The node types to match
|
||||
* @returns {ASTNode} The matched node or undefined.
|
||||
*/
|
||||
function findClosestParentOfType(node, types) {
|
||||
if (!node.parent) {
|
||||
return null;
|
||||
}
|
||||
if (!types.includes(node.parent.type)) {
|
||||
return findClosestParentOfType(node.parent, types);
|
||||
}
|
||||
return node.parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a node contains only identifiers
|
||||
* @param {ASTNode} node The node to check
|
||||
* @returns {boolean} Whether or not the node contains only identifiers
|
||||
*/
|
||||
function containsOnlyIdentifiers(node) {
|
||||
if (node.type === "Identifier") {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (node.type === "MemberExpression") {
|
||||
if (node.object.type === "Identifier") {
|
||||
return true;
|
||||
}
|
||||
if (node.object.type === "MemberExpression") {
|
||||
return containsOnlyIdentifiers(node.object);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a CallExpression is in our callback list.
|
||||
* @param {ASTNode} node The node to check against our callback names list.
|
||||
* @returns {boolean} Whether or not this function matches our callback name.
|
||||
*/
|
||||
function isCallback(node) {
|
||||
return containsOnlyIdentifiers(node.callee) && callbacks.includes(sourceCode.getText(node.callee));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not the callback is part of a callback expression.
|
||||
* @param {ASTNode} node The callback node
|
||||
* @param {ASTNode} parentNode The expression node
|
||||
* @returns {boolean} Whether or not this is part of a callback expression
|
||||
*/
|
||||
function isCallbackExpression(node, parentNode) {
|
||||
|
||||
// ensure the parent node exists and is an expression
|
||||
if (!parentNode || parentNode.type !== "ExpressionStatement") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// cb()
|
||||
if (parentNode.expression === node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// special case for cb && cb() and similar
|
||||
if (parentNode.expression.type === "BinaryExpression" || parentNode.expression.type === "LogicalExpression") {
|
||||
if (parentNode.expression.right === node) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
CallExpression(node) {
|
||||
|
||||
// if we're not a callback we can return
|
||||
if (!isCallback(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// find the closest block, return or loop
|
||||
const closestBlock = findClosestParentOfType(node, ["BlockStatement", "ReturnStatement", "ArrowFunctionExpression"]) || {};
|
||||
|
||||
// if our parent is a return we know we're ok
|
||||
if (closestBlock.type === "ReturnStatement") {
|
||||
return;
|
||||
}
|
||||
|
||||
// arrow functions don't always have blocks and implicitly return
|
||||
if (closestBlock.type === "ArrowFunctionExpression") {
|
||||
return;
|
||||
}
|
||||
|
||||
// block statements are part of functions and most if statements
|
||||
if (closestBlock.type === "BlockStatement") {
|
||||
|
||||
// find the last item in the block
|
||||
const lastItem = closestBlock.body[closestBlock.body.length - 1];
|
||||
|
||||
// if the callback is the last thing in a block that might be ok
|
||||
if (isCallbackExpression(node, lastItem)) {
|
||||
|
||||
const parentType = closestBlock.parent.type;
|
||||
|
||||
// but only if the block is part of a function
|
||||
if (parentType === "FunctionExpression" ||
|
||||
parentType === "FunctionDeclaration" ||
|
||||
parentType === "ArrowFunctionExpression"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ending a block with a return is also ok
|
||||
if (lastItem.type === "ReturnStatement") {
|
||||
|
||||
// but only if the callback is immediately before
|
||||
if (isCallbackExpression(node, closestBlock.body[closestBlock.body.length - 2])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// as long as you're the child of a function at this point you should be asked to return
|
||||
if (findClosestParentOfType(node, ["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"])) {
|
||||
context.report({ node, messageId: "missingReturn" });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
};
|
||||
399
qwen/nodejs/node_modules/eslint/lib/rules/camelcase.js
generated
vendored
Normal file
399
qwen/nodejs/node_modules/eslint/lib/rules/camelcase.js
generated
vendored
Normal file
@@ -0,0 +1,399 @@
|
||||
/**
|
||||
* @fileoverview Rule to flag non-camelcased identifiers
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Enforce camelcase naming convention",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/camelcase"
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
ignoreDestructuring: {
|
||||
type: "boolean",
|
||||
default: false
|
||||
},
|
||||
ignoreImports: {
|
||||
type: "boolean",
|
||||
default: false
|
||||
},
|
||||
ignoreGlobals: {
|
||||
type: "boolean",
|
||||
default: false
|
||||
},
|
||||
properties: {
|
||||
enum: ["always", "never"]
|
||||
},
|
||||
allow: {
|
||||
type: "array",
|
||||
items: [
|
||||
{
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
minItems: 0,
|
||||
uniqueItems: true
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
],
|
||||
|
||||
messages: {
|
||||
notCamelCase: "Identifier '{{name}}' is not in camel case.",
|
||||
notCamelCasePrivate: "#{{name}} is not in camel case."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const options = context.options[0] || {};
|
||||
const properties = options.properties === "never" ? "never" : "always";
|
||||
const ignoreDestructuring = options.ignoreDestructuring;
|
||||
const ignoreImports = options.ignoreImports;
|
||||
const ignoreGlobals = options.ignoreGlobals;
|
||||
const allow = options.allow || [];
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// contains reported nodes to avoid reporting twice on destructuring with shorthand notation
|
||||
const reported = new Set();
|
||||
|
||||
/**
|
||||
* Checks if a string contains an underscore and isn't all upper-case
|
||||
* @param {string} name The string to check.
|
||||
* @returns {boolean} if the string is underscored
|
||||
* @private
|
||||
*/
|
||||
function isUnderscored(name) {
|
||||
const nameBody = name.replace(/^_+|_+$/gu, "");
|
||||
|
||||
// if there's an underscore, it might be A_CONSTANT, which is okay
|
||||
return nameBody.includes("_") && nameBody !== nameBody.toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string match the ignore list
|
||||
* @param {string} name The string to check.
|
||||
* @returns {boolean} if the string is ignored
|
||||
* @private
|
||||
*/
|
||||
function isAllowed(name) {
|
||||
return allow.some(
|
||||
entry => name === entry || name.match(new RegExp(entry, "u"))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given name is good or not.
|
||||
* @param {string} name The name to check.
|
||||
* @returns {boolean} `true` if the name is good.
|
||||
* @private
|
||||
*/
|
||||
function isGoodName(name) {
|
||||
return !isUnderscored(name) || isAllowed(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given identifier reference or member expression is an assignment
|
||||
* target.
|
||||
* @param {ASTNode} node The node to check.
|
||||
* @returns {boolean} `true` if the node is an assignment target.
|
||||
*/
|
||||
function isAssignmentTarget(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
switch (parent.type) {
|
||||
case "AssignmentExpression":
|
||||
case "AssignmentPattern":
|
||||
return parent.left === node;
|
||||
|
||||
case "Property":
|
||||
return (
|
||||
parent.parent.type === "ObjectPattern" &&
|
||||
parent.value === node
|
||||
);
|
||||
case "ArrayPattern":
|
||||
case "RestElement":
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given binding identifier uses the original name as-is.
|
||||
* - If it's in object destructuring or object expression, the original name is its property name.
|
||||
* - If it's in import declaration, the original name is its exported name.
|
||||
* @param {ASTNode} node The `Identifier` node to check.
|
||||
* @returns {boolean} `true` if the identifier uses the original name as-is.
|
||||
*/
|
||||
function equalsToOriginalName(node) {
|
||||
const localName = node.name;
|
||||
const valueNode = node.parent.type === "AssignmentPattern"
|
||||
? node.parent
|
||||
: node;
|
||||
const parent = valueNode.parent;
|
||||
|
||||
switch (parent.type) {
|
||||
case "Property":
|
||||
return (
|
||||
(parent.parent.type === "ObjectPattern" || parent.parent.type === "ObjectExpression") &&
|
||||
parent.value === valueNode &&
|
||||
!parent.computed &&
|
||||
parent.key.type === "Identifier" &&
|
||||
parent.key.name === localName
|
||||
);
|
||||
|
||||
case "ImportSpecifier":
|
||||
return (
|
||||
parent.local === node &&
|
||||
astUtils.getModuleExportName(parent.imported) === localName
|
||||
);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports an AST node as a rule violation.
|
||||
* @param {ASTNode} node The node to report.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function report(node) {
|
||||
if (reported.has(node.range[0])) {
|
||||
return;
|
||||
}
|
||||
reported.add(node.range[0]);
|
||||
|
||||
// Report it.
|
||||
context.report({
|
||||
node,
|
||||
messageId: node.type === "PrivateIdentifier"
|
||||
? "notCamelCasePrivate"
|
||||
: "notCamelCase",
|
||||
data: { name: node.name }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports an identifier reference or a binding identifier.
|
||||
* @param {ASTNode} node The `Identifier` node to report.
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportReferenceId(node) {
|
||||
|
||||
/*
|
||||
* For backward compatibility, if it's in callings then ignore it.
|
||||
* Not sure why it is.
|
||||
*/
|
||||
if (
|
||||
node.parent.type === "CallExpression" ||
|
||||
node.parent.type === "NewExpression"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* For backward compatibility, if it's a default value of
|
||||
* destructuring/parameters then ignore it.
|
||||
* Not sure why it is.
|
||||
*/
|
||||
if (
|
||||
node.parent.type === "AssignmentPattern" &&
|
||||
node.parent.right === node
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The `ignoreDestructuring` flag skips the identifiers that uses
|
||||
* the property name as-is.
|
||||
*/
|
||||
if (ignoreDestructuring && equalsToOriginalName(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
report(node);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
// Report camelcase of global variable references ------------------
|
||||
Program(node) {
|
||||
const scope = sourceCode.getScope(node);
|
||||
|
||||
if (!ignoreGlobals) {
|
||||
|
||||
// Defined globals in config files or directive comments.
|
||||
for (const variable of scope.variables) {
|
||||
if (
|
||||
variable.identifiers.length > 0 ||
|
||||
isGoodName(variable.name)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
for (const reference of variable.references) {
|
||||
|
||||
/*
|
||||
* For backward compatibility, this rule reports read-only
|
||||
* references as well.
|
||||
*/
|
||||
reportReferenceId(reference.identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Undefined globals.
|
||||
for (const reference of scope.through) {
|
||||
const id = reference.identifier;
|
||||
|
||||
if (isGoodName(id.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* For backward compatibility, this rule reports read-only
|
||||
* references as well.
|
||||
*/
|
||||
reportReferenceId(id);
|
||||
}
|
||||
},
|
||||
|
||||
// Report camelcase of declared variables --------------------------
|
||||
[[
|
||||
"VariableDeclaration",
|
||||
"FunctionDeclaration",
|
||||
"FunctionExpression",
|
||||
"ArrowFunctionExpression",
|
||||
"ClassDeclaration",
|
||||
"ClassExpression",
|
||||
"CatchClause"
|
||||
]](node) {
|
||||
for (const variable of sourceCode.getDeclaredVariables(node)) {
|
||||
if (isGoodName(variable.name)) {
|
||||
continue;
|
||||
}
|
||||
const id = variable.identifiers[0];
|
||||
|
||||
// Report declaration.
|
||||
if (!(ignoreDestructuring && equalsToOriginalName(id))) {
|
||||
report(id);
|
||||
}
|
||||
|
||||
/*
|
||||
* For backward compatibility, report references as well.
|
||||
* It looks unnecessary because declarations are reported.
|
||||
*/
|
||||
for (const reference of variable.references) {
|
||||
if (reference.init) {
|
||||
continue; // Skip the write references of initializers.
|
||||
}
|
||||
reportReferenceId(reference.identifier);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Report camelcase in properties ----------------------------------
|
||||
[[
|
||||
"ObjectExpression > Property[computed!=true] > Identifier.key",
|
||||
"MethodDefinition[computed!=true] > Identifier.key",
|
||||
"PropertyDefinition[computed!=true] > Identifier.key",
|
||||
"MethodDefinition > PrivateIdentifier.key",
|
||||
"PropertyDefinition > PrivateIdentifier.key"
|
||||
]](node) {
|
||||
if (properties === "never" || isGoodName(node.name)) {
|
||||
return;
|
||||
}
|
||||
report(node);
|
||||
},
|
||||
"MemberExpression[computed!=true] > Identifier.property"(node) {
|
||||
if (
|
||||
properties === "never" ||
|
||||
!isAssignmentTarget(node.parent) || // ← ignore read-only references.
|
||||
isGoodName(node.name)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
report(node);
|
||||
},
|
||||
|
||||
// Report camelcase in import --------------------------------------
|
||||
ImportDeclaration(node) {
|
||||
for (const variable of sourceCode.getDeclaredVariables(node)) {
|
||||
if (isGoodName(variable.name)) {
|
||||
continue;
|
||||
}
|
||||
const id = variable.identifiers[0];
|
||||
|
||||
// Report declaration.
|
||||
if (!(ignoreImports && equalsToOriginalName(id))) {
|
||||
report(id);
|
||||
}
|
||||
|
||||
/*
|
||||
* For backward compatibility, report references as well.
|
||||
* It looks unnecessary because declarations are reported.
|
||||
*/
|
||||
for (const reference of variable.references) {
|
||||
reportReferenceId(reference.identifier);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Report camelcase in re-export -----------------------------------
|
||||
[[
|
||||
"ExportAllDeclaration > Identifier.exported",
|
||||
"ExportSpecifier > Identifier.exported"
|
||||
]](node) {
|
||||
if (isGoodName(node.name)) {
|
||||
return;
|
||||
}
|
||||
report(node);
|
||||
},
|
||||
|
||||
// Report camelcase in labels --------------------------------------
|
||||
[[
|
||||
"LabeledStatement > Identifier.label",
|
||||
|
||||
/*
|
||||
* For backward compatibility, report references as well.
|
||||
* It looks unnecessary because declarations are reported.
|
||||
*/
|
||||
"BreakStatement > Identifier.label",
|
||||
"ContinueStatement > Identifier.label"
|
||||
]](node) {
|
||||
if (isGoodName(node.name)) {
|
||||
return;
|
||||
}
|
||||
report(node);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
300
qwen/nodejs/node_modules/eslint/lib/rules/capitalized-comments.js
generated
vendored
Normal file
300
qwen/nodejs/node_modules/eslint/lib/rules/capitalized-comments.js
generated
vendored
Normal file
@@ -0,0 +1,300 @@
|
||||
/**
|
||||
* @fileoverview enforce or disallow capitalization of the first letter of a comment
|
||||
* @author Kevin Partington
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const LETTER_PATTERN = require("./utils/patterns/letters");
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const DEFAULT_IGNORE_PATTERN = astUtils.COMMENTS_IGNORE_PATTERN,
|
||||
WHITESPACE = /\s/gu,
|
||||
MAYBE_URL = /^\s*[^:/?#\s]+:\/\/[^?#]/u; // TODO: Combine w/ max-len pattern?
|
||||
|
||||
/*
|
||||
* Base schema body for defining the basic capitalization rule, ignorePattern,
|
||||
* and ignoreInlineComments values.
|
||||
* This can be used in a few different ways in the actual schema.
|
||||
*/
|
||||
const SCHEMA_BODY = {
|
||||
type: "object",
|
||||
properties: {
|
||||
ignorePattern: {
|
||||
type: "string"
|
||||
},
|
||||
ignoreInlineComments: {
|
||||
type: "boolean"
|
||||
},
|
||||
ignoreConsecutiveComments: {
|
||||
type: "boolean"
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
};
|
||||
const DEFAULTS = {
|
||||
ignorePattern: "",
|
||||
ignoreInlineComments: false,
|
||||
ignoreConsecutiveComments: false
|
||||
};
|
||||
|
||||
/**
|
||||
* Get normalized options for either block or line comments from the given
|
||||
* user-provided options.
|
||||
* - If the user-provided options is just a string, returns a normalized
|
||||
* set of options using default values for all other options.
|
||||
* - If the user-provided options is an object, then a normalized option
|
||||
* set is returned. Options specified in overrides will take priority
|
||||
* over options specified in the main options object, which will in
|
||||
* turn take priority over the rule's defaults.
|
||||
* @param {Object|string} rawOptions The user-provided options.
|
||||
* @param {string} which Either "line" or "block".
|
||||
* @returns {Object} The normalized options.
|
||||
*/
|
||||
function getNormalizedOptions(rawOptions, which) {
|
||||
return Object.assign({}, DEFAULTS, rawOptions[which] || rawOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get normalized options for block and line comments.
|
||||
* @param {Object|string} rawOptions The user-provided options.
|
||||
* @returns {Object} An object with "Line" and "Block" keys and corresponding
|
||||
* normalized options objects.
|
||||
*/
|
||||
function getAllNormalizedOptions(rawOptions = {}) {
|
||||
return {
|
||||
Line: getNormalizedOptions(rawOptions, "line"),
|
||||
Block: getNormalizedOptions(rawOptions, "block")
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a regular expression for each ignorePattern defined in the rule
|
||||
* options.
|
||||
*
|
||||
* This is done in order to avoid invoking the RegExp constructor repeatedly.
|
||||
* @param {Object} normalizedOptions The normalized rule options.
|
||||
* @returns {void}
|
||||
*/
|
||||
function createRegExpForIgnorePatterns(normalizedOptions) {
|
||||
Object.keys(normalizedOptions).forEach(key => {
|
||||
const ignorePatternStr = normalizedOptions[key].ignorePattern;
|
||||
|
||||
if (ignorePatternStr) {
|
||||
const regExp = RegExp(`^\\s*(?:${ignorePatternStr})`, "u");
|
||||
|
||||
normalizedOptions[key].ignorePatternRegExp = regExp;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Enforce or disallow capitalization of the first letter of a comment",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/capitalized-comments"
|
||||
},
|
||||
|
||||
fixable: "code",
|
||||
|
||||
schema: [
|
||||
{ enum: ["always", "never"] },
|
||||
{
|
||||
oneOf: [
|
||||
SCHEMA_BODY,
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
line: SCHEMA_BODY,
|
||||
block: SCHEMA_BODY
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
messages: {
|
||||
unexpectedLowercaseComment: "Comments should not begin with a lowercase character.",
|
||||
unexpectedUppercaseComment: "Comments should not begin with an uppercase character."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
|
||||
const capitalize = context.options[0] || "always",
|
||||
normalizedOptions = getAllNormalizedOptions(context.options[1]),
|
||||
sourceCode = context.sourceCode;
|
||||
|
||||
createRegExpForIgnorePatterns(normalizedOptions);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Helpers
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks whether a comment is an inline comment.
|
||||
*
|
||||
* For the purpose of this rule, a comment is inline if:
|
||||
* 1. The comment is preceded by a token on the same line; and
|
||||
* 2. The command is followed by a token on the same line.
|
||||
*
|
||||
* Note that the comment itself need not be single-line!
|
||||
*
|
||||
* Also, it follows from this definition that only block comments can
|
||||
* be considered as possibly inline. This is because line comments
|
||||
* would consume any following tokens on the same line as the comment.
|
||||
* @param {ASTNode} comment The comment node to check.
|
||||
* @returns {boolean} True if the comment is an inline comment, false
|
||||
* otherwise.
|
||||
*/
|
||||
function isInlineComment(comment) {
|
||||
const previousToken = sourceCode.getTokenBefore(comment, { includeComments: true }),
|
||||
nextToken = sourceCode.getTokenAfter(comment, { includeComments: true });
|
||||
|
||||
return Boolean(
|
||||
previousToken &&
|
||||
nextToken &&
|
||||
comment.loc.start.line === previousToken.loc.end.line &&
|
||||
comment.loc.end.line === nextToken.loc.start.line
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a comment follows another comment.
|
||||
* @param {ASTNode} comment The comment to check.
|
||||
* @returns {boolean} True if the comment follows a valid comment.
|
||||
*/
|
||||
function isConsecutiveComment(comment) {
|
||||
const previousTokenOrComment = sourceCode.getTokenBefore(comment, { includeComments: true });
|
||||
|
||||
return Boolean(
|
||||
previousTokenOrComment &&
|
||||
["Block", "Line"].includes(previousTokenOrComment.type)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a comment to determine if it is valid for this rule.
|
||||
* @param {ASTNode} comment The comment node to process.
|
||||
* @param {Object} options The options for checking this comment.
|
||||
* @returns {boolean} True if the comment is valid, false otherwise.
|
||||
*/
|
||||
function isCommentValid(comment, options) {
|
||||
|
||||
// 1. Check for default ignore pattern.
|
||||
if (DEFAULT_IGNORE_PATTERN.test(comment.value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. Check for custom ignore pattern.
|
||||
const commentWithoutAsterisks = comment.value
|
||||
.replace(/\*/gu, "");
|
||||
|
||||
if (options.ignorePatternRegExp && options.ignorePatternRegExp.test(commentWithoutAsterisks)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3. Check for inline comments.
|
||||
if (options.ignoreInlineComments && isInlineComment(comment)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 4. Is this a consecutive comment (and are we tolerating those)?
|
||||
if (options.ignoreConsecutiveComments && isConsecutiveComment(comment)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 5. Does the comment start with a possible URL?
|
||||
if (MAYBE_URL.test(commentWithoutAsterisks)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 6. Is the initial word character a letter?
|
||||
const commentWordCharsOnly = commentWithoutAsterisks
|
||||
.replace(WHITESPACE, "");
|
||||
|
||||
if (commentWordCharsOnly.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const firstWordChar = commentWordCharsOnly[0];
|
||||
|
||||
if (!LETTER_PATTERN.test(firstWordChar)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 7. Check the case of the initial word character.
|
||||
const isUppercase = firstWordChar !== firstWordChar.toLocaleLowerCase(),
|
||||
isLowercase = firstWordChar !== firstWordChar.toLocaleUpperCase();
|
||||
|
||||
if (capitalize === "always" && isLowercase) {
|
||||
return false;
|
||||
}
|
||||
if (capitalize === "never" && isUppercase) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a comment to determine if it needs to be reported.
|
||||
* @param {ASTNode} comment The comment node to process.
|
||||
* @returns {void}
|
||||
*/
|
||||
function processComment(comment) {
|
||||
const options = normalizedOptions[comment.type],
|
||||
commentValid = isCommentValid(comment, options);
|
||||
|
||||
if (!commentValid) {
|
||||
const messageId = capitalize === "always"
|
||||
? "unexpectedLowercaseComment"
|
||||
: "unexpectedUppercaseComment";
|
||||
|
||||
context.report({
|
||||
node: null, // Intentionally using loc instead
|
||||
loc: comment.loc,
|
||||
messageId,
|
||||
fix(fixer) {
|
||||
const match = comment.value.match(LETTER_PATTERN);
|
||||
|
||||
return fixer.replaceTextRange(
|
||||
|
||||
// Offset match.index by 2 to account for the first 2 characters that start the comment (// or /*)
|
||||
[comment.range[0] + match.index + 2, comment.range[0] + match.index + 3],
|
||||
capitalize === "always" ? match[0].toLocaleUpperCase() : match[0].toLocaleLowerCase()
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Public
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
Program() {
|
||||
const comments = sourceCode.getAllComments();
|
||||
|
||||
comments.filter(token => token.type !== "Shebang").forEach(processComment);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
187
qwen/nodejs/node_modules/eslint/lib/rules/class-methods-use-this.js
generated
vendored
Normal file
187
qwen/nodejs/node_modules/eslint/lib/rules/class-methods-use-this.js
generated
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* @fileoverview Rule to enforce that all class methods use 'this'.
|
||||
* @author Patrick Williams
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Enforce that class methods utilize `this`",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/class-methods-use-this"
|
||||
},
|
||||
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
exceptMethods: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
enforceForClassFields: {
|
||||
type: "boolean",
|
||||
default: true
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
|
||||
messages: {
|
||||
missingThis: "Expected 'this' to be used by class {{name}}."
|
||||
}
|
||||
},
|
||||
create(context) {
|
||||
const config = Object.assign({}, context.options[0]);
|
||||
const enforceForClassFields = config.enforceForClassFields !== false;
|
||||
const exceptMethods = new Set(config.exceptMethods || []);
|
||||
|
||||
const stack = [];
|
||||
|
||||
/**
|
||||
* Push `this` used flag initialized with `false` onto the stack.
|
||||
* @returns {void}
|
||||
*/
|
||||
function pushContext() {
|
||||
stack.push(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop `this` used flag from the stack.
|
||||
* @returns {boolean | undefined} `this` used flag
|
||||
*/
|
||||
function popContext() {
|
||||
return stack.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the current context to false and pushes it onto the stack.
|
||||
* These booleans represent whether 'this' has been used in the context.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function enterFunction() {
|
||||
pushContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the node is an instance method
|
||||
* @param {ASTNode} node node to check
|
||||
* @returns {boolean} True if its an instance method
|
||||
* @private
|
||||
*/
|
||||
function isInstanceMethod(node) {
|
||||
switch (node.type) {
|
||||
case "MethodDefinition":
|
||||
return !node.static && node.kind !== "constructor";
|
||||
case "PropertyDefinition":
|
||||
return !node.static && enforceForClassFields;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the node is an instance method not excluded by config
|
||||
* @param {ASTNode} node node to check
|
||||
* @returns {boolean} True if it is an instance method, and not excluded by config
|
||||
* @private
|
||||
*/
|
||||
function isIncludedInstanceMethod(node) {
|
||||
if (isInstanceMethod(node)) {
|
||||
if (node.computed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const hashIfNeeded = node.key.type === "PrivateIdentifier" ? "#" : "";
|
||||
const name = node.key.type === "Literal"
|
||||
? astUtils.getStaticStringValue(node.key)
|
||||
: (node.key.name || "");
|
||||
|
||||
return !exceptMethods.has(hashIfNeeded + name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we are leaving a function that is a method, and reports if 'this' has not been used.
|
||||
* Static methods and the constructor are exempt.
|
||||
* Then pops the context off the stack.
|
||||
* @param {ASTNode} node A function node that was entered.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function exitFunction(node) {
|
||||
const methodUsesThis = popContext();
|
||||
|
||||
if (isIncludedInstanceMethod(node.parent) && !methodUsesThis) {
|
||||
context.report({
|
||||
node,
|
||||
loc: astUtils.getFunctionHeadLoc(node, context.sourceCode),
|
||||
messageId: "missingThis",
|
||||
data: {
|
||||
name: astUtils.getFunctionNameWithKind(node)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the current context as having used 'this'.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function markThisUsed() {
|
||||
if (stack.length) {
|
||||
stack[stack.length - 1] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
FunctionDeclaration: enterFunction,
|
||||
"FunctionDeclaration:exit": exitFunction,
|
||||
FunctionExpression: enterFunction,
|
||||
"FunctionExpression:exit": exitFunction,
|
||||
|
||||
/*
|
||||
* Class field value are implicit functions.
|
||||
*/
|
||||
"PropertyDefinition > *.key:exit": pushContext,
|
||||
"PropertyDefinition:exit": popContext,
|
||||
|
||||
/*
|
||||
* Class static blocks are implicit functions. They aren't required to use `this`,
|
||||
* but we have to push context so that it captures any use of `this` in the static block
|
||||
* separately from enclosing contexts, because static blocks have their own `this` and it
|
||||
* shouldn't count as used `this` in enclosing contexts.
|
||||
*/
|
||||
StaticBlock: pushContext,
|
||||
"StaticBlock:exit": popContext,
|
||||
|
||||
ThisExpression: markThisUsed,
|
||||
Super: markThisUsed,
|
||||
...(
|
||||
enforceForClassFields && {
|
||||
"PropertyDefinition > ArrowFunctionExpression.value": enterFunction,
|
||||
"PropertyDefinition > ArrowFunctionExpression.value:exit": exitFunction
|
||||
}
|
||||
)
|
||||
};
|
||||
}
|
||||
};
|
||||
373
qwen/nodejs/node_modules/eslint/lib/rules/comma-dangle.js
generated
vendored
Normal file
373
qwen/nodejs/node_modules/eslint/lib/rules/comma-dangle.js
generated
vendored
Normal file
@@ -0,0 +1,373 @@
|
||||
/**
|
||||
* @fileoverview Rule to forbid or enforce dangling commas.
|
||||
* @author Ian Christian Myers
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const DEFAULT_OPTIONS = Object.freeze({
|
||||
arrays: "never",
|
||||
objects: "never",
|
||||
imports: "never",
|
||||
exports: "never",
|
||||
functions: "never"
|
||||
});
|
||||
|
||||
/**
|
||||
* Checks whether or not a trailing comma is allowed in a given node.
|
||||
* If the `lastItem` is `RestElement` or `RestProperty`, it disallows trailing commas.
|
||||
* @param {ASTNode} lastItem The node of the last element in the given node.
|
||||
* @returns {boolean} `true` if a trailing comma is allowed.
|
||||
*/
|
||||
function isTrailingCommaAllowed(lastItem) {
|
||||
return !(
|
||||
lastItem.type === "RestElement" ||
|
||||
lastItem.type === "RestProperty" ||
|
||||
lastItem.type === "ExperimentalRestProperty"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize option value.
|
||||
* @param {string|Object|undefined} optionValue The 1st option value to normalize.
|
||||
* @param {number} ecmaVersion The normalized ECMAScript version.
|
||||
* @returns {Object} The normalized option value.
|
||||
*/
|
||||
function normalizeOptions(optionValue, ecmaVersion) {
|
||||
if (typeof optionValue === "string") {
|
||||
return {
|
||||
arrays: optionValue,
|
||||
objects: optionValue,
|
||||
imports: optionValue,
|
||||
exports: optionValue,
|
||||
functions: ecmaVersion < 2017 ? "ignore" : optionValue
|
||||
};
|
||||
}
|
||||
if (typeof optionValue === "object" && optionValue !== null) {
|
||||
return {
|
||||
arrays: optionValue.arrays || DEFAULT_OPTIONS.arrays,
|
||||
objects: optionValue.objects || DEFAULT_OPTIONS.objects,
|
||||
imports: optionValue.imports || DEFAULT_OPTIONS.imports,
|
||||
exports: optionValue.exports || DEFAULT_OPTIONS.exports,
|
||||
functions: optionValue.functions || DEFAULT_OPTIONS.functions
|
||||
};
|
||||
}
|
||||
|
||||
return DEFAULT_OPTIONS;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
replacedBy: [],
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description: "Require or disallow trailing commas",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/comma-dangle"
|
||||
},
|
||||
|
||||
fixable: "code",
|
||||
|
||||
schema: {
|
||||
definitions: {
|
||||
value: {
|
||||
enum: [
|
||||
"always-multiline",
|
||||
"always",
|
||||
"never",
|
||||
"only-multiline"
|
||||
]
|
||||
},
|
||||
valueWithIgnore: {
|
||||
enum: [
|
||||
"always-multiline",
|
||||
"always",
|
||||
"ignore",
|
||||
"never",
|
||||
"only-multiline"
|
||||
]
|
||||
}
|
||||
},
|
||||
type: "array",
|
||||
items: [
|
||||
{
|
||||
oneOf: [
|
||||
{
|
||||
$ref: "#/definitions/value"
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
arrays: { $ref: "#/definitions/valueWithIgnore" },
|
||||
objects: { $ref: "#/definitions/valueWithIgnore" },
|
||||
imports: { $ref: "#/definitions/valueWithIgnore" },
|
||||
exports: { $ref: "#/definitions/valueWithIgnore" },
|
||||
functions: { $ref: "#/definitions/valueWithIgnore" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
additionalItems: false
|
||||
},
|
||||
|
||||
messages: {
|
||||
unexpected: "Unexpected trailing comma.",
|
||||
missing: "Missing trailing comma."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const options = normalizeOptions(context.options[0], context.languageOptions.ecmaVersion);
|
||||
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Gets the last item of the given node.
|
||||
* @param {ASTNode} node The node to get.
|
||||
* @returns {ASTNode|null} The last node or null.
|
||||
*/
|
||||
function getLastItem(node) {
|
||||
|
||||
/**
|
||||
* Returns the last element of an array
|
||||
* @param {any[]} array The input array
|
||||
* @returns {any} The last element
|
||||
*/
|
||||
function last(array) {
|
||||
return array[array.length - 1];
|
||||
}
|
||||
|
||||
switch (node.type) {
|
||||
case "ObjectExpression":
|
||||
case "ObjectPattern":
|
||||
return last(node.properties);
|
||||
case "ArrayExpression":
|
||||
case "ArrayPattern":
|
||||
return last(node.elements);
|
||||
case "ImportDeclaration":
|
||||
case "ExportNamedDeclaration":
|
||||
return last(node.specifiers);
|
||||
case "FunctionDeclaration":
|
||||
case "FunctionExpression":
|
||||
case "ArrowFunctionExpression":
|
||||
return last(node.params);
|
||||
case "CallExpression":
|
||||
case "NewExpression":
|
||||
return last(node.arguments);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the trailing comma token of the given node.
|
||||
* If the trailing comma does not exist, this returns the token which is
|
||||
* the insertion point of the trailing comma token.
|
||||
* @param {ASTNode} node The node to get.
|
||||
* @param {ASTNode} lastItem The last item of the node.
|
||||
* @returns {Token} The trailing comma token or the insertion point.
|
||||
*/
|
||||
function getTrailingToken(node, lastItem) {
|
||||
switch (node.type) {
|
||||
case "ObjectExpression":
|
||||
case "ArrayExpression":
|
||||
case "CallExpression":
|
||||
case "NewExpression":
|
||||
return sourceCode.getLastToken(node, 1);
|
||||
default: {
|
||||
const nextToken = sourceCode.getTokenAfter(lastItem);
|
||||
|
||||
if (astUtils.isCommaToken(nextToken)) {
|
||||
return nextToken;
|
||||
}
|
||||
return sourceCode.getLastToken(lastItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a given node is multiline.
|
||||
* This rule handles a given node as multiline when the closing parenthesis
|
||||
* and the last element are not on the same line.
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @returns {boolean} `true` if the node is multiline.
|
||||
*/
|
||||
function isMultiline(node) {
|
||||
const lastItem = getLastItem(node);
|
||||
|
||||
if (!lastItem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const penultimateToken = getTrailingToken(node, lastItem);
|
||||
const lastToken = sourceCode.getTokenAfter(penultimateToken);
|
||||
|
||||
return lastToken.loc.end.line !== penultimateToken.loc.end.line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports a trailing comma if it exists.
|
||||
* @param {ASTNode} node A node to check. Its type is one of
|
||||
* ObjectExpression, ObjectPattern, ArrayExpression, ArrayPattern,
|
||||
* ImportDeclaration, and ExportNamedDeclaration.
|
||||
* @returns {void}
|
||||
*/
|
||||
function forbidTrailingComma(node) {
|
||||
const lastItem = getLastItem(node);
|
||||
|
||||
if (!lastItem || (node.type === "ImportDeclaration" && lastItem.type !== "ImportSpecifier")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const trailingToken = getTrailingToken(node, lastItem);
|
||||
|
||||
if (astUtils.isCommaToken(trailingToken)) {
|
||||
context.report({
|
||||
node: lastItem,
|
||||
loc: trailingToken.loc,
|
||||
messageId: "unexpected",
|
||||
*fix(fixer) {
|
||||
yield fixer.remove(trailingToken);
|
||||
|
||||
/*
|
||||
* Extend the range of the fix to include surrounding tokens to ensure
|
||||
* that the element after which the comma is removed stays _last_.
|
||||
* This intentionally makes conflicts in fix ranges with rules that may be
|
||||
* adding or removing elements in the same autofix pass.
|
||||
* https://github.com/eslint/eslint/issues/15660
|
||||
*/
|
||||
yield fixer.insertTextBefore(sourceCode.getTokenBefore(trailingToken), "");
|
||||
yield fixer.insertTextAfter(sourceCode.getTokenAfter(trailingToken), "");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports the last element of a given node if it does not have a trailing
|
||||
* comma.
|
||||
*
|
||||
* If a given node is `ArrayPattern` which has `RestElement`, the trailing
|
||||
* comma is disallowed, so report if it exists.
|
||||
* @param {ASTNode} node A node to check. Its type is one of
|
||||
* ObjectExpression, ObjectPattern, ArrayExpression, ArrayPattern,
|
||||
* ImportDeclaration, and ExportNamedDeclaration.
|
||||
* @returns {void}
|
||||
*/
|
||||
function forceTrailingComma(node) {
|
||||
const lastItem = getLastItem(node);
|
||||
|
||||
if (!lastItem || (node.type === "ImportDeclaration" && lastItem.type !== "ImportSpecifier")) {
|
||||
return;
|
||||
}
|
||||
if (!isTrailingCommaAllowed(lastItem)) {
|
||||
forbidTrailingComma(node);
|
||||
return;
|
||||
}
|
||||
|
||||
const trailingToken = getTrailingToken(node, lastItem);
|
||||
|
||||
if (trailingToken.value !== ",") {
|
||||
context.report({
|
||||
node: lastItem,
|
||||
loc: {
|
||||
start: trailingToken.loc.end,
|
||||
end: astUtils.getNextLocation(sourceCode, trailingToken.loc.end)
|
||||
},
|
||||
messageId: "missing",
|
||||
*fix(fixer) {
|
||||
yield fixer.insertTextAfter(trailingToken, ",");
|
||||
|
||||
/*
|
||||
* Extend the range of the fix to include surrounding tokens to ensure
|
||||
* that the element after which the comma is inserted stays _last_.
|
||||
* This intentionally makes conflicts in fix ranges with rules that may be
|
||||
* adding or removing elements in the same autofix pass.
|
||||
* https://github.com/eslint/eslint/issues/15660
|
||||
*/
|
||||
yield fixer.insertTextBefore(trailingToken, "");
|
||||
yield fixer.insertTextAfter(sourceCode.getTokenAfter(trailingToken), "");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If a given node is multiline, reports the last element of a given node
|
||||
* when it does not have a trailing comma.
|
||||
* Otherwise, reports a trailing comma if it exists.
|
||||
* @param {ASTNode} node A node to check. Its type is one of
|
||||
* ObjectExpression, ObjectPattern, ArrayExpression, ArrayPattern,
|
||||
* ImportDeclaration, and ExportNamedDeclaration.
|
||||
* @returns {void}
|
||||
*/
|
||||
function forceTrailingCommaIfMultiline(node) {
|
||||
if (isMultiline(node)) {
|
||||
forceTrailingComma(node);
|
||||
} else {
|
||||
forbidTrailingComma(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only if a given node is not multiline, reports the last element of a given node
|
||||
* when it does not have a trailing comma.
|
||||
* Otherwise, reports a trailing comma if it exists.
|
||||
* @param {ASTNode} node A node to check. Its type is one of
|
||||
* ObjectExpression, ObjectPattern, ArrayExpression, ArrayPattern,
|
||||
* ImportDeclaration, and ExportNamedDeclaration.
|
||||
* @returns {void}
|
||||
*/
|
||||
function allowTrailingCommaIfMultiline(node) {
|
||||
if (!isMultiline(node)) {
|
||||
forbidTrailingComma(node);
|
||||
}
|
||||
}
|
||||
|
||||
const predicate = {
|
||||
always: forceTrailingComma,
|
||||
"always-multiline": forceTrailingCommaIfMultiline,
|
||||
"only-multiline": allowTrailingCommaIfMultiline,
|
||||
never: forbidTrailingComma,
|
||||
ignore() {}
|
||||
};
|
||||
|
||||
return {
|
||||
ObjectExpression: predicate[options.objects],
|
||||
ObjectPattern: predicate[options.objects],
|
||||
|
||||
ArrayExpression: predicate[options.arrays],
|
||||
ArrayPattern: predicate[options.arrays],
|
||||
|
||||
ImportDeclaration: predicate[options.imports],
|
||||
|
||||
ExportNamedDeclaration: predicate[options.exports],
|
||||
|
||||
FunctionDeclaration: predicate[options.functions],
|
||||
FunctionExpression: predicate[options.functions],
|
||||
ArrowFunctionExpression: predicate[options.functions],
|
||||
CallExpression: predicate[options.functions],
|
||||
NewExpression: predicate[options.functions]
|
||||
};
|
||||
}
|
||||
};
|
||||
192
qwen/nodejs/node_modules/eslint/lib/rules/comma-spacing.js
generated
vendored
Normal file
192
qwen/nodejs/node_modules/eslint/lib/rules/comma-spacing.js
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
/**
|
||||
* @fileoverview Comma spacing - validates spacing before and after comma
|
||||
* @author Vignesh Anand aka vegetableman.
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
replacedBy: [],
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description: "Enforce consistent spacing before and after commas",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/comma-spacing"
|
||||
},
|
||||
|
||||
fixable: "whitespace",
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
before: {
|
||||
type: "boolean",
|
||||
default: false
|
||||
},
|
||||
after: {
|
||||
type: "boolean",
|
||||
default: true
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
],
|
||||
|
||||
messages: {
|
||||
missing: "A space is required {{loc}} ','.",
|
||||
unexpected: "There should be no space {{loc}} ','."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
|
||||
const sourceCode = context.sourceCode;
|
||||
const tokensAndComments = sourceCode.tokensAndComments;
|
||||
|
||||
const options = {
|
||||
before: context.options[0] ? context.options[0].before : false,
|
||||
after: context.options[0] ? context.options[0].after : true
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// list of comma tokens to ignore for the check of leading whitespace
|
||||
const commaTokensToIgnore = [];
|
||||
|
||||
/**
|
||||
* Reports a spacing error with an appropriate message.
|
||||
* @param {ASTNode} node The binary expression node to report.
|
||||
* @param {string} loc Is the error "before" or "after" the comma?
|
||||
* @param {ASTNode} otherNode The node at the left or right of `node`
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function report(node, loc, otherNode) {
|
||||
context.report({
|
||||
node,
|
||||
fix(fixer) {
|
||||
if (options[loc]) {
|
||||
if (loc === "before") {
|
||||
return fixer.insertTextBefore(node, " ");
|
||||
}
|
||||
return fixer.insertTextAfter(node, " ");
|
||||
|
||||
}
|
||||
let start, end;
|
||||
const newText = "";
|
||||
|
||||
if (loc === "before") {
|
||||
start = otherNode.range[1];
|
||||
end = node.range[0];
|
||||
} else {
|
||||
start = node.range[1];
|
||||
end = otherNode.range[0];
|
||||
}
|
||||
|
||||
return fixer.replaceTextRange([start, end], newText);
|
||||
|
||||
},
|
||||
messageId: options[loc] ? "missing" : "unexpected",
|
||||
data: {
|
||||
loc
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds null elements of the given ArrayExpression or ArrayPattern node to the ignore list.
|
||||
* @param {ASTNode} node An ArrayExpression or ArrayPattern node.
|
||||
* @returns {void}
|
||||
*/
|
||||
function addNullElementsToIgnoreList(node) {
|
||||
let previousToken = sourceCode.getFirstToken(node);
|
||||
|
||||
node.elements.forEach(element => {
|
||||
let token;
|
||||
|
||||
if (element === null) {
|
||||
token = sourceCode.getTokenAfter(previousToken);
|
||||
|
||||
if (astUtils.isCommaToken(token)) {
|
||||
commaTokensToIgnore.push(token);
|
||||
}
|
||||
} else {
|
||||
token = sourceCode.getTokenAfter(element);
|
||||
}
|
||||
|
||||
previousToken = token;
|
||||
});
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
"Program:exit"() {
|
||||
tokensAndComments.forEach((token, i) => {
|
||||
|
||||
if (!astUtils.isCommaToken(token)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const previousToken = tokensAndComments[i - 1];
|
||||
const nextToken = tokensAndComments[i + 1];
|
||||
|
||||
if (
|
||||
previousToken &&
|
||||
!astUtils.isCommaToken(previousToken) && // ignore spacing between two commas
|
||||
|
||||
/*
|
||||
* `commaTokensToIgnore` are ending commas of `null` elements (array holes/elisions).
|
||||
* In addition to spacing between two commas, this can also ignore:
|
||||
*
|
||||
* - Spacing after `[` (controlled by array-bracket-spacing)
|
||||
* Example: [ , ]
|
||||
* ^
|
||||
* - Spacing after a comment (for backwards compatibility, this was possibly unintentional)
|
||||
* Example: [a, /* * / ,]
|
||||
* ^
|
||||
*/
|
||||
!commaTokensToIgnore.includes(token) &&
|
||||
|
||||
astUtils.isTokenOnSameLine(previousToken, token) &&
|
||||
options.before !== sourceCode.isSpaceBetweenTokens(previousToken, token)
|
||||
) {
|
||||
report(token, "before", previousToken);
|
||||
}
|
||||
|
||||
if (
|
||||
nextToken &&
|
||||
!astUtils.isCommaToken(nextToken) && // ignore spacing between two commas
|
||||
!astUtils.isClosingParenToken(nextToken) && // controlled by space-in-parens
|
||||
!astUtils.isClosingBracketToken(nextToken) && // controlled by array-bracket-spacing
|
||||
!astUtils.isClosingBraceToken(nextToken) && // controlled by object-curly-spacing
|
||||
!(!options.after && nextToken.type === "Line") && // special case, allow space before line comment
|
||||
astUtils.isTokenOnSameLine(token, nextToken) &&
|
||||
options.after !== sourceCode.isSpaceBetweenTokens(token, nextToken)
|
||||
) {
|
||||
report(token, "after", nextToken);
|
||||
}
|
||||
});
|
||||
},
|
||||
ArrayExpression: addNullElementsToIgnoreList,
|
||||
ArrayPattern: addNullElementsToIgnoreList
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
};
|
||||
314
qwen/nodejs/node_modules/eslint/lib/rules/comma-style.js
generated
vendored
Normal file
314
qwen/nodejs/node_modules/eslint/lib/rules/comma-style.js
generated
vendored
Normal file
@@ -0,0 +1,314 @@
|
||||
/**
|
||||
* @fileoverview Comma style - enforces comma styles of two types: last and first
|
||||
* @author Vignesh Anand aka vegetableman
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
replacedBy: [],
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description: "Enforce consistent comma style",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/comma-style"
|
||||
},
|
||||
|
||||
fixable: "code",
|
||||
|
||||
schema: [
|
||||
{
|
||||
enum: ["first", "last"]
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
exceptions: {
|
||||
type: "object",
|
||||
additionalProperties: {
|
||||
type: "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
],
|
||||
|
||||
messages: {
|
||||
unexpectedLineBeforeAndAfterComma: "Bad line breaking before and after ','.",
|
||||
expectedCommaFirst: "',' should be placed first.",
|
||||
expectedCommaLast: "',' should be placed last."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const style = context.options[0] || "last",
|
||||
sourceCode = context.sourceCode;
|
||||
const exceptions = {
|
||||
ArrayPattern: true,
|
||||
ArrowFunctionExpression: true,
|
||||
CallExpression: true,
|
||||
FunctionDeclaration: true,
|
||||
FunctionExpression: true,
|
||||
ImportDeclaration: true,
|
||||
ObjectPattern: true,
|
||||
NewExpression: true
|
||||
};
|
||||
|
||||
if (context.options.length === 2 && Object.prototype.hasOwnProperty.call(context.options[1], "exceptions")) {
|
||||
const keys = Object.keys(context.options[1].exceptions);
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
exceptions[keys[i]] = context.options[1].exceptions[keys[i]];
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Modified text based on the style
|
||||
* @param {string} styleType Style type
|
||||
* @param {string} text Source code text
|
||||
* @returns {string} modified text
|
||||
* @private
|
||||
*/
|
||||
function getReplacedText(styleType, text) {
|
||||
switch (styleType) {
|
||||
case "between":
|
||||
return `,${text.replace(astUtils.LINEBREAK_MATCHER, "")}`;
|
||||
|
||||
case "first":
|
||||
return `${text},`;
|
||||
|
||||
case "last":
|
||||
return `,${text}`;
|
||||
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the fixer function for a given style.
|
||||
* @param {string} styleType comma style
|
||||
* @param {ASTNode} previousItemToken The token to check.
|
||||
* @param {ASTNode} commaToken The token to check.
|
||||
* @param {ASTNode} currentItemToken The token to check.
|
||||
* @returns {Function} Fixer function
|
||||
* @private
|
||||
*/
|
||||
function getFixerFunction(styleType, previousItemToken, commaToken, currentItemToken) {
|
||||
const text =
|
||||
sourceCode.text.slice(previousItemToken.range[1], commaToken.range[0]) +
|
||||
sourceCode.text.slice(commaToken.range[1], currentItemToken.range[0]);
|
||||
const range = [previousItemToken.range[1], currentItemToken.range[0]];
|
||||
|
||||
return function(fixer) {
|
||||
return fixer.replaceTextRange(range, getReplacedText(styleType, text));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the spacing around single items in lists.
|
||||
* @param {Token} previousItemToken The last token from the previous item.
|
||||
* @param {Token} commaToken The token representing the comma.
|
||||
* @param {Token} currentItemToken The first token of the current item.
|
||||
* @param {Token} reportItem The item to use when reporting an error.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function validateCommaItemSpacing(previousItemToken, commaToken, currentItemToken, reportItem) {
|
||||
|
||||
// if single line
|
||||
if (astUtils.isTokenOnSameLine(commaToken, currentItemToken) &&
|
||||
astUtils.isTokenOnSameLine(previousItemToken, commaToken)) {
|
||||
|
||||
// do nothing.
|
||||
|
||||
} else if (!astUtils.isTokenOnSameLine(commaToken, currentItemToken) &&
|
||||
!astUtils.isTokenOnSameLine(previousItemToken, commaToken)) {
|
||||
|
||||
const comment = sourceCode.getCommentsAfter(commaToken)[0];
|
||||
const styleType = comment && comment.type === "Block" && astUtils.isTokenOnSameLine(commaToken, comment)
|
||||
? style
|
||||
: "between";
|
||||
|
||||
// lone comma
|
||||
context.report({
|
||||
node: reportItem,
|
||||
loc: commaToken.loc,
|
||||
messageId: "unexpectedLineBeforeAndAfterComma",
|
||||
fix: getFixerFunction(styleType, previousItemToken, commaToken, currentItemToken)
|
||||
});
|
||||
|
||||
} else if (style === "first" && !astUtils.isTokenOnSameLine(commaToken, currentItemToken)) {
|
||||
|
||||
context.report({
|
||||
node: reportItem,
|
||||
loc: commaToken.loc,
|
||||
messageId: "expectedCommaFirst",
|
||||
fix: getFixerFunction(style, previousItemToken, commaToken, currentItemToken)
|
||||
});
|
||||
|
||||
} else if (style === "last" && astUtils.isTokenOnSameLine(commaToken, currentItemToken)) {
|
||||
|
||||
context.report({
|
||||
node: reportItem,
|
||||
loc: commaToken.loc,
|
||||
messageId: "expectedCommaLast",
|
||||
fix: getFixerFunction(style, previousItemToken, commaToken, currentItemToken)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the comma placement with regards to a declaration/property/element
|
||||
* @param {ASTNode} node The binary expression node to check
|
||||
* @param {string} property The property of the node containing child nodes.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function validateComma(node, property) {
|
||||
const items = node[property],
|
||||
arrayLiteral = (node.type === "ArrayExpression" || node.type === "ArrayPattern");
|
||||
|
||||
if (items.length > 1 || arrayLiteral) {
|
||||
|
||||
// seed as opening [
|
||||
let previousItemToken = sourceCode.getFirstToken(node);
|
||||
|
||||
items.forEach(item => {
|
||||
const commaToken = item ? sourceCode.getTokenBefore(item) : previousItemToken,
|
||||
currentItemToken = item ? sourceCode.getFirstToken(item) : sourceCode.getTokenAfter(commaToken),
|
||||
reportItem = item || currentItemToken;
|
||||
|
||||
/*
|
||||
* This works by comparing three token locations:
|
||||
* - previousItemToken is the last token of the previous item
|
||||
* - commaToken is the location of the comma before the current item
|
||||
* - currentItemToken is the first token of the current item
|
||||
*
|
||||
* These values get switched around if item is undefined.
|
||||
* previousItemToken will refer to the last token not belonging
|
||||
* to the current item, which could be a comma or an opening
|
||||
* square bracket. currentItemToken could be a comma.
|
||||
*
|
||||
* All comparisons are done based on these tokens directly, so
|
||||
* they are always valid regardless of an undefined item.
|
||||
*/
|
||||
if (astUtils.isCommaToken(commaToken)) {
|
||||
validateCommaItemSpacing(previousItemToken, commaToken, currentItemToken, reportItem);
|
||||
}
|
||||
|
||||
if (item) {
|
||||
const tokenAfterItem = sourceCode.getTokenAfter(item, astUtils.isNotClosingParenToken);
|
||||
|
||||
previousItemToken = tokenAfterItem
|
||||
? sourceCode.getTokenBefore(tokenAfterItem)
|
||||
: sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1];
|
||||
} else {
|
||||
previousItemToken = currentItemToken;
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Special case for array literals that have empty last items, such
|
||||
* as [ 1, 2, ]. These arrays only have two items show up in the
|
||||
* AST, so we need to look at the token to verify that there's no
|
||||
* dangling comma.
|
||||
*/
|
||||
if (arrayLiteral) {
|
||||
|
||||
const lastToken = sourceCode.getLastToken(node),
|
||||
nextToLastToken = sourceCode.getTokenBefore(lastToken);
|
||||
|
||||
if (astUtils.isCommaToken(nextToLastToken)) {
|
||||
validateCommaItemSpacing(
|
||||
sourceCode.getTokenBefore(nextToLastToken),
|
||||
nextToLastToken,
|
||||
lastToken,
|
||||
lastToken
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
const nodes = {};
|
||||
|
||||
if (!exceptions.VariableDeclaration) {
|
||||
nodes.VariableDeclaration = function(node) {
|
||||
validateComma(node, "declarations");
|
||||
};
|
||||
}
|
||||
if (!exceptions.ObjectExpression) {
|
||||
nodes.ObjectExpression = function(node) {
|
||||
validateComma(node, "properties");
|
||||
};
|
||||
}
|
||||
if (!exceptions.ObjectPattern) {
|
||||
nodes.ObjectPattern = function(node) {
|
||||
validateComma(node, "properties");
|
||||
};
|
||||
}
|
||||
if (!exceptions.ArrayExpression) {
|
||||
nodes.ArrayExpression = function(node) {
|
||||
validateComma(node, "elements");
|
||||
};
|
||||
}
|
||||
if (!exceptions.ArrayPattern) {
|
||||
nodes.ArrayPattern = function(node) {
|
||||
validateComma(node, "elements");
|
||||
};
|
||||
}
|
||||
if (!exceptions.FunctionDeclaration) {
|
||||
nodes.FunctionDeclaration = function(node) {
|
||||
validateComma(node, "params");
|
||||
};
|
||||
}
|
||||
if (!exceptions.FunctionExpression) {
|
||||
nodes.FunctionExpression = function(node) {
|
||||
validateComma(node, "params");
|
||||
};
|
||||
}
|
||||
if (!exceptions.ArrowFunctionExpression) {
|
||||
nodes.ArrowFunctionExpression = function(node) {
|
||||
validateComma(node, "params");
|
||||
};
|
||||
}
|
||||
if (!exceptions.CallExpression) {
|
||||
nodes.CallExpression = function(node) {
|
||||
validateComma(node, "arguments");
|
||||
};
|
||||
}
|
||||
if (!exceptions.ImportDeclaration) {
|
||||
nodes.ImportDeclaration = function(node) {
|
||||
validateComma(node, "specifiers");
|
||||
};
|
||||
}
|
||||
if (!exceptions.NewExpression) {
|
||||
nodes.NewExpression = function(node) {
|
||||
validateComma(node, "arguments");
|
||||
};
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
};
|
||||
165
qwen/nodejs/node_modules/eslint/lib/rules/complexity.js
generated
vendored
Normal file
165
qwen/nodejs/node_modules/eslint/lib/rules/complexity.js
generated
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* @fileoverview Counts the cyclomatic complexity of each function of the script. See http://en.wikipedia.org/wiki/Cyclomatic_complexity.
|
||||
* Counts the number of if, conditional, for, while, try, switch/case,
|
||||
* @author Patrick Brosset
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
const { upperCaseFirst } = require("../shared/string-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Enforce a maximum cyclomatic complexity allowed in a program",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/complexity"
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
oneOf: [
|
||||
{
|
||||
type: "integer",
|
||||
minimum: 0
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
maximum: {
|
||||
type: "integer",
|
||||
minimum: 0
|
||||
},
|
||||
max: {
|
||||
type: "integer",
|
||||
minimum: 0
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
messages: {
|
||||
complex: "{{name}} has a complexity of {{complexity}}. Maximum allowed is {{max}}."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const option = context.options[0];
|
||||
let THRESHOLD = 20;
|
||||
|
||||
if (
|
||||
typeof option === "object" &&
|
||||
(Object.prototype.hasOwnProperty.call(option, "maximum") || Object.prototype.hasOwnProperty.call(option, "max"))
|
||||
) {
|
||||
THRESHOLD = option.maximum || option.max;
|
||||
} else if (typeof option === "number") {
|
||||
THRESHOLD = option;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// Using a stack to store complexity per code path
|
||||
const complexities = [];
|
||||
|
||||
/**
|
||||
* Increase the complexity of the code path in context
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function increaseComplexity() {
|
||||
complexities[complexities.length - 1]++;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public API
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
|
||||
onCodePathStart() {
|
||||
|
||||
// The initial complexity is 1, representing one execution path in the CodePath
|
||||
complexities.push(1);
|
||||
},
|
||||
|
||||
// Each branching in the code adds 1 to the complexity
|
||||
CatchClause: increaseComplexity,
|
||||
ConditionalExpression: increaseComplexity,
|
||||
LogicalExpression: increaseComplexity,
|
||||
ForStatement: increaseComplexity,
|
||||
ForInStatement: increaseComplexity,
|
||||
ForOfStatement: increaseComplexity,
|
||||
IfStatement: increaseComplexity,
|
||||
WhileStatement: increaseComplexity,
|
||||
DoWhileStatement: increaseComplexity,
|
||||
|
||||
// Avoid `default`
|
||||
"SwitchCase[test]": increaseComplexity,
|
||||
|
||||
// Logical assignment operators have short-circuiting behavior
|
||||
AssignmentExpression(node) {
|
||||
if (astUtils.isLogicalAssignmentOperator(node.operator)) {
|
||||
increaseComplexity();
|
||||
}
|
||||
},
|
||||
|
||||
onCodePathEnd(codePath, node) {
|
||||
const complexity = complexities.pop();
|
||||
|
||||
/*
|
||||
* This rule only evaluates complexity of functions, so "program" is excluded.
|
||||
* Class field initializers and class static blocks are implicit functions. Therefore,
|
||||
* they shouldn't contribute to the enclosing function's complexity, but their
|
||||
* own complexity should be evaluated.
|
||||
*/
|
||||
if (
|
||||
codePath.origin !== "function" &&
|
||||
codePath.origin !== "class-field-initializer" &&
|
||||
codePath.origin !== "class-static-block"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (complexity > THRESHOLD) {
|
||||
let name;
|
||||
|
||||
if (codePath.origin === "class-field-initializer") {
|
||||
name = "class field initializer";
|
||||
} else if (codePath.origin === "class-static-block") {
|
||||
name = "class static block";
|
||||
} else {
|
||||
name = astUtils.getFunctionNameWithKind(node);
|
||||
}
|
||||
|
||||
context.report({
|
||||
node,
|
||||
messageId: "complex",
|
||||
data: {
|
||||
name: upperCaseFirst(name),
|
||||
complexity,
|
||||
max: THRESHOLD
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
};
|
||||
208
qwen/nodejs/node_modules/eslint/lib/rules/computed-property-spacing.js
generated
vendored
Normal file
208
qwen/nodejs/node_modules/eslint/lib/rules/computed-property-spacing.js
generated
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* @fileoverview Disallows or enforces spaces inside computed properties.
|
||||
* @author Jamund Ferguson
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
replacedBy: [],
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description: "Enforce consistent spacing inside computed property brackets",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/computed-property-spacing"
|
||||
},
|
||||
|
||||
fixable: "whitespace",
|
||||
|
||||
schema: [
|
||||
{
|
||||
enum: ["always", "never"]
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
enforceForClassMembers: {
|
||||
type: "boolean",
|
||||
default: true
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
],
|
||||
|
||||
messages: {
|
||||
unexpectedSpaceBefore: "There should be no space before '{{tokenValue}}'.",
|
||||
unexpectedSpaceAfter: "There should be no space after '{{tokenValue}}'.",
|
||||
|
||||
missingSpaceBefore: "A space is required before '{{tokenValue}}'.",
|
||||
missingSpaceAfter: "A space is required after '{{tokenValue}}'."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const propertyNameMustBeSpaced = context.options[0] === "always"; // default is "never"
|
||||
const enforceForClassMembers = !context.options[1] || context.options[1].enforceForClassMembers;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Reports that there shouldn't be a space after the first token
|
||||
* @param {ASTNode} node The node to report in the event of an error.
|
||||
* @param {Token} token The token to use for the report.
|
||||
* @param {Token} tokenAfter The token after `token`.
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportNoBeginningSpace(node, token, tokenAfter) {
|
||||
context.report({
|
||||
node,
|
||||
loc: { start: token.loc.end, end: tokenAfter.loc.start },
|
||||
messageId: "unexpectedSpaceAfter",
|
||||
data: {
|
||||
tokenValue: token.value
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([token.range[1], tokenAfter.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that there shouldn't be a space before the last token
|
||||
* @param {ASTNode} node The node to report in the event of an error.
|
||||
* @param {Token} token The token to use for the report.
|
||||
* @param {Token} tokenBefore The token before `token`.
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportNoEndingSpace(node, token, tokenBefore) {
|
||||
context.report({
|
||||
node,
|
||||
loc: { start: tokenBefore.loc.end, end: token.loc.start },
|
||||
messageId: "unexpectedSpaceBefore",
|
||||
data: {
|
||||
tokenValue: token.value
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([tokenBefore.range[1], token.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that there should be a space after the first token
|
||||
* @param {ASTNode} node The node to report in the event of an error.
|
||||
* @param {Token} token The token to use for the report.
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportRequiredBeginningSpace(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc,
|
||||
messageId: "missingSpaceAfter",
|
||||
data: {
|
||||
tokenValue: token.value
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(token, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that there should be a space before the last token
|
||||
* @param {ASTNode} node The node to report in the event of an error.
|
||||
* @param {Token} token The token to use for the report.
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportRequiredEndingSpace(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc,
|
||||
messageId: "missingSpaceBefore",
|
||||
data: {
|
||||
tokenValue: token.value
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(token, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a function that checks the spacing of a node on the property name
|
||||
* that was passed in.
|
||||
* @param {string} propertyName The property on the node to check for spacing
|
||||
* @returns {Function} A function that will check spacing on a node
|
||||
*/
|
||||
function checkSpacing(propertyName) {
|
||||
return function(node) {
|
||||
if (!node.computed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const property = node[propertyName];
|
||||
|
||||
const before = sourceCode.getTokenBefore(property, astUtils.isOpeningBracketToken),
|
||||
first = sourceCode.getTokenAfter(before, { includeComments: true }),
|
||||
after = sourceCode.getTokenAfter(property, astUtils.isClosingBracketToken),
|
||||
last = sourceCode.getTokenBefore(after, { includeComments: true });
|
||||
|
||||
if (astUtils.isTokenOnSameLine(before, first)) {
|
||||
if (propertyNameMustBeSpaced) {
|
||||
if (!sourceCode.isSpaceBetweenTokens(before, first) && astUtils.isTokenOnSameLine(before, first)) {
|
||||
reportRequiredBeginningSpace(node, before);
|
||||
}
|
||||
} else {
|
||||
if (sourceCode.isSpaceBetweenTokens(before, first)) {
|
||||
reportNoBeginningSpace(node, before, first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (astUtils.isTokenOnSameLine(last, after)) {
|
||||
if (propertyNameMustBeSpaced) {
|
||||
if (!sourceCode.isSpaceBetweenTokens(last, after) && astUtils.isTokenOnSameLine(last, after)) {
|
||||
reportRequiredEndingSpace(node, after);
|
||||
}
|
||||
} else {
|
||||
if (sourceCode.isSpaceBetweenTokens(last, after)) {
|
||||
reportNoEndingSpace(node, after, last);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
const listeners = {
|
||||
Property: checkSpacing("key"),
|
||||
MemberExpression: checkSpacing("property")
|
||||
};
|
||||
|
||||
if (enforceForClassMembers) {
|
||||
listeners.MethodDefinition =
|
||||
listeners.PropertyDefinition = listeners.Property;
|
||||
}
|
||||
|
||||
return listeners;
|
||||
|
||||
}
|
||||
};
|
||||
210
qwen/nodejs/node_modules/eslint/lib/rules/consistent-return.js
generated
vendored
Normal file
210
qwen/nodejs/node_modules/eslint/lib/rules/consistent-return.js
generated
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
/**
|
||||
* @fileoverview Rule to flag consistent return values
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
const { upperCaseFirst } = require("../shared/string-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks all segments in a set and returns true if all are unreachable.
|
||||
* @param {Set<CodePathSegment>} segments The segments to check.
|
||||
* @returns {boolean} True if all segments are unreachable; false otherwise.
|
||||
*/
|
||||
function areAllSegmentsUnreachable(segments) {
|
||||
|
||||
for (const segment of segments) {
|
||||
if (segment.reachable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given node is a `constructor` method in an ES6 class
|
||||
* @param {ASTNode} node A node to check
|
||||
* @returns {boolean} `true` if the node is a `constructor` method
|
||||
*/
|
||||
function isClassConstructor(node) {
|
||||
return node.type === "FunctionExpression" &&
|
||||
node.parent &&
|
||||
node.parent.type === "MethodDefinition" &&
|
||||
node.parent.kind === "constructor";
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Require `return` statements to either always or never specify values",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/consistent-return"
|
||||
},
|
||||
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
treatUndefinedAsUnspecified: {
|
||||
type: "boolean",
|
||||
default: false
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
|
||||
messages: {
|
||||
missingReturn: "Expected to return a value at the end of {{name}}.",
|
||||
missingReturnValue: "{{name}} expected a return value.",
|
||||
unexpectedReturnValue: "{{name}} expected no return value."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const options = context.options[0] || {};
|
||||
const treatUndefinedAsUnspecified = options.treatUndefinedAsUnspecified === true;
|
||||
let funcInfo = null;
|
||||
|
||||
/**
|
||||
* Checks whether of not the implicit returning is consistent if the last
|
||||
* code path segment is reachable.
|
||||
* @param {ASTNode} node A program/function node to check.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkLastSegment(node) {
|
||||
let loc, name;
|
||||
|
||||
/*
|
||||
* Skip if it expected no return value or unreachable.
|
||||
* When unreachable, all paths are returned or thrown.
|
||||
*/
|
||||
if (!funcInfo.hasReturnValue ||
|
||||
areAllSegmentsUnreachable(funcInfo.currentSegments) ||
|
||||
astUtils.isES5Constructor(node) ||
|
||||
isClassConstructor(node)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Adjust a location and a message.
|
||||
if (node.type === "Program") {
|
||||
|
||||
// The head of program.
|
||||
loc = { line: 1, column: 0 };
|
||||
name = "program";
|
||||
} else if (node.type === "ArrowFunctionExpression") {
|
||||
|
||||
// `=>` token
|
||||
loc = context.sourceCode.getTokenBefore(node.body, astUtils.isArrowToken).loc;
|
||||
} else if (
|
||||
node.parent.type === "MethodDefinition" ||
|
||||
(node.parent.type === "Property" && node.parent.method)
|
||||
) {
|
||||
|
||||
// Method name.
|
||||
loc = node.parent.key.loc;
|
||||
} else {
|
||||
|
||||
// Function name or `function` keyword.
|
||||
loc = (node.id || context.sourceCode.getFirstToken(node)).loc;
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
name = astUtils.getFunctionNameWithKind(node);
|
||||
}
|
||||
|
||||
// Reports.
|
||||
context.report({
|
||||
node,
|
||||
loc,
|
||||
messageId: "missingReturn",
|
||||
data: { name }
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
// Initializes/Disposes state of each code path.
|
||||
onCodePathStart(codePath, node) {
|
||||
funcInfo = {
|
||||
upper: funcInfo,
|
||||
codePath,
|
||||
hasReturn: false,
|
||||
hasReturnValue: false,
|
||||
messageId: "",
|
||||
node,
|
||||
currentSegments: new Set()
|
||||
};
|
||||
},
|
||||
onCodePathEnd() {
|
||||
funcInfo = funcInfo.upper;
|
||||
},
|
||||
|
||||
onUnreachableCodePathSegmentStart(segment) {
|
||||
funcInfo.currentSegments.add(segment);
|
||||
},
|
||||
|
||||
onUnreachableCodePathSegmentEnd(segment) {
|
||||
funcInfo.currentSegments.delete(segment);
|
||||
},
|
||||
|
||||
onCodePathSegmentStart(segment) {
|
||||
funcInfo.currentSegments.add(segment);
|
||||
},
|
||||
|
||||
onCodePathSegmentEnd(segment) {
|
||||
funcInfo.currentSegments.delete(segment);
|
||||
},
|
||||
|
||||
|
||||
// Reports a given return statement if it's inconsistent.
|
||||
ReturnStatement(node) {
|
||||
const argument = node.argument;
|
||||
let hasReturnValue = Boolean(argument);
|
||||
|
||||
if (treatUndefinedAsUnspecified && hasReturnValue) {
|
||||
hasReturnValue = !astUtils.isSpecificId(argument, "undefined") && argument.operator !== "void";
|
||||
}
|
||||
|
||||
if (!funcInfo.hasReturn) {
|
||||
funcInfo.hasReturn = true;
|
||||
funcInfo.hasReturnValue = hasReturnValue;
|
||||
funcInfo.messageId = hasReturnValue ? "missingReturnValue" : "unexpectedReturnValue";
|
||||
funcInfo.data = {
|
||||
name: funcInfo.node.type === "Program"
|
||||
? "Program"
|
||||
: upperCaseFirst(astUtils.getFunctionNameWithKind(funcInfo.node))
|
||||
};
|
||||
} else if (funcInfo.hasReturnValue !== hasReturnValue) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: funcInfo.messageId,
|
||||
data: funcInfo.data
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Reports a given program/function if the implicit returning is not consistent.
|
||||
"Program:exit": checkLastSegment,
|
||||
"FunctionDeclaration:exit": checkLastSegment,
|
||||
"FunctionExpression:exit": checkLastSegment,
|
||||
"ArrowFunctionExpression:exit": checkLastSegment
|
||||
};
|
||||
}
|
||||
};
|
||||
153
qwen/nodejs/node_modules/eslint/lib/rules/consistent-this.js
generated
vendored
Normal file
153
qwen/nodejs/node_modules/eslint/lib/rules/consistent-this.js
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* @fileoverview Rule to enforce consistent naming of "this" context variables
|
||||
* @author Raphael Pigulla
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Enforce consistent naming when capturing the current execution context",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/consistent-this"
|
||||
},
|
||||
|
||||
schema: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
minLength: 1
|
||||
},
|
||||
uniqueItems: true
|
||||
},
|
||||
|
||||
messages: {
|
||||
aliasNotAssignedToThis: "Designated alias '{{name}}' is not assigned to 'this'.",
|
||||
unexpectedAlias: "Unexpected alias '{{name}}' for 'this'."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
let aliases = [];
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
if (context.options.length === 0) {
|
||||
aliases.push("that");
|
||||
} else {
|
||||
aliases = context.options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that a variable declarator or assignment expression is assigning
|
||||
* a non-'this' value to the specified alias.
|
||||
* @param {ASTNode} node The assigning node.
|
||||
* @param {string} name the name of the alias that was incorrectly used.
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportBadAssignment(node, name) {
|
||||
context.report({ node, messageId: "aliasNotAssignedToThis", data: { name } });
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that an assignment to an identifier only assigns 'this' to the
|
||||
* appropriate alias, and the alias is only assigned to 'this'.
|
||||
* @param {ASTNode} node The assigning node.
|
||||
* @param {Identifier} name The name of the variable assigned to.
|
||||
* @param {Expression} value The value of the assignment.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkAssignment(node, name, value) {
|
||||
const isThis = value.type === "ThisExpression";
|
||||
|
||||
if (aliases.includes(name)) {
|
||||
if (!isThis || node.operator && node.operator !== "=") {
|
||||
reportBadAssignment(node, name);
|
||||
}
|
||||
} else if (isThis) {
|
||||
context.report({ node, messageId: "unexpectedAlias", data: { name } });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that a variable declaration of the alias in a program or function
|
||||
* is assigned to the correct value.
|
||||
* @param {string} alias alias the check the assignment of.
|
||||
* @param {Object} scope scope of the current code we are checking.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkWasAssigned(alias, scope) {
|
||||
const variable = scope.set.get(alias);
|
||||
|
||||
if (!variable) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (variable.defs.some(def => def.node.type === "VariableDeclarator" &&
|
||||
def.node.init !== null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The alias has been declared and not assigned: check it was
|
||||
* assigned later in the same scope.
|
||||
*/
|
||||
if (!variable.references.some(reference => {
|
||||
const write = reference.writeExpr;
|
||||
|
||||
return (
|
||||
reference.from === scope &&
|
||||
write && write.type === "ThisExpression" &&
|
||||
write.parent.operator === "="
|
||||
);
|
||||
})) {
|
||||
variable.defs.map(def => def.node).forEach(node => {
|
||||
reportBadAssignment(node, alias);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check each alias to ensure that is was assigned to the correct value.
|
||||
* @param {ASTNode} node The node that represents the scope to check.
|
||||
* @returns {void}
|
||||
*/
|
||||
function ensureWasAssigned(node) {
|
||||
const scope = sourceCode.getScope(node);
|
||||
|
||||
aliases.forEach(alias => {
|
||||
checkWasAssigned(alias, scope);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
"Program:exit": ensureWasAssigned,
|
||||
"FunctionExpression:exit": ensureWasAssigned,
|
||||
"FunctionDeclaration:exit": ensureWasAssigned,
|
||||
|
||||
VariableDeclarator(node) {
|
||||
const id = node.id;
|
||||
const isDestructuring =
|
||||
id.type === "ArrayPattern" || id.type === "ObjectPattern";
|
||||
|
||||
if (node.init !== null && !isDestructuring) {
|
||||
checkAssignment(node, id.name, node.init);
|
||||
}
|
||||
},
|
||||
|
||||
AssignmentExpression(node) {
|
||||
if (node.left.type === "Identifier") {
|
||||
checkAssignment(node, node.left.name, node.right);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
};
|
||||
446
qwen/nodejs/node_modules/eslint/lib/rules/constructor-super.js
generated
vendored
Normal file
446
qwen/nodejs/node_modules/eslint/lib/rules/constructor-super.js
generated
vendored
Normal file
@@ -0,0 +1,446 @@
|
||||
/**
|
||||
* @fileoverview A rule to verify `super()` callings in constructor.
|
||||
* @author Toru Nagashima
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks all segments in a set and returns true if any are reachable.
|
||||
* @param {Set<CodePathSegment>} segments The segments to check.
|
||||
* @returns {boolean} True if any segment is reachable; false otherwise.
|
||||
*/
|
||||
function isAnySegmentReachable(segments) {
|
||||
|
||||
for (const segment of segments) {
|
||||
if (segment.reachable) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a given node is a constructor.
|
||||
* @param {ASTNode} node A node to check. This node type is one of
|
||||
* `Program`, `FunctionDeclaration`, `FunctionExpression`, and
|
||||
* `ArrowFunctionExpression`.
|
||||
* @returns {boolean} `true` if the node is a constructor.
|
||||
*/
|
||||
function isConstructorFunction(node) {
|
||||
return (
|
||||
node.type === "FunctionExpression" &&
|
||||
node.parent.type === "MethodDefinition" &&
|
||||
node.parent.kind === "constructor"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given node can be a constructor or not.
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @returns {boolean} `true` if the node can be a constructor.
|
||||
*/
|
||||
function isPossibleConstructor(node) {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (node.type) {
|
||||
case "ClassExpression":
|
||||
case "FunctionExpression":
|
||||
case "ThisExpression":
|
||||
case "MemberExpression":
|
||||
case "CallExpression":
|
||||
case "NewExpression":
|
||||
case "ChainExpression":
|
||||
case "YieldExpression":
|
||||
case "TaggedTemplateExpression":
|
||||
case "MetaProperty":
|
||||
return true;
|
||||
|
||||
case "Identifier":
|
||||
return node.name !== "undefined";
|
||||
|
||||
case "AssignmentExpression":
|
||||
if (["=", "&&="].includes(node.operator)) {
|
||||
return isPossibleConstructor(node.right);
|
||||
}
|
||||
|
||||
if (["||=", "??="].includes(node.operator)) {
|
||||
return (
|
||||
isPossibleConstructor(node.left) ||
|
||||
isPossibleConstructor(node.right)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* All other assignment operators are mathematical assignment operators (arithmetic or bitwise).
|
||||
* An assignment expression with a mathematical operator can either evaluate to a primitive value,
|
||||
* or throw, depending on the operands. Thus, it cannot evaluate to a constructor function.
|
||||
*/
|
||||
return false;
|
||||
|
||||
case "LogicalExpression":
|
||||
|
||||
/*
|
||||
* If the && operator short-circuits, the left side was falsy and therefore not a constructor, and if
|
||||
* it doesn't short-circuit, it takes the value from the right side, so the right side must always be a
|
||||
* possible constructor. A future improvement could verify that the left side could be truthy by
|
||||
* excluding falsy literals.
|
||||
*/
|
||||
if (node.operator === "&&") {
|
||||
return isPossibleConstructor(node.right);
|
||||
}
|
||||
|
||||
return (
|
||||
isPossibleConstructor(node.left) ||
|
||||
isPossibleConstructor(node.right)
|
||||
);
|
||||
|
||||
case "ConditionalExpression":
|
||||
return (
|
||||
isPossibleConstructor(node.alternate) ||
|
||||
isPossibleConstructor(node.consequent)
|
||||
);
|
||||
|
||||
case "SequenceExpression": {
|
||||
const lastExpression = node.expressions[node.expressions.length - 1];
|
||||
|
||||
return isPossibleConstructor(lastExpression);
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "problem",
|
||||
|
||||
docs: {
|
||||
description: "Require `super()` calls in constructors",
|
||||
recommended: true,
|
||||
url: "https://eslint.org/docs/latest/rules/constructor-super"
|
||||
},
|
||||
|
||||
schema: [],
|
||||
|
||||
messages: {
|
||||
missingSome: "Lacked a call of 'super()' in some code paths.",
|
||||
missingAll: "Expected to call 'super()'.",
|
||||
|
||||
duplicate: "Unexpected duplicate 'super()'.",
|
||||
badSuper: "Unexpected 'super()' because 'super' is not a constructor.",
|
||||
unexpected: "Unexpected 'super()'."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
|
||||
/*
|
||||
* {{hasExtends: boolean, scope: Scope, codePath: CodePath}[]}
|
||||
* Information for each constructor.
|
||||
* - upper: Information of the upper constructor.
|
||||
* - hasExtends: A flag which shows whether own class has a valid `extends`
|
||||
* part.
|
||||
* - scope: The scope of own class.
|
||||
* - codePath: The code path object of the constructor.
|
||||
*/
|
||||
let funcInfo = null;
|
||||
|
||||
/*
|
||||
* {Map<string, {calledInSomePaths: boolean, calledInEveryPaths: boolean}>}
|
||||
* Information for each code path segment.
|
||||
* - calledInSomePaths: A flag of be called `super()` in some code paths.
|
||||
* - calledInEveryPaths: A flag of be called `super()` in all code paths.
|
||||
* - validNodes:
|
||||
*/
|
||||
let segInfoMap = Object.create(null);
|
||||
|
||||
/**
|
||||
* Gets the flag which shows `super()` is called in some paths.
|
||||
* @param {CodePathSegment} segment A code path segment to get.
|
||||
* @returns {boolean} The flag which shows `super()` is called in some paths
|
||||
*/
|
||||
function isCalledInSomePath(segment) {
|
||||
return segment.reachable && segInfoMap[segment.id].calledInSomePaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the flag which shows `super()` is called in all paths.
|
||||
* @param {CodePathSegment} segment A code path segment to get.
|
||||
* @returns {boolean} The flag which shows `super()` is called in all paths.
|
||||
*/
|
||||
function isCalledInEveryPath(segment) {
|
||||
|
||||
/*
|
||||
* If specific segment is the looped segment of the current segment,
|
||||
* skip the segment.
|
||||
* If not skipped, this never becomes true after a loop.
|
||||
*/
|
||||
if (segment.nextSegments.length === 1 &&
|
||||
segment.nextSegments[0].isLoopedPrevSegment(segment)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return segment.reachable && segInfoMap[segment.id].calledInEveryPaths;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
/**
|
||||
* Stacks a constructor information.
|
||||
* @param {CodePath} codePath A code path which was started.
|
||||
* @param {ASTNode} node The current node.
|
||||
* @returns {void}
|
||||
*/
|
||||
onCodePathStart(codePath, node) {
|
||||
if (isConstructorFunction(node)) {
|
||||
|
||||
// Class > ClassBody > MethodDefinition > FunctionExpression
|
||||
const classNode = node.parent.parent.parent;
|
||||
const superClass = classNode.superClass;
|
||||
|
||||
funcInfo = {
|
||||
upper: funcInfo,
|
||||
isConstructor: true,
|
||||
hasExtends: Boolean(superClass),
|
||||
superIsConstructor: isPossibleConstructor(superClass),
|
||||
codePath,
|
||||
currentSegments: new Set()
|
||||
};
|
||||
} else {
|
||||
funcInfo = {
|
||||
upper: funcInfo,
|
||||
isConstructor: false,
|
||||
hasExtends: false,
|
||||
superIsConstructor: false,
|
||||
codePath,
|
||||
currentSegments: new Set()
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Pops a constructor information.
|
||||
* And reports if `super()` lacked.
|
||||
* @param {CodePath} codePath A code path which was ended.
|
||||
* @param {ASTNode} node The current node.
|
||||
* @returns {void}
|
||||
*/
|
||||
onCodePathEnd(codePath, node) {
|
||||
const hasExtends = funcInfo.hasExtends;
|
||||
|
||||
// Pop.
|
||||
funcInfo = funcInfo.upper;
|
||||
|
||||
if (!hasExtends) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reports if `super()` lacked.
|
||||
const segments = codePath.returnedSegments;
|
||||
const calledInEveryPaths = segments.every(isCalledInEveryPath);
|
||||
const calledInSomePaths = segments.some(isCalledInSomePath);
|
||||
|
||||
if (!calledInEveryPaths) {
|
||||
context.report({
|
||||
messageId: calledInSomePaths
|
||||
? "missingSome"
|
||||
: "missingAll",
|
||||
node: node.parent
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize information of a given code path segment.
|
||||
* @param {CodePathSegment} segment A code path segment to initialize.
|
||||
* @returns {void}
|
||||
*/
|
||||
onCodePathSegmentStart(segment) {
|
||||
|
||||
funcInfo.currentSegments.add(segment);
|
||||
|
||||
if (!(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize info.
|
||||
const info = segInfoMap[segment.id] = {
|
||||
calledInSomePaths: false,
|
||||
calledInEveryPaths: false,
|
||||
validNodes: []
|
||||
};
|
||||
|
||||
// When there are previous segments, aggregates these.
|
||||
const prevSegments = segment.prevSegments;
|
||||
|
||||
if (prevSegments.length > 0) {
|
||||
info.calledInSomePaths = prevSegments.some(isCalledInSomePath);
|
||||
info.calledInEveryPaths = prevSegments.every(isCalledInEveryPath);
|
||||
}
|
||||
},
|
||||
|
||||
onUnreachableCodePathSegmentStart(segment) {
|
||||
funcInfo.currentSegments.add(segment);
|
||||
},
|
||||
|
||||
onUnreachableCodePathSegmentEnd(segment) {
|
||||
funcInfo.currentSegments.delete(segment);
|
||||
},
|
||||
|
||||
onCodePathSegmentEnd(segment) {
|
||||
funcInfo.currentSegments.delete(segment);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Update information of the code path segment when a code path was
|
||||
* looped.
|
||||
* @param {CodePathSegment} fromSegment The code path segment of the
|
||||
* end of a loop.
|
||||
* @param {CodePathSegment} toSegment A code path segment of the head
|
||||
* of a loop.
|
||||
* @returns {void}
|
||||
*/
|
||||
onCodePathSegmentLoop(fromSegment, toSegment) {
|
||||
if (!(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update information inside of the loop.
|
||||
const isRealLoop = toSegment.prevSegments.length >= 2;
|
||||
|
||||
funcInfo.codePath.traverseSegments(
|
||||
{ first: toSegment, last: fromSegment },
|
||||
segment => {
|
||||
const info = segInfoMap[segment.id];
|
||||
const prevSegments = segment.prevSegments;
|
||||
|
||||
// Updates flags.
|
||||
info.calledInSomePaths = prevSegments.some(isCalledInSomePath);
|
||||
info.calledInEveryPaths = prevSegments.every(isCalledInEveryPath);
|
||||
|
||||
// If flags become true anew, reports the valid nodes.
|
||||
if (info.calledInSomePaths || isRealLoop) {
|
||||
const nodes = info.validNodes;
|
||||
|
||||
info.validNodes = [];
|
||||
|
||||
for (let i = 0; i < nodes.length; ++i) {
|
||||
const node = nodes[i];
|
||||
|
||||
context.report({
|
||||
messageId: "duplicate",
|
||||
node
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks for a call of `super()`.
|
||||
* @param {ASTNode} node A CallExpression node to check.
|
||||
* @returns {void}
|
||||
*/
|
||||
"CallExpression:exit"(node) {
|
||||
if (!(funcInfo && funcInfo.isConstructor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skips except `super()`.
|
||||
if (node.callee.type !== "Super") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reports if needed.
|
||||
if (funcInfo.hasExtends) {
|
||||
const segments = funcInfo.currentSegments;
|
||||
let duplicate = false;
|
||||
let info = null;
|
||||
|
||||
for (const segment of segments) {
|
||||
|
||||
if (segment.reachable) {
|
||||
info = segInfoMap[segment.id];
|
||||
|
||||
duplicate = duplicate || info.calledInSomePaths;
|
||||
info.calledInSomePaths = info.calledInEveryPaths = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (info) {
|
||||
if (duplicate) {
|
||||
context.report({
|
||||
messageId: "duplicate",
|
||||
node
|
||||
});
|
||||
} else if (!funcInfo.superIsConstructor) {
|
||||
context.report({
|
||||
messageId: "badSuper",
|
||||
node
|
||||
});
|
||||
} else {
|
||||
info.validNodes.push(node);
|
||||
}
|
||||
}
|
||||
} else if (isAnySegmentReachable(funcInfo.currentSegments)) {
|
||||
context.report({
|
||||
messageId: "unexpected",
|
||||
node
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the mark to the returned path as `super()` was called.
|
||||
* @param {ASTNode} node A ReturnStatement node to check.
|
||||
* @returns {void}
|
||||
*/
|
||||
ReturnStatement(node) {
|
||||
if (!(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skips if no argument.
|
||||
if (!node.argument) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Returning argument is a substitute of 'super()'.
|
||||
const segments = funcInfo.currentSegments;
|
||||
|
||||
for (const segment of segments) {
|
||||
|
||||
if (segment.reachable) {
|
||||
const info = segInfoMap[segment.id];
|
||||
|
||||
info.calledInSomePaths = info.calledInEveryPaths = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets state.
|
||||
* @returns {void}
|
||||
*/
|
||||
"Program:exit"() {
|
||||
segInfoMap = Object.create(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
486
qwen/nodejs/node_modules/eslint/lib/rules/curly.js
generated
vendored
Normal file
486
qwen/nodejs/node_modules/eslint/lib/rules/curly.js
generated
vendored
Normal file
@@ -0,0 +1,486 @@
|
||||
/**
|
||||
* @fileoverview Rule to flag statements without curly braces
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Enforce consistent brace style for all control statements",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/curly"
|
||||
},
|
||||
|
||||
schema: {
|
||||
anyOf: [
|
||||
{
|
||||
type: "array",
|
||||
items: [
|
||||
{
|
||||
enum: ["all"]
|
||||
}
|
||||
],
|
||||
minItems: 0,
|
||||
maxItems: 1
|
||||
},
|
||||
{
|
||||
type: "array",
|
||||
items: [
|
||||
{
|
||||
enum: ["multi", "multi-line", "multi-or-nest"]
|
||||
},
|
||||
{
|
||||
enum: ["consistent"]
|
||||
}
|
||||
],
|
||||
minItems: 0,
|
||||
maxItems: 2
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
fixable: "code",
|
||||
|
||||
messages: {
|
||||
missingCurlyAfter: "Expected { after '{{name}}'.",
|
||||
missingCurlyAfterCondition: "Expected { after '{{name}}' condition.",
|
||||
unexpectedCurlyAfter: "Unnecessary { after '{{name}}'.",
|
||||
unexpectedCurlyAfterCondition: "Unnecessary { after '{{name}}' condition."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
|
||||
const multiOnly = (context.options[0] === "multi");
|
||||
const multiLine = (context.options[0] === "multi-line");
|
||||
const multiOrNest = (context.options[0] === "multi-or-nest");
|
||||
const consistent = (context.options[1] === "consistent");
|
||||
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines if a given node is a one-liner that's on the same line as it's preceding code.
|
||||
* @param {ASTNode} node The node to check.
|
||||
* @returns {boolean} True if the node is a one-liner that's on the same line as it's preceding code.
|
||||
* @private
|
||||
*/
|
||||
function isCollapsedOneLiner(node) {
|
||||
const before = sourceCode.getTokenBefore(node);
|
||||
const last = sourceCode.getLastToken(node);
|
||||
const lastExcludingSemicolon = astUtils.isSemicolonToken(last) ? sourceCode.getTokenBefore(last) : last;
|
||||
|
||||
return before.loc.start.line === lastExcludingSemicolon.loc.end.line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a given node is a one-liner.
|
||||
* @param {ASTNode} node The node to check.
|
||||
* @returns {boolean} True if the node is a one-liner.
|
||||
* @private
|
||||
*/
|
||||
function isOneLiner(node) {
|
||||
if (node.type === "EmptyStatement") {
|
||||
return true;
|
||||
}
|
||||
|
||||
const first = sourceCode.getFirstToken(node);
|
||||
const last = sourceCode.getLastToken(node);
|
||||
const lastExcludingSemicolon = astUtils.isSemicolonToken(last) ? sourceCode.getTokenBefore(last) : last;
|
||||
|
||||
return first.loc.start.line === lastExcludingSemicolon.loc.end.line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given node is a lexical declaration (let, const, function, or class)
|
||||
* @param {ASTNode} node The node to check
|
||||
* @returns {boolean} True if the node is a lexical declaration
|
||||
* @private
|
||||
*/
|
||||
function isLexicalDeclaration(node) {
|
||||
if (node.type === "VariableDeclaration") {
|
||||
return node.kind === "const" || node.kind === "let";
|
||||
}
|
||||
|
||||
return node.type === "FunctionDeclaration" || node.type === "ClassDeclaration";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given token is an `else` token or not.
|
||||
* @param {Token} token The token to check.
|
||||
* @returns {boolean} `true` if the token is an `else` token.
|
||||
*/
|
||||
function isElseKeywordToken(token) {
|
||||
return token.value === "else" && token.type === "Keyword";
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given node has an `else` keyword token as the first token after.
|
||||
* @param {ASTNode} node The node to check.
|
||||
* @returns {boolean} `true` if the node is followed by an `else` keyword token.
|
||||
*/
|
||||
function isFollowedByElseKeyword(node) {
|
||||
const nextToken = sourceCode.getTokenAfter(node);
|
||||
|
||||
return Boolean(nextToken) && isElseKeywordToken(nextToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a semicolon needs to be inserted after removing a set of curly brackets, in order to avoid a SyntaxError.
|
||||
* @param {Token} closingBracket The } token
|
||||
* @returns {boolean} `true` if a semicolon needs to be inserted after the last statement in the block.
|
||||
*/
|
||||
function needsSemicolon(closingBracket) {
|
||||
const tokenBefore = sourceCode.getTokenBefore(closingBracket);
|
||||
const tokenAfter = sourceCode.getTokenAfter(closingBracket);
|
||||
const lastBlockNode = sourceCode.getNodeByRangeIndex(tokenBefore.range[0]);
|
||||
|
||||
if (astUtils.isSemicolonToken(tokenBefore)) {
|
||||
|
||||
// If the last statement already has a semicolon, don't add another one.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tokenAfter) {
|
||||
|
||||
// If there are no statements after this block, there is no need to add a semicolon.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lastBlockNode.type === "BlockStatement" && lastBlockNode.parent.type !== "FunctionExpression" && lastBlockNode.parent.type !== "ArrowFunctionExpression") {
|
||||
|
||||
/*
|
||||
* If the last node surrounded by curly brackets is a BlockStatement (other than a FunctionExpression or an ArrowFunctionExpression),
|
||||
* don't insert a semicolon. Otherwise, the semicolon would be parsed as a separate statement, which would cause
|
||||
* a SyntaxError if it was followed by `else`.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tokenBefore.loc.end.line === tokenAfter.loc.start.line) {
|
||||
|
||||
// If the next token is on the same line, insert a semicolon.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (/^[([/`+-]/u.test(tokenAfter.value)) {
|
||||
|
||||
// If the next token starts with a character that would disrupt ASI, insert a semicolon.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tokenBefore.type === "Punctuator" && (tokenBefore.value === "++" || tokenBefore.value === "--")) {
|
||||
|
||||
// If the last token is ++ or --, insert a semicolon to avoid disrupting ASI.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, do not insert a semicolon.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the code represented by the given node contains an `if` statement
|
||||
* that would become associated with an `else` keyword directly appended to that code.
|
||||
*
|
||||
* Examples where it returns `true`:
|
||||
*
|
||||
* if (a)
|
||||
* foo();
|
||||
*
|
||||
* if (a) {
|
||||
* foo();
|
||||
* }
|
||||
*
|
||||
* if (a)
|
||||
* foo();
|
||||
* else if (b)
|
||||
* bar();
|
||||
*
|
||||
* while (a)
|
||||
* if (b)
|
||||
* if(c)
|
||||
* foo();
|
||||
* else
|
||||
* bar();
|
||||
*
|
||||
* Examples where it returns `false`:
|
||||
*
|
||||
* if (a)
|
||||
* foo();
|
||||
* else
|
||||
* bar();
|
||||
*
|
||||
* while (a) {
|
||||
* if (b)
|
||||
* if(c)
|
||||
* foo();
|
||||
* else
|
||||
* bar();
|
||||
* }
|
||||
*
|
||||
* while (a)
|
||||
* if (b) {
|
||||
* if(c)
|
||||
* foo();
|
||||
* }
|
||||
* else
|
||||
* bar();
|
||||
* @param {ASTNode} node Node representing the code to check.
|
||||
* @returns {boolean} `true` if an `if` statement within the code would become associated with an `else` appended to that code.
|
||||
*/
|
||||
function hasUnsafeIf(node) {
|
||||
switch (node.type) {
|
||||
case "IfStatement":
|
||||
if (!node.alternate) {
|
||||
return true;
|
||||
}
|
||||
return hasUnsafeIf(node.alternate);
|
||||
case "ForStatement":
|
||||
case "ForInStatement":
|
||||
case "ForOfStatement":
|
||||
case "LabeledStatement":
|
||||
case "WithStatement":
|
||||
case "WhileStatement":
|
||||
return hasUnsafeIf(node.body);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the existing curly braces around the single statement are necessary to preserve the semantics of the code.
|
||||
* The braces, which make the given block body, are necessary in either of the following situations:
|
||||
*
|
||||
* 1. The statement is a lexical declaration.
|
||||
* 2. Without the braces, an `if` within the statement would become associated with an `else` after the closing brace:
|
||||
*
|
||||
* if (a) {
|
||||
* if (b)
|
||||
* foo();
|
||||
* }
|
||||
* else
|
||||
* bar();
|
||||
*
|
||||
* if (a)
|
||||
* while (b)
|
||||
* while (c) {
|
||||
* while (d)
|
||||
* if (e)
|
||||
* while(f)
|
||||
* foo();
|
||||
* }
|
||||
* else
|
||||
* bar();
|
||||
* @param {ASTNode} node `BlockStatement` body with exactly one statement directly inside. The statement can have its own nested statements.
|
||||
* @returns {boolean} `true` if the braces are necessary - removing them (replacing the given `BlockStatement` body with its single statement content)
|
||||
* would change the semantics of the code or produce a syntax error.
|
||||
*/
|
||||
function areBracesNecessary(node) {
|
||||
const statement = node.body[0];
|
||||
|
||||
return isLexicalDeclaration(statement) ||
|
||||
hasUnsafeIf(statement) && isFollowedByElseKeyword(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares to check the body of a node to see if it's a block statement.
|
||||
* @param {ASTNode} node The node to report if there's a problem.
|
||||
* @param {ASTNode} body The body node to check for blocks.
|
||||
* @param {string} name The name to report if there's a problem.
|
||||
* @param {{ condition: boolean }} opts Options to pass to the report functions
|
||||
* @returns {Object} a prepared check object, with "actual", "expected", "check" properties.
|
||||
* "actual" will be `true` or `false` whether the body is already a block statement.
|
||||
* "expected" will be `true` or `false` if the body should be a block statement or not, or
|
||||
* `null` if it doesn't matter, depending on the rule options. It can be modified to change
|
||||
* the final behavior of "check".
|
||||
* "check" will be a function reporting appropriate problems depending on the other
|
||||
* properties.
|
||||
*/
|
||||
function prepareCheck(node, body, name, opts) {
|
||||
const hasBlock = (body.type === "BlockStatement");
|
||||
let expected = null;
|
||||
|
||||
if (hasBlock && (body.body.length !== 1 || areBracesNecessary(body))) {
|
||||
expected = true;
|
||||
} else if (multiOnly) {
|
||||
expected = false;
|
||||
} else if (multiLine) {
|
||||
if (!isCollapsedOneLiner(body)) {
|
||||
expected = true;
|
||||
}
|
||||
|
||||
// otherwise, the body is allowed to have braces or not to have braces
|
||||
|
||||
} else if (multiOrNest) {
|
||||
if (hasBlock) {
|
||||
const statement = body.body[0];
|
||||
const leadingCommentsInBlock = sourceCode.getCommentsBefore(statement);
|
||||
|
||||
expected = !isOneLiner(statement) || leadingCommentsInBlock.length > 0;
|
||||
} else {
|
||||
expected = !isOneLiner(body);
|
||||
}
|
||||
} else {
|
||||
|
||||
// default "all"
|
||||
expected = true;
|
||||
}
|
||||
|
||||
return {
|
||||
actual: hasBlock,
|
||||
expected,
|
||||
check() {
|
||||
if (this.expected !== null && this.expected !== this.actual) {
|
||||
if (this.expected) {
|
||||
context.report({
|
||||
node,
|
||||
loc: body.loc,
|
||||
messageId: opts && opts.condition ? "missingCurlyAfterCondition" : "missingCurlyAfter",
|
||||
data: {
|
||||
name
|
||||
},
|
||||
fix: fixer => fixer.replaceText(body, `{${sourceCode.getText(body)}}`)
|
||||
});
|
||||
} else {
|
||||
context.report({
|
||||
node,
|
||||
loc: body.loc,
|
||||
messageId: opts && opts.condition ? "unexpectedCurlyAfterCondition" : "unexpectedCurlyAfter",
|
||||
data: {
|
||||
name
|
||||
},
|
||||
fix(fixer) {
|
||||
|
||||
/*
|
||||
* `do while` expressions sometimes need a space to be inserted after `do`.
|
||||
* e.g. `do{foo()} while (bar)` should be corrected to `do foo() while (bar)`
|
||||
*/
|
||||
const needsPrecedingSpace = node.type === "DoWhileStatement" &&
|
||||
sourceCode.getTokenBefore(body).range[1] === body.range[0] &&
|
||||
!astUtils.canTokensBeAdjacent("do", sourceCode.getFirstToken(body, { skip: 1 }));
|
||||
|
||||
const openingBracket = sourceCode.getFirstToken(body);
|
||||
const closingBracket = sourceCode.getLastToken(body);
|
||||
const lastTokenInBlock = sourceCode.getTokenBefore(closingBracket);
|
||||
|
||||
if (needsSemicolon(closingBracket)) {
|
||||
|
||||
/*
|
||||
* If removing braces would cause a SyntaxError due to multiple statements on the same line (or
|
||||
* change the semantics of the code due to ASI), don't perform a fix.
|
||||
*/
|
||||
return null;
|
||||
}
|
||||
|
||||
const resultingBodyText = sourceCode.getText().slice(openingBracket.range[1], lastTokenInBlock.range[0]) +
|
||||
sourceCode.getText(lastTokenInBlock) +
|
||||
sourceCode.getText().slice(lastTokenInBlock.range[1], closingBracket.range[0]);
|
||||
|
||||
return fixer.replaceText(body, (needsPrecedingSpace ? " " : "") + resultingBodyText);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares to check the bodies of a "if", "else if" and "else" chain.
|
||||
* @param {ASTNode} node The first IfStatement node of the chain.
|
||||
* @returns {Object[]} prepared checks for each body of the chain. See `prepareCheck` for more
|
||||
* information.
|
||||
*/
|
||||
function prepareIfChecks(node) {
|
||||
const preparedChecks = [];
|
||||
|
||||
for (let currentNode = node; currentNode; currentNode = currentNode.alternate) {
|
||||
preparedChecks.push(prepareCheck(currentNode, currentNode.consequent, "if", { condition: true }));
|
||||
if (currentNode.alternate && currentNode.alternate.type !== "IfStatement") {
|
||||
preparedChecks.push(prepareCheck(currentNode, currentNode.alternate, "else"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (consistent) {
|
||||
|
||||
/*
|
||||
* If any node should have or already have braces, make sure they
|
||||
* all have braces.
|
||||
* If all nodes shouldn't have braces, make sure they don't.
|
||||
*/
|
||||
const expected = preparedChecks.some(preparedCheck => {
|
||||
if (preparedCheck.expected !== null) {
|
||||
return preparedCheck.expected;
|
||||
}
|
||||
return preparedCheck.actual;
|
||||
});
|
||||
|
||||
preparedChecks.forEach(preparedCheck => {
|
||||
preparedCheck.expected = expected;
|
||||
});
|
||||
}
|
||||
|
||||
return preparedChecks;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
IfStatement(node) {
|
||||
const parent = node.parent;
|
||||
const isElseIf = parent.type === "IfStatement" && parent.alternate === node;
|
||||
|
||||
if (!isElseIf) {
|
||||
|
||||
// This is a top `if`, check the whole `if-else-if` chain
|
||||
prepareIfChecks(node).forEach(preparedCheck => {
|
||||
preparedCheck.check();
|
||||
});
|
||||
}
|
||||
|
||||
// Skip `else if`, it's already checked (when the top `if` was visited)
|
||||
},
|
||||
|
||||
WhileStatement(node) {
|
||||
prepareCheck(node, node.body, "while", { condition: true }).check();
|
||||
},
|
||||
|
||||
DoWhileStatement(node) {
|
||||
prepareCheck(node, node.body, "do").check();
|
||||
},
|
||||
|
||||
ForStatement(node) {
|
||||
prepareCheck(node, node.body, "for", { condition: true }).check();
|
||||
},
|
||||
|
||||
ForInStatement(node) {
|
||||
prepareCheck(node, node.body, "for-in").check();
|
||||
},
|
||||
|
||||
ForOfStatement(node) {
|
||||
prepareCheck(node, node.body, "for-of").check();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
44
qwen/nodejs/node_modules/eslint/lib/rules/default-case-last.js
generated
vendored
Normal file
44
qwen/nodejs/node_modules/eslint/lib/rules/default-case-last.js
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* @fileoverview Rule to enforce default clauses in switch statements to be last
|
||||
* @author Milos Djermanovic
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Enforce default clauses in switch statements to be last",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/default-case-last"
|
||||
},
|
||||
|
||||
schema: [],
|
||||
|
||||
messages: {
|
||||
notLast: "Default clause should be the last clause."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
return {
|
||||
SwitchStatement(node) {
|
||||
const cases = node.cases,
|
||||
indexOfDefault = cases.findIndex(c => c.test === null);
|
||||
|
||||
if (indexOfDefault !== -1 && indexOfDefault !== cases.length - 1) {
|
||||
const defaultClause = cases[indexOfDefault];
|
||||
|
||||
context.report({ node: defaultClause, messageId: "notLast" });
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
97
qwen/nodejs/node_modules/eslint/lib/rules/default-case.js
generated
vendored
Normal file
97
qwen/nodejs/node_modules/eslint/lib/rules/default-case.js
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* @fileoverview require default case in switch statements
|
||||
* @author Aliaksei Shytkin
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const DEFAULT_COMMENT_PATTERN = /^no default$/iu;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Require `default` cases in `switch` statements",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/default-case"
|
||||
},
|
||||
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
commentPattern: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
|
||||
messages: {
|
||||
missingDefaultCase: "Expected a default case."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const options = context.options[0] || {};
|
||||
const commentPattern = options.commentPattern
|
||||
? new RegExp(options.commentPattern, "u")
|
||||
: DEFAULT_COMMENT_PATTERN;
|
||||
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Shortcut to get last element of array
|
||||
* @param {*[]} collection Array
|
||||
* @returns {any} Last element
|
||||
*/
|
||||
function last(collection) {
|
||||
return collection[collection.length - 1];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
|
||||
SwitchStatement(node) {
|
||||
|
||||
if (!node.cases.length) {
|
||||
|
||||
/*
|
||||
* skip check of empty switch because there is no easy way
|
||||
* to extract comments inside it now
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
const hasDefault = node.cases.some(v => v.test === null);
|
||||
|
||||
if (!hasDefault) {
|
||||
|
||||
let comment;
|
||||
|
||||
const lastCase = last(node.cases);
|
||||
const comments = sourceCode.getCommentsAfter(lastCase);
|
||||
|
||||
if (comments.length) {
|
||||
comment = last(comments);
|
||||
}
|
||||
|
||||
if (!comment || !commentPattern.test(comment.value.trim())) {
|
||||
context.report({ node, messageId: "missingDefaultCase" });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
62
qwen/nodejs/node_modules/eslint/lib/rules/default-param-last.js
generated
vendored
Normal file
62
qwen/nodejs/node_modules/eslint/lib/rules/default-param-last.js
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* @fileoverview enforce default parameters to be last
|
||||
* @author Chiawen Chen
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Enforce default parameters to be last",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/default-param-last"
|
||||
},
|
||||
|
||||
schema: [],
|
||||
|
||||
messages: {
|
||||
shouldBeLast: "Default parameters should be last."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
|
||||
/**
|
||||
* Handler for function contexts.
|
||||
* @param {ASTNode} node function node
|
||||
* @returns {void}
|
||||
*/
|
||||
function handleFunction(node) {
|
||||
let hasSeenPlainParam = false;
|
||||
|
||||
for (let i = node.params.length - 1; i >= 0; i -= 1) {
|
||||
const param = node.params[i];
|
||||
|
||||
if (
|
||||
param.type !== "AssignmentPattern" &&
|
||||
param.type !== "RestElement"
|
||||
) {
|
||||
hasSeenPlainParam = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hasSeenPlainParam && param.type === "AssignmentPattern") {
|
||||
context.report({
|
||||
node: param,
|
||||
messageId: "shouldBeLast"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
FunctionDeclaration: handleFunction,
|
||||
FunctionExpression: handleFunction,
|
||||
ArrowFunctionExpression: handleFunction
|
||||
};
|
||||
}
|
||||
};
|
||||
108
qwen/nodejs/node_modules/eslint/lib/rules/dot-location.js
generated
vendored
Normal file
108
qwen/nodejs/node_modules/eslint/lib/rules/dot-location.js
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* @fileoverview Validates newlines before and after dots
|
||||
* @author Greg Cochard
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
replacedBy: [],
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description: "Enforce consistent newlines before and after dots",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/dot-location"
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
enum: ["object", "property"]
|
||||
}
|
||||
],
|
||||
|
||||
fixable: "code",
|
||||
|
||||
messages: {
|
||||
expectedDotAfterObject: "Expected dot to be on same line as object.",
|
||||
expectedDotBeforeProperty: "Expected dot to be on same line as property."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
|
||||
const config = context.options[0];
|
||||
|
||||
// default to onObject if no preference is passed
|
||||
const onObject = config === "object" || !config;
|
||||
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Reports if the dot between object and property is on the correct location.
|
||||
* @param {ASTNode} node The `MemberExpression` node.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkDotLocation(node) {
|
||||
const property = node.property;
|
||||
const dotToken = sourceCode.getTokenBefore(property);
|
||||
|
||||
if (onObject) {
|
||||
|
||||
// `obj` expression can be parenthesized, but those paren tokens are not a part of the `obj` node.
|
||||
const tokenBeforeDot = sourceCode.getTokenBefore(dotToken);
|
||||
|
||||
if (!astUtils.isTokenOnSameLine(tokenBeforeDot, dotToken)) {
|
||||
context.report({
|
||||
node,
|
||||
loc: dotToken.loc,
|
||||
messageId: "expectedDotAfterObject",
|
||||
*fix(fixer) {
|
||||
if (dotToken.value.startsWith(".") && astUtils.isDecimalIntegerNumericToken(tokenBeforeDot)) {
|
||||
yield fixer.insertTextAfter(tokenBeforeDot, ` ${dotToken.value}`);
|
||||
} else {
|
||||
yield fixer.insertTextAfter(tokenBeforeDot, dotToken.value);
|
||||
}
|
||||
yield fixer.remove(dotToken);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (!astUtils.isTokenOnSameLine(dotToken, property)) {
|
||||
context.report({
|
||||
node,
|
||||
loc: dotToken.loc,
|
||||
messageId: "expectedDotBeforeProperty",
|
||||
*fix(fixer) {
|
||||
yield fixer.remove(dotToken);
|
||||
yield fixer.insertTextBefore(property, dotToken.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the spacing of the dot within a member expression.
|
||||
* @param {ASTNode} node The node to check.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkNode(node) {
|
||||
if (!node.computed) {
|
||||
checkDotLocation(node);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
MemberExpression: checkNode
|
||||
};
|
||||
}
|
||||
};
|
||||
176
qwen/nodejs/node_modules/eslint/lib/rules/dot-notation.js
generated
vendored
Normal file
176
qwen/nodejs/node_modules/eslint/lib/rules/dot-notation.js
generated
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* @fileoverview Rule to warn about using dot notation instead of square bracket notation when possible.
|
||||
* @author Josh Perez
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
const keywords = require("./utils/keywords");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/u;
|
||||
|
||||
// `null` literal must be handled separately.
|
||||
const literalTypesToCheck = new Set(["string", "boolean"]);
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Enforce dot notation whenever possible",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/dot-notation"
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
allowKeywords: {
|
||||
type: "boolean",
|
||||
default: true
|
||||
},
|
||||
allowPattern: {
|
||||
type: "string",
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
],
|
||||
|
||||
fixable: "code",
|
||||
|
||||
messages: {
|
||||
useDot: "[{{key}}] is better written in dot notation.",
|
||||
useBrackets: ".{{key}} is a syntax error."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const options = context.options[0] || {};
|
||||
const allowKeywords = options.allowKeywords === void 0 || options.allowKeywords;
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
let allowPattern;
|
||||
|
||||
if (options.allowPattern) {
|
||||
allowPattern = new RegExp(options.allowPattern, "u");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the property is valid dot notation
|
||||
* @param {ASTNode} node The dot notation node
|
||||
* @param {string} value Value which is to be checked
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkComputedProperty(node, value) {
|
||||
if (
|
||||
validIdentifier.test(value) &&
|
||||
(allowKeywords || !keywords.includes(String(value))) &&
|
||||
!(allowPattern && allowPattern.test(value))
|
||||
) {
|
||||
const formattedValue = node.property.type === "Literal" ? JSON.stringify(value) : `\`${value}\``;
|
||||
|
||||
context.report({
|
||||
node: node.property,
|
||||
messageId: "useDot",
|
||||
data: {
|
||||
key: formattedValue
|
||||
},
|
||||
*fix(fixer) {
|
||||
const leftBracket = sourceCode.getTokenAfter(node.object, astUtils.isOpeningBracketToken);
|
||||
const rightBracket = sourceCode.getLastToken(node);
|
||||
const nextToken = sourceCode.getTokenAfter(node);
|
||||
|
||||
// Don't perform any fixes if there are comments inside the brackets.
|
||||
if (sourceCode.commentsExistBetween(leftBracket, rightBracket)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Replace the brackets by an identifier.
|
||||
if (!node.optional) {
|
||||
yield fixer.insertTextBefore(
|
||||
leftBracket,
|
||||
astUtils.isDecimalInteger(node.object) ? " ." : "."
|
||||
);
|
||||
}
|
||||
yield fixer.replaceTextRange(
|
||||
[leftBracket.range[0], rightBracket.range[1]],
|
||||
value
|
||||
);
|
||||
|
||||
// Insert a space after the property if it will be connected to the next token.
|
||||
if (
|
||||
nextToken &&
|
||||
rightBracket.range[1] === nextToken.range[0] &&
|
||||
!astUtils.canTokensBeAdjacent(String(value), nextToken)
|
||||
) {
|
||||
yield fixer.insertTextAfter(node, " ");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
MemberExpression(node) {
|
||||
if (
|
||||
node.computed &&
|
||||
node.property.type === "Literal" &&
|
||||
(literalTypesToCheck.has(typeof node.property.value) || astUtils.isNullLiteral(node.property))
|
||||
) {
|
||||
checkComputedProperty(node, node.property.value);
|
||||
}
|
||||
if (
|
||||
node.computed &&
|
||||
astUtils.isStaticTemplateLiteral(node.property)
|
||||
) {
|
||||
checkComputedProperty(node, node.property.quasis[0].value.cooked);
|
||||
}
|
||||
if (
|
||||
!allowKeywords &&
|
||||
!node.computed &&
|
||||
node.property.type === "Identifier" &&
|
||||
keywords.includes(String(node.property.name))
|
||||
) {
|
||||
context.report({
|
||||
node: node.property,
|
||||
messageId: "useBrackets",
|
||||
data: {
|
||||
key: node.property.name
|
||||
},
|
||||
*fix(fixer) {
|
||||
const dotToken = sourceCode.getTokenBefore(node.property);
|
||||
|
||||
// A statement that starts with `let[` is parsed as a destructuring variable declaration, not a MemberExpression.
|
||||
if (node.object.type === "Identifier" && node.object.name === "let" && !node.optional) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't perform any fixes if there are comments between the dot and the property name.
|
||||
if (sourceCode.commentsExistBetween(dotToken, node.property)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Replace the identifier to brackets.
|
||||
if (!node.optional) {
|
||||
yield fixer.remove(dotToken);
|
||||
}
|
||||
yield fixer.replaceText(node.property, `["${node.property.name}"]`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
115
qwen/nodejs/node_modules/eslint/lib/rules/eol-last.js
generated
vendored
Normal file
115
qwen/nodejs/node_modules/eslint/lib/rules/eol-last.js
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* @fileoverview Require or disallow newline at the end of files
|
||||
* @author Nodeca Team <https://github.com/nodeca>
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
replacedBy: [],
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description: "Require or disallow newline at the end of files",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/eol-last"
|
||||
},
|
||||
|
||||
fixable: "whitespace",
|
||||
|
||||
schema: [
|
||||
{
|
||||
enum: ["always", "never", "unix", "windows"]
|
||||
}
|
||||
],
|
||||
|
||||
messages: {
|
||||
missing: "Newline required at end of file but not found.",
|
||||
unexpected: "Newline not allowed at end of file."
|
||||
}
|
||||
},
|
||||
create(context) {
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
Program: function checkBadEOF(node) {
|
||||
const sourceCode = context.sourceCode,
|
||||
src = sourceCode.getText(),
|
||||
lastLine = sourceCode.lines[sourceCode.lines.length - 1],
|
||||
location = {
|
||||
column: lastLine.length,
|
||||
line: sourceCode.lines.length
|
||||
},
|
||||
LF = "\n",
|
||||
CRLF = `\r${LF}`,
|
||||
endsWithNewline = src.endsWith(LF);
|
||||
|
||||
/*
|
||||
* Empty source is always valid: No content in file so we don't
|
||||
* need to lint for a newline on the last line of content.
|
||||
*/
|
||||
if (!src.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mode = context.options[0] || "always",
|
||||
appendCRLF = false;
|
||||
|
||||
if (mode === "unix") {
|
||||
|
||||
// `"unix"` should behave exactly as `"always"`
|
||||
mode = "always";
|
||||
}
|
||||
if (mode === "windows") {
|
||||
|
||||
// `"windows"` should behave exactly as `"always"`, but append CRLF in the fixer for backwards compatibility
|
||||
mode = "always";
|
||||
appendCRLF = true;
|
||||
}
|
||||
if (mode === "always" && !endsWithNewline) {
|
||||
|
||||
// File is not newline-terminated, but should be
|
||||
context.report({
|
||||
node,
|
||||
loc: location,
|
||||
messageId: "missing",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfterRange([0, src.length], appendCRLF ? CRLF : LF);
|
||||
}
|
||||
});
|
||||
} else if (mode === "never" && endsWithNewline) {
|
||||
|
||||
const secondLastLine = sourceCode.lines[sourceCode.lines.length - 2];
|
||||
|
||||
// File is newline-terminated, but shouldn't be
|
||||
context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: { line: sourceCode.lines.length - 1, column: secondLastLine.length },
|
||||
end: { line: sourceCode.lines.length, column: 0 }
|
||||
},
|
||||
messageId: "unexpected",
|
||||
fix(fixer) {
|
||||
const finalEOLs = /(?:\r?\n)+$/u,
|
||||
match = finalEOLs.exec(sourceCode.text),
|
||||
start = match.index,
|
||||
end = sourceCode.text.length;
|
||||
|
||||
return fixer.replaceTextRange([start, end], "");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
174
qwen/nodejs/node_modules/eslint/lib/rules/eqeqeq.js
generated
vendored
Normal file
174
qwen/nodejs/node_modules/eslint/lib/rules/eqeqeq.js
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* @fileoverview Rule to flag statements that use != and == instead of !== and ===
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Require the use of `===` and `!==`",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/eqeqeq"
|
||||
},
|
||||
|
||||
schema: {
|
||||
anyOf: [
|
||||
{
|
||||
type: "array",
|
||||
items: [
|
||||
{
|
||||
enum: ["always"]
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
null: {
|
||||
enum: ["always", "never", "ignore"]
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
],
|
||||
additionalItems: false
|
||||
},
|
||||
{
|
||||
type: "array",
|
||||
items: [
|
||||
{
|
||||
enum: ["smart", "allow-null"]
|
||||
}
|
||||
],
|
||||
additionalItems: false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
fixable: "code",
|
||||
|
||||
messages: {
|
||||
unexpected: "Expected '{{expectedOperator}}' and instead saw '{{actualOperator}}'."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const config = context.options[0] || "always";
|
||||
const options = context.options[1] || {};
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
const nullOption = (config === "always")
|
||||
? options.null || "always"
|
||||
: "ignore";
|
||||
const enforceRuleForNull = (nullOption === "always");
|
||||
const enforceInverseRuleForNull = (nullOption === "never");
|
||||
|
||||
/**
|
||||
* Checks if an expression is a typeof expression
|
||||
* @param {ASTNode} node The node to check
|
||||
* @returns {boolean} if the node is a typeof expression
|
||||
*/
|
||||
function isTypeOf(node) {
|
||||
return node.type === "UnaryExpression" && node.operator === "typeof";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if either operand of a binary expression is a typeof operation
|
||||
* @param {ASTNode} node The node to check
|
||||
* @returns {boolean} if one of the operands is typeof
|
||||
* @private
|
||||
*/
|
||||
function isTypeOfBinary(node) {
|
||||
return isTypeOf(node.left) || isTypeOf(node.right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if operands are literals of the same type (via typeof)
|
||||
* @param {ASTNode} node The node to check
|
||||
* @returns {boolean} if operands are of same type
|
||||
* @private
|
||||
*/
|
||||
function areLiteralsAndSameType(node) {
|
||||
return node.left.type === "Literal" && node.right.type === "Literal" &&
|
||||
typeof node.left.value === typeof node.right.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if one of the operands is a literal null
|
||||
* @param {ASTNode} node The node to check
|
||||
* @returns {boolean} if operands are null
|
||||
* @private
|
||||
*/
|
||||
function isNullCheck(node) {
|
||||
return astUtils.isNullLiteral(node.right) || astUtils.isNullLiteral(node.left);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports a message for this rule.
|
||||
* @param {ASTNode} node The binary expression node that was checked
|
||||
* @param {string} expectedOperator The operator that was expected (either '==', '!=', '===', or '!==')
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function report(node, expectedOperator) {
|
||||
const operatorToken = sourceCode.getFirstTokenBetween(
|
||||
node.left,
|
||||
node.right,
|
||||
token => token.value === node.operator
|
||||
);
|
||||
|
||||
context.report({
|
||||
node,
|
||||
loc: operatorToken.loc,
|
||||
messageId: "unexpected",
|
||||
data: { expectedOperator, actualOperator: node.operator },
|
||||
fix(fixer) {
|
||||
|
||||
// If the comparison is a `typeof` comparison or both sides are literals with the same type, then it's safe to fix.
|
||||
if (isTypeOfBinary(node) || areLiteralsAndSameType(node)) {
|
||||
return fixer.replaceText(operatorToken, expectedOperator);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
BinaryExpression(node) {
|
||||
const isNull = isNullCheck(node);
|
||||
|
||||
if (node.operator !== "==" && node.operator !== "!=") {
|
||||
if (enforceInverseRuleForNull && isNull) {
|
||||
report(node, node.operator.slice(0, -1));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (config === "smart" && (isTypeOfBinary(node) ||
|
||||
areLiteralsAndSameType(node) || isNull)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!enforceRuleForNull && isNull) {
|
||||
return;
|
||||
}
|
||||
|
||||
report(node, `${node.operator}=`);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
};
|
||||
140
qwen/nodejs/node_modules/eslint/lib/rules/for-direction.js
generated
vendored
Normal file
140
qwen/nodejs/node_modules/eslint/lib/rules/for-direction.js
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* @fileoverview enforce "for" loop update clause moving the counter in the right direction.(for-direction)
|
||||
* @author Aladdin-ADD<hh_2013@foxmail.com>
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const { getStaticValue } = require("@eslint-community/eslint-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "problem",
|
||||
|
||||
docs: {
|
||||
description: "Enforce \"for\" loop update clause moving the counter in the right direction",
|
||||
recommended: true,
|
||||
url: "https://eslint.org/docs/latest/rules/for-direction"
|
||||
},
|
||||
|
||||
fixable: null,
|
||||
schema: [],
|
||||
|
||||
messages: {
|
||||
incorrectDirection: "The update clause in this loop moves the variable in the wrong direction."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const { sourceCode } = context;
|
||||
|
||||
/**
|
||||
* report an error.
|
||||
* @param {ASTNode} node the node to report.
|
||||
* @returns {void}
|
||||
*/
|
||||
function report(node) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "incorrectDirection"
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* check the right side of the assignment
|
||||
* @param {ASTNode} update UpdateExpression to check
|
||||
* @param {int} dir expected direction that could either be turned around or invalidated
|
||||
* @returns {int} return dir, the negated dir, or zero if the counter does not change or the direction is not clear
|
||||
*/
|
||||
function getRightDirection(update, dir) {
|
||||
const staticValue = getStaticValue(update.right, sourceCode.getScope(update));
|
||||
|
||||
if (staticValue && ["bigint", "boolean", "number"].includes(typeof staticValue.value)) {
|
||||
const sign = Math.sign(Number(staticValue.value)) || 0; // convert NaN to 0
|
||||
|
||||
return dir * sign;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* check UpdateExpression add/sub the counter
|
||||
* @param {ASTNode} update UpdateExpression to check
|
||||
* @param {string} counter variable name to check
|
||||
* @returns {int} if add return 1, if sub return -1, if nochange, return 0
|
||||
*/
|
||||
function getUpdateDirection(update, counter) {
|
||||
if (update.argument.type === "Identifier" && update.argument.name === counter) {
|
||||
if (update.operator === "++") {
|
||||
return 1;
|
||||
}
|
||||
if (update.operator === "--") {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* check AssignmentExpression add/sub the counter
|
||||
* @param {ASTNode} update AssignmentExpression to check
|
||||
* @param {string} counter variable name to check
|
||||
* @returns {int} if add return 1, if sub return -1, if nochange, return 0
|
||||
*/
|
||||
function getAssignmentDirection(update, counter) {
|
||||
if (update.left.name === counter) {
|
||||
if (update.operator === "+=") {
|
||||
return getRightDirection(update, 1);
|
||||
}
|
||||
if (update.operator === "-=") {
|
||||
return getRightDirection(update, -1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return {
|
||||
ForStatement(node) {
|
||||
|
||||
if (node.test && node.test.type === "BinaryExpression" && node.update) {
|
||||
for (const counterPosition of ["left", "right"]) {
|
||||
if (node.test[counterPosition].type !== "Identifier") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const counter = node.test[counterPosition].name;
|
||||
const operator = node.test.operator;
|
||||
const update = node.update;
|
||||
|
||||
let wrongDirection;
|
||||
|
||||
if (operator === "<" || operator === "<=") {
|
||||
wrongDirection = counterPosition === "left" ? -1 : 1;
|
||||
} else if (operator === ">" || operator === ">=") {
|
||||
wrongDirection = counterPosition === "left" ? 1 : -1;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (update.type === "UpdateExpression") {
|
||||
if (getUpdateDirection(update, counter) === wrongDirection) {
|
||||
report(node);
|
||||
}
|
||||
} else if (update.type === "AssignmentExpression" && getAssignmentDirection(update, counter) === wrongDirection) {
|
||||
report(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
233
qwen/nodejs/node_modules/eslint/lib/rules/func-call-spacing.js
generated
vendored
Normal file
233
qwen/nodejs/node_modules/eslint/lib/rules/func-call-spacing.js
generated
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
/**
|
||||
* @fileoverview Rule to control spacing within function calls
|
||||
* @author Matt DuVall <http://www.mattduvall.com>
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
replacedBy: [],
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description: "Require or disallow spacing between function identifiers and their invocations",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/func-call-spacing"
|
||||
},
|
||||
|
||||
fixable: "whitespace",
|
||||
|
||||
schema: {
|
||||
anyOf: [
|
||||
{
|
||||
type: "array",
|
||||
items: [
|
||||
{
|
||||
enum: ["never"]
|
||||
}
|
||||
],
|
||||
minItems: 0,
|
||||
maxItems: 1
|
||||
},
|
||||
{
|
||||
type: "array",
|
||||
items: [
|
||||
{
|
||||
enum: ["always"]
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
allowNewlines: {
|
||||
type: "boolean"
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
],
|
||||
minItems: 0,
|
||||
maxItems: 2
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
messages: {
|
||||
unexpectedWhitespace: "Unexpected whitespace between function name and paren.",
|
||||
unexpectedNewline: "Unexpected newline between function name and paren.",
|
||||
missing: "Missing space between function name and paren."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
|
||||
const never = context.options[0] !== "always";
|
||||
const allowNewlines = !never && context.options[1] && context.options[1].allowNewlines;
|
||||
const sourceCode = context.sourceCode;
|
||||
const text = sourceCode.getText();
|
||||
|
||||
/**
|
||||
* Check if open space is present in a function name
|
||||
* @param {ASTNode} node node to evaluate
|
||||
* @param {Token} leftToken The last token of the callee. This may be the closing parenthesis that encloses the callee.
|
||||
* @param {Token} rightToken Tha first token of the arguments. this is the opening parenthesis that encloses the arguments.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function checkSpacing(node, leftToken, rightToken) {
|
||||
const textBetweenTokens = text.slice(leftToken.range[1], rightToken.range[0]).replace(/\/\*.*?\*\//gu, "");
|
||||
const hasWhitespace = /\s/u.test(textBetweenTokens);
|
||||
const hasNewline = hasWhitespace && astUtils.LINEBREAK_MATCHER.test(textBetweenTokens);
|
||||
|
||||
/*
|
||||
* never allowNewlines hasWhitespace hasNewline message
|
||||
* F F F F Missing space between function name and paren.
|
||||
* F F F T (Invalid `!hasWhitespace && hasNewline`)
|
||||
* F F T T Unexpected newline between function name and paren.
|
||||
* F F T F (OK)
|
||||
* F T T F (OK)
|
||||
* F T T T (OK)
|
||||
* F T F T (Invalid `!hasWhitespace && hasNewline`)
|
||||
* F T F F Missing space between function name and paren.
|
||||
* T T F F (Invalid `never && allowNewlines`)
|
||||
* T T F T (Invalid `!hasWhitespace && hasNewline`)
|
||||
* T T T T (Invalid `never && allowNewlines`)
|
||||
* T T T F (Invalid `never && allowNewlines`)
|
||||
* T F T F Unexpected space between function name and paren.
|
||||
* T F T T Unexpected space between function name and paren.
|
||||
* T F F T (Invalid `!hasWhitespace && hasNewline`)
|
||||
* T F F F (OK)
|
||||
*
|
||||
* T T Unexpected space between function name and paren.
|
||||
* F F Missing space between function name and paren.
|
||||
* F F T Unexpected newline between function name and paren.
|
||||
*/
|
||||
|
||||
if (never && hasWhitespace) {
|
||||
context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: leftToken.loc.end,
|
||||
end: {
|
||||
line: rightToken.loc.start.line,
|
||||
column: rightToken.loc.start.column - 1
|
||||
}
|
||||
},
|
||||
messageId: "unexpectedWhitespace",
|
||||
fix(fixer) {
|
||||
|
||||
// Don't remove comments.
|
||||
if (sourceCode.commentsExistBetween(leftToken, rightToken)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If `?.` exists, it doesn't hide no-unexpected-multiline errors
|
||||
if (node.optional) {
|
||||
return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], "?.");
|
||||
}
|
||||
|
||||
/*
|
||||
* Only autofix if there is no newline
|
||||
* https://github.com/eslint/eslint/issues/7787
|
||||
*/
|
||||
if (hasNewline) {
|
||||
return null;
|
||||
}
|
||||
return fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
|
||||
}
|
||||
});
|
||||
} else if (!never && !hasWhitespace) {
|
||||
context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: {
|
||||
line: leftToken.loc.end.line,
|
||||
column: leftToken.loc.end.column - 1
|
||||
},
|
||||
end: rightToken.loc.start
|
||||
},
|
||||
messageId: "missing",
|
||||
fix(fixer) {
|
||||
if (node.optional) {
|
||||
return null; // Not sure if inserting a space to either before/after `?.` token.
|
||||
}
|
||||
return fixer.insertTextBefore(rightToken, " ");
|
||||
}
|
||||
});
|
||||
} else if (!never && !allowNewlines && hasNewline) {
|
||||
context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: leftToken.loc.end,
|
||||
end: rightToken.loc.start
|
||||
},
|
||||
messageId: "unexpectedNewline",
|
||||
fix(fixer) {
|
||||
|
||||
/*
|
||||
* Only autofix if there is no newline
|
||||
* https://github.com/eslint/eslint/issues/7787
|
||||
* But if `?.` exists, it doesn't hide no-unexpected-multiline errors
|
||||
*/
|
||||
if (!node.optional) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Don't remove comments.
|
||||
if (sourceCode.commentsExistBetween(leftToken, rightToken)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const range = [leftToken.range[1], rightToken.range[0]];
|
||||
const qdToken = sourceCode.getTokenAfter(leftToken);
|
||||
|
||||
if (qdToken.range[0] === leftToken.range[1]) {
|
||||
return fixer.replaceTextRange(range, "?. ");
|
||||
}
|
||||
if (qdToken.range[1] === rightToken.range[0]) {
|
||||
return fixer.replaceTextRange(range, " ?.");
|
||||
}
|
||||
return fixer.replaceTextRange(range, " ?. ");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
"CallExpression, NewExpression"(node) {
|
||||
const lastToken = sourceCode.getLastToken(node);
|
||||
const lastCalleeToken = sourceCode.getLastToken(node.callee);
|
||||
const parenToken = sourceCode.getFirstTokenBetween(lastCalleeToken, lastToken, astUtils.isOpeningParenToken);
|
||||
const prevToken = parenToken && sourceCode.getTokenBefore(parenToken, astUtils.isNotQuestionDotToken);
|
||||
|
||||
// Parens in NewExpression are optional
|
||||
if (!(parenToken && parenToken.range[1] < node.range[1])) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkSpacing(node, prevToken, parenToken);
|
||||
},
|
||||
|
||||
ImportExpression(node) {
|
||||
const leftToken = sourceCode.getFirstToken(node);
|
||||
const rightToken = sourceCode.getTokenAfter(leftToken);
|
||||
|
||||
checkSpacing(node, leftToken, rightToken);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
};
|
||||
253
qwen/nodejs/node_modules/eslint/lib/rules/func-name-matching.js
generated
vendored
Normal file
253
qwen/nodejs/node_modules/eslint/lib/rules/func-name-matching.js
generated
vendored
Normal file
@@ -0,0 +1,253 @@
|
||||
/**
|
||||
* @fileoverview Rule to require function names to match the name of the variable or property to which they are assigned.
|
||||
* @author Annie Zhang, Pavel Strashkin
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
const esutils = require("esutils");
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines if a pattern is `module.exports` or `module["exports"]`
|
||||
* @param {ASTNode} pattern The left side of the AssignmentExpression
|
||||
* @returns {boolean} True if the pattern is `module.exports` or `module["exports"]`
|
||||
*/
|
||||
function isModuleExports(pattern) {
|
||||
if (pattern.type === "MemberExpression" && pattern.object.type === "Identifier" && pattern.object.name === "module") {
|
||||
|
||||
// module.exports
|
||||
if (pattern.property.type === "Identifier" && pattern.property.name === "exports") {
|
||||
return true;
|
||||
}
|
||||
|
||||
// module["exports"]
|
||||
if (pattern.property.type === "Literal" && pattern.property.value === "exports") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a string name is a valid identifier
|
||||
* @param {string} name The string to be checked
|
||||
* @param {int} ecmaVersion The ECMAScript version if specified in the parserOptions config
|
||||
* @returns {boolean} True if the string is a valid identifier
|
||||
*/
|
||||
function isIdentifier(name, ecmaVersion) {
|
||||
if (ecmaVersion >= 2015) {
|
||||
return esutils.keyword.isIdentifierES6(name);
|
||||
}
|
||||
return esutils.keyword.isIdentifierES5(name);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const alwaysOrNever = { enum: ["always", "never"] };
|
||||
const optionsObject = {
|
||||
type: "object",
|
||||
properties: {
|
||||
considerPropertyDescriptor: {
|
||||
type: "boolean"
|
||||
},
|
||||
includeCommonJSModuleExports: {
|
||||
type: "boolean"
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
};
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Require function names to match the name of the variable or property to which they are assigned",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/func-name-matching"
|
||||
},
|
||||
|
||||
schema: {
|
||||
anyOf: [{
|
||||
type: "array",
|
||||
additionalItems: false,
|
||||
items: [alwaysOrNever, optionsObject]
|
||||
}, {
|
||||
type: "array",
|
||||
additionalItems: false,
|
||||
items: [optionsObject]
|
||||
}]
|
||||
},
|
||||
|
||||
messages: {
|
||||
matchProperty: "Function name `{{funcName}}` should match property name `{{name}}`.",
|
||||
matchVariable: "Function name `{{funcName}}` should match variable name `{{name}}`.",
|
||||
notMatchProperty: "Function name `{{funcName}}` should not match property name `{{name}}`.",
|
||||
notMatchVariable: "Function name `{{funcName}}` should not match variable name `{{name}}`."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const options = (typeof context.options[0] === "object" ? context.options[0] : context.options[1]) || {};
|
||||
const nameMatches = typeof context.options[0] === "string" ? context.options[0] : "always";
|
||||
const considerPropertyDescriptor = options.considerPropertyDescriptor;
|
||||
const includeModuleExports = options.includeCommonJSModuleExports;
|
||||
const ecmaVersion = context.languageOptions.ecmaVersion;
|
||||
|
||||
/**
|
||||
* Check whether node is a certain CallExpression.
|
||||
* @param {string} objName object name
|
||||
* @param {string} funcName function name
|
||||
* @param {ASTNode} node The node to check
|
||||
* @returns {boolean} `true` if node matches CallExpression
|
||||
*/
|
||||
function isPropertyCall(objName, funcName, node) {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
return node.type === "CallExpression" && astUtils.isSpecificMemberAccess(node.callee, objName, funcName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares identifiers based on the nameMatches option
|
||||
* @param {string} x the first identifier
|
||||
* @param {string} y the second identifier
|
||||
* @returns {boolean} whether the two identifiers should warn.
|
||||
*/
|
||||
function shouldWarn(x, y) {
|
||||
return (nameMatches === "always" && x !== y) || (nameMatches === "never" && x === y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports
|
||||
* @param {ASTNode} node The node to report
|
||||
* @param {string} name The variable or property name
|
||||
* @param {string} funcName The function name
|
||||
* @param {boolean} isProp True if the reported node is a property assignment
|
||||
* @returns {void}
|
||||
*/
|
||||
function report(node, name, funcName, isProp) {
|
||||
let messageId;
|
||||
|
||||
if (nameMatches === "always" && isProp) {
|
||||
messageId = "matchProperty";
|
||||
} else if (nameMatches === "always") {
|
||||
messageId = "matchVariable";
|
||||
} else if (isProp) {
|
||||
messageId = "notMatchProperty";
|
||||
} else {
|
||||
messageId = "notMatchVariable";
|
||||
}
|
||||
context.report({
|
||||
node,
|
||||
messageId,
|
||||
data: {
|
||||
name,
|
||||
funcName
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a given node is a string literal
|
||||
* @param {ASTNode} node The node to check
|
||||
* @returns {boolean} `true` if the node is a string literal
|
||||
*/
|
||||
function isStringLiteral(node) {
|
||||
return node.type === "Literal" && typeof node.value === "string";
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
VariableDeclarator(node) {
|
||||
if (!node.init || node.init.type !== "FunctionExpression" || node.id.type !== "Identifier") {
|
||||
return;
|
||||
}
|
||||
if (node.init.id && shouldWarn(node.id.name, node.init.id.name)) {
|
||||
report(node, node.id.name, node.init.id.name, false);
|
||||
}
|
||||
},
|
||||
|
||||
AssignmentExpression(node) {
|
||||
if (
|
||||
node.right.type !== "FunctionExpression" ||
|
||||
(node.left.computed && node.left.property.type !== "Literal") ||
|
||||
(!includeModuleExports && isModuleExports(node.left)) ||
|
||||
(node.left.type !== "Identifier" && node.left.type !== "MemberExpression")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isProp = node.left.type === "MemberExpression";
|
||||
const name = isProp ? astUtils.getStaticPropertyName(node.left) : node.left.name;
|
||||
|
||||
if (node.right.id && name && isIdentifier(name) && shouldWarn(name, node.right.id.name)) {
|
||||
report(node, name, node.right.id.name, isProp);
|
||||
}
|
||||
},
|
||||
|
||||
"Property, PropertyDefinition[value]"(node) {
|
||||
if (!(node.value.type === "FunctionExpression" && node.value.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.key.type === "Identifier" && !node.computed) {
|
||||
const functionName = node.value.id.name;
|
||||
let propertyName = node.key.name;
|
||||
|
||||
if (
|
||||
considerPropertyDescriptor &&
|
||||
propertyName === "value" &&
|
||||
node.parent.type === "ObjectExpression"
|
||||
) {
|
||||
if (isPropertyCall("Object", "defineProperty", node.parent.parent) || isPropertyCall("Reflect", "defineProperty", node.parent.parent)) {
|
||||
const property = node.parent.parent.arguments[1];
|
||||
|
||||
if (isStringLiteral(property) && shouldWarn(property.value, functionName)) {
|
||||
report(node, property.value, functionName, true);
|
||||
}
|
||||
} else if (isPropertyCall("Object", "defineProperties", node.parent.parent.parent.parent)) {
|
||||
propertyName = node.parent.parent.key.name;
|
||||
if (!node.parent.parent.computed && shouldWarn(propertyName, functionName)) {
|
||||
report(node, propertyName, functionName, true);
|
||||
}
|
||||
} else if (isPropertyCall("Object", "create", node.parent.parent.parent.parent)) {
|
||||
propertyName = node.parent.parent.key.name;
|
||||
if (!node.parent.parent.computed && shouldWarn(propertyName, functionName)) {
|
||||
report(node, propertyName, functionName, true);
|
||||
}
|
||||
} else if (shouldWarn(propertyName, functionName)) {
|
||||
report(node, propertyName, functionName, true);
|
||||
}
|
||||
} else if (shouldWarn(propertyName, functionName)) {
|
||||
report(node, propertyName, functionName, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
isStringLiteral(node.key) &&
|
||||
isIdentifier(node.key.value, ecmaVersion) &&
|
||||
shouldWarn(node.key.value, node.value.id.name)
|
||||
) {
|
||||
report(node, node.key.value, node.value.id.name, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
191
qwen/nodejs/node_modules/eslint/lib/rules/func-names.js
generated
vendored
Normal file
191
qwen/nodejs/node_modules/eslint/lib/rules/func-names.js
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* @fileoverview Rule to warn when a function expression does not have a name.
|
||||
* @author Kyle T. Nunery
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
/**
|
||||
* Checks whether or not a given variable is a function name.
|
||||
* @param {eslint-scope.Variable} variable A variable to check.
|
||||
* @returns {boolean} `true` if the variable is a function name.
|
||||
*/
|
||||
function isFunctionName(variable) {
|
||||
return variable && variable.defs[0].type === "FunctionName";
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Require or disallow named `function` expressions",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/func-names"
|
||||
},
|
||||
|
||||
schema: {
|
||||
definitions: {
|
||||
value: {
|
||||
enum: [
|
||||
"always",
|
||||
"as-needed",
|
||||
"never"
|
||||
]
|
||||
}
|
||||
},
|
||||
items: [
|
||||
{
|
||||
$ref: "#/definitions/value"
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
generators: {
|
||||
$ref: "#/definitions/value"
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
messages: {
|
||||
unnamed: "Unexpected unnamed {{name}}.",
|
||||
named: "Unexpected named {{name}}."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Returns the config option for the given node.
|
||||
* @param {ASTNode} node A node to get the config for.
|
||||
* @returns {string} The config option.
|
||||
*/
|
||||
function getConfigForNode(node) {
|
||||
if (
|
||||
node.generator &&
|
||||
context.options.length > 1 &&
|
||||
context.options[1].generators
|
||||
) {
|
||||
return context.options[1].generators;
|
||||
}
|
||||
|
||||
return context.options[0] || "always";
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the current FunctionExpression node is a get, set, or
|
||||
* shorthand method in an object literal or a class.
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @returns {boolean} True if the node is a get, set, or shorthand method.
|
||||
*/
|
||||
function isObjectOrClassMethod(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
return (parent.type === "MethodDefinition" || (
|
||||
parent.type === "Property" && (
|
||||
parent.method ||
|
||||
parent.kind === "get" ||
|
||||
parent.kind === "set"
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the current FunctionExpression node has a name that would be
|
||||
* inferred from context in a conforming ES6 environment.
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @returns {boolean} True if the node would have a name assigned automatically.
|
||||
*/
|
||||
function hasInferredName(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
return isObjectOrClassMethod(node) ||
|
||||
(parent.type === "VariableDeclarator" && parent.id.type === "Identifier" && parent.init === node) ||
|
||||
(parent.type === "Property" && parent.value === node) ||
|
||||
(parent.type === "PropertyDefinition" && parent.value === node) ||
|
||||
(parent.type === "AssignmentExpression" && parent.left.type === "Identifier" && parent.right === node) ||
|
||||
(parent.type === "AssignmentPattern" && parent.left.type === "Identifier" && parent.right === node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that an unnamed function should be named
|
||||
* @param {ASTNode} node The node to report in the event of an error.
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportUnexpectedUnnamedFunction(node) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "unnamed",
|
||||
loc: astUtils.getFunctionHeadLoc(node, sourceCode),
|
||||
data: { name: astUtils.getFunctionNameWithKind(node) }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports that a named function should be unnamed
|
||||
* @param {ASTNode} node The node to report in the event of an error.
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportUnexpectedNamedFunction(node) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "named",
|
||||
loc: astUtils.getFunctionHeadLoc(node, sourceCode),
|
||||
data: { name: astUtils.getFunctionNameWithKind(node) }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The listener for function nodes.
|
||||
* @param {ASTNode} node function node
|
||||
* @returns {void}
|
||||
*/
|
||||
function handleFunction(node) {
|
||||
|
||||
// Skip recursive functions.
|
||||
const nameVar = sourceCode.getDeclaredVariables(node)[0];
|
||||
|
||||
if (isFunctionName(nameVar) && nameVar.references.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hasName = Boolean(node.id && node.id.name);
|
||||
const config = getConfigForNode(node);
|
||||
|
||||
if (config === "never") {
|
||||
if (hasName && node.type !== "FunctionDeclaration") {
|
||||
reportUnexpectedNamedFunction(node);
|
||||
}
|
||||
} else if (config === "as-needed") {
|
||||
if (!hasName && !hasInferredName(node)) {
|
||||
reportUnexpectedUnnamedFunction(node);
|
||||
}
|
||||
} else {
|
||||
if (!hasName && !isObjectOrClassMethod(node)) {
|
||||
reportUnexpectedUnnamedFunction(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
"FunctionExpression:exit": handleFunction,
|
||||
"ExportDefaultDeclaration > FunctionDeclaration": handleFunction
|
||||
};
|
||||
}
|
||||
};
|
||||
98
qwen/nodejs/node_modules/eslint/lib/rules/func-style.js
generated
vendored
Normal file
98
qwen/nodejs/node_modules/eslint/lib/rules/func-style.js
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* @fileoverview Rule to enforce a particular function style
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Enforce the consistent use of either `function` declarations or expressions",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/func-style"
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
enum: ["declaration", "expression"]
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
allowArrowFunctions: {
|
||||
type: "boolean",
|
||||
default: false
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
],
|
||||
|
||||
messages: {
|
||||
expression: "Expected a function expression.",
|
||||
declaration: "Expected a function declaration."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
|
||||
const style = context.options[0],
|
||||
allowArrowFunctions = context.options[1] && context.options[1].allowArrowFunctions,
|
||||
enforceDeclarations = (style === "declaration"),
|
||||
stack = [];
|
||||
|
||||
const nodesToCheck = {
|
||||
FunctionDeclaration(node) {
|
||||
stack.push(false);
|
||||
|
||||
if (!enforceDeclarations && node.parent.type !== "ExportDefaultDeclaration") {
|
||||
context.report({ node, messageId: "expression" });
|
||||
}
|
||||
},
|
||||
"FunctionDeclaration:exit"() {
|
||||
stack.pop();
|
||||
},
|
||||
|
||||
FunctionExpression(node) {
|
||||
stack.push(false);
|
||||
|
||||
if (enforceDeclarations && node.parent.type === "VariableDeclarator") {
|
||||
context.report({ node: node.parent, messageId: "declaration" });
|
||||
}
|
||||
},
|
||||
"FunctionExpression:exit"() {
|
||||
stack.pop();
|
||||
},
|
||||
|
||||
ThisExpression() {
|
||||
if (stack.length > 0) {
|
||||
stack[stack.length - 1] = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!allowArrowFunctions) {
|
||||
nodesToCheck.ArrowFunctionExpression = function() {
|
||||
stack.push(false);
|
||||
};
|
||||
|
||||
nodesToCheck["ArrowFunctionExpression:exit"] = function(node) {
|
||||
const hasThisExpr = stack.pop();
|
||||
|
||||
if (enforceDeclarations && !hasThisExpr && node.parent.type === "VariableDeclarator") {
|
||||
context.report({ node: node.parent, messageId: "declaration" });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return nodesToCheck;
|
||||
|
||||
}
|
||||
};
|
||||
125
qwen/nodejs/node_modules/eslint/lib/rules/function-call-argument-newline.js
generated
vendored
Normal file
125
qwen/nodejs/node_modules/eslint/lib/rules/function-call-argument-newline.js
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* @fileoverview Rule to enforce line breaks between arguments of a function call
|
||||
* @author Alexey Gonchar <https://github.com/finico>
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
replacedBy: [],
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description: "Enforce line breaks between arguments of a function call",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/function-call-argument-newline"
|
||||
},
|
||||
|
||||
fixable: "whitespace",
|
||||
|
||||
schema: [
|
||||
{
|
||||
enum: ["always", "never", "consistent"]
|
||||
}
|
||||
],
|
||||
|
||||
messages: {
|
||||
unexpectedLineBreak: "There should be no line break here.",
|
||||
missingLineBreak: "There should be a line break after this argument."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
const checkers = {
|
||||
unexpected: {
|
||||
messageId: "unexpectedLineBreak",
|
||||
check: (prevToken, currentToken) => prevToken.loc.end.line !== currentToken.loc.start.line,
|
||||
createFix: (token, tokenBefore) => fixer =>
|
||||
fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], " ")
|
||||
},
|
||||
missing: {
|
||||
messageId: "missingLineBreak",
|
||||
check: (prevToken, currentToken) => prevToken.loc.end.line === currentToken.loc.start.line,
|
||||
createFix: (token, tokenBefore) => fixer =>
|
||||
fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], "\n")
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check all arguments for line breaks in the CallExpression
|
||||
* @param {CallExpression} node node to evaluate
|
||||
* @param {{ messageId: string, check: Function }} checker selected checker
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function checkArguments(node, checker) {
|
||||
for (let i = 1; i < node.arguments.length; i++) {
|
||||
const prevArgToken = sourceCode.getLastToken(node.arguments[i - 1]);
|
||||
const currentArgToken = sourceCode.getFirstToken(node.arguments[i]);
|
||||
|
||||
if (checker.check(prevArgToken, currentArgToken)) {
|
||||
const tokenBefore = sourceCode.getTokenBefore(
|
||||
currentArgToken,
|
||||
{ includeComments: true }
|
||||
);
|
||||
|
||||
const hasLineCommentBefore = tokenBefore.type === "Line";
|
||||
|
||||
context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: tokenBefore.loc.end,
|
||||
end: currentArgToken.loc.start
|
||||
},
|
||||
messageId: checker.messageId,
|
||||
fix: hasLineCommentBefore ? null : checker.createFix(currentArgToken, tokenBefore)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if open space is present in a function name
|
||||
* @param {CallExpression} node node to evaluate
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function check(node) {
|
||||
if (node.arguments.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
const option = context.options[0] || "always";
|
||||
|
||||
if (option === "never") {
|
||||
checkArguments(node, checkers.unexpected);
|
||||
} else if (option === "always") {
|
||||
checkArguments(node, checkers.missing);
|
||||
} else if (option === "consistent") {
|
||||
const firstArgToken = sourceCode.getLastToken(node.arguments[0]);
|
||||
const secondArgToken = sourceCode.getFirstToken(node.arguments[1]);
|
||||
|
||||
if (firstArgToken.loc.end.line === secondArgToken.loc.start.line) {
|
||||
checkArguments(node, checkers.unexpected);
|
||||
} else {
|
||||
checkArguments(node, checkers.missing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
CallExpression: check,
|
||||
NewExpression: check
|
||||
};
|
||||
}
|
||||
};
|
||||
292
qwen/nodejs/node_modules/eslint/lib/rules/function-paren-newline.js
generated
vendored
Normal file
292
qwen/nodejs/node_modules/eslint/lib/rules/function-paren-newline.js
generated
vendored
Normal file
@@ -0,0 +1,292 @@
|
||||
/**
|
||||
* @fileoverview enforce consistent line breaks inside function parentheses
|
||||
* @author Teddy Katz
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
replacedBy: [],
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description: "Enforce consistent line breaks inside function parentheses",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/function-paren-newline"
|
||||
},
|
||||
|
||||
fixable: "whitespace",
|
||||
|
||||
schema: [
|
||||
{
|
||||
oneOf: [
|
||||
{
|
||||
enum: ["always", "never", "consistent", "multiline", "multiline-arguments"]
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
minItems: {
|
||||
type: "integer",
|
||||
minimum: 0
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
messages: {
|
||||
expectedBefore: "Expected newline before ')'.",
|
||||
expectedAfter: "Expected newline after '('.",
|
||||
expectedBetween: "Expected newline between arguments/params.",
|
||||
unexpectedBefore: "Unexpected newline before ')'.",
|
||||
unexpectedAfter: "Unexpected newline after '('."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const rawOption = context.options[0] || "multiline";
|
||||
const multilineOption = rawOption === "multiline";
|
||||
const multilineArgumentsOption = rawOption === "multiline-arguments";
|
||||
const consistentOption = rawOption === "consistent";
|
||||
let minItems;
|
||||
|
||||
if (typeof rawOption === "object") {
|
||||
minItems = rawOption.minItems;
|
||||
} else if (rawOption === "always") {
|
||||
minItems = 0;
|
||||
} else if (rawOption === "never") {
|
||||
minItems = Infinity;
|
||||
} else {
|
||||
minItems = null;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Helpers
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines whether there should be newlines inside function parens
|
||||
* @param {ASTNode[]} elements The arguments or parameters in the list
|
||||
* @param {boolean} hasLeftNewline `true` if the left paren has a newline in the current code.
|
||||
* @returns {boolean} `true` if there should be newlines inside the function parens
|
||||
*/
|
||||
function shouldHaveNewlines(elements, hasLeftNewline) {
|
||||
if (multilineArgumentsOption && elements.length === 1) {
|
||||
return hasLeftNewline;
|
||||
}
|
||||
if (multilineOption || multilineArgumentsOption) {
|
||||
return elements.some((element, index) => index !== elements.length - 1 && element.loc.end.line !== elements[index + 1].loc.start.line);
|
||||
}
|
||||
if (consistentOption) {
|
||||
return hasLeftNewline;
|
||||
}
|
||||
return elements.length >= minItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates parens
|
||||
* @param {Object} parens An object with keys `leftParen` for the left paren token, and `rightParen` for the right paren token
|
||||
* @param {ASTNode[]} elements The arguments or parameters in the list
|
||||
* @returns {void}
|
||||
*/
|
||||
function validateParens(parens, elements) {
|
||||
const leftParen = parens.leftParen;
|
||||
const rightParen = parens.rightParen;
|
||||
const tokenAfterLeftParen = sourceCode.getTokenAfter(leftParen);
|
||||
const tokenBeforeRightParen = sourceCode.getTokenBefore(rightParen);
|
||||
const hasLeftNewline = !astUtils.isTokenOnSameLine(leftParen, tokenAfterLeftParen);
|
||||
const hasRightNewline = !astUtils.isTokenOnSameLine(tokenBeforeRightParen, rightParen);
|
||||
const needsNewlines = shouldHaveNewlines(elements, hasLeftNewline);
|
||||
|
||||
if (hasLeftNewline && !needsNewlines) {
|
||||
context.report({
|
||||
node: leftParen,
|
||||
messageId: "unexpectedAfter",
|
||||
fix(fixer) {
|
||||
return sourceCode.getText().slice(leftParen.range[1], tokenAfterLeftParen.range[0]).trim()
|
||||
|
||||
// If there is a comment between the ( and the first element, don't do a fix.
|
||||
? null
|
||||
: fixer.removeRange([leftParen.range[1], tokenAfterLeftParen.range[0]]);
|
||||
}
|
||||
});
|
||||
} else if (!hasLeftNewline && needsNewlines) {
|
||||
context.report({
|
||||
node: leftParen,
|
||||
messageId: "expectedAfter",
|
||||
fix: fixer => fixer.insertTextAfter(leftParen, "\n")
|
||||
});
|
||||
}
|
||||
|
||||
if (hasRightNewline && !needsNewlines) {
|
||||
context.report({
|
||||
node: rightParen,
|
||||
messageId: "unexpectedBefore",
|
||||
fix(fixer) {
|
||||
return sourceCode.getText().slice(tokenBeforeRightParen.range[1], rightParen.range[0]).trim()
|
||||
|
||||
// If there is a comment between the last element and the ), don't do a fix.
|
||||
? null
|
||||
: fixer.removeRange([tokenBeforeRightParen.range[1], rightParen.range[0]]);
|
||||
}
|
||||
});
|
||||
} else if (!hasRightNewline && needsNewlines) {
|
||||
context.report({
|
||||
node: rightParen,
|
||||
messageId: "expectedBefore",
|
||||
fix: fixer => fixer.insertTextBefore(rightParen, "\n")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a list of arguments or parameters
|
||||
* @param {Object} parens An object with keys `leftParen` for the left paren token, and `rightParen` for the right paren token
|
||||
* @param {ASTNode[]} elements The arguments or parameters in the list
|
||||
* @returns {void}
|
||||
*/
|
||||
function validateArguments(parens, elements) {
|
||||
const leftParen = parens.leftParen;
|
||||
const tokenAfterLeftParen = sourceCode.getTokenAfter(leftParen);
|
||||
const hasLeftNewline = !astUtils.isTokenOnSameLine(leftParen, tokenAfterLeftParen);
|
||||
const needsNewlines = shouldHaveNewlines(elements, hasLeftNewline);
|
||||
|
||||
for (let i = 0; i <= elements.length - 2; i++) {
|
||||
const currentElement = elements[i];
|
||||
const nextElement = elements[i + 1];
|
||||
const hasNewLine = currentElement.loc.end.line !== nextElement.loc.start.line;
|
||||
|
||||
if (!hasNewLine && needsNewlines) {
|
||||
context.report({
|
||||
node: currentElement,
|
||||
messageId: "expectedBetween",
|
||||
fix: fixer => fixer.insertTextBefore(nextElement, "\n")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the left paren and right paren tokens of a node.
|
||||
* @param {ASTNode} node The node with parens
|
||||
* @throws {TypeError} Unexpected node type.
|
||||
* @returns {Object} An object with keys `leftParen` for the left paren token, and `rightParen` for the right paren token.
|
||||
* Can also return `null` if an expression has no parens (e.g. a NewExpression with no arguments, or an ArrowFunctionExpression
|
||||
* with a single parameter)
|
||||
*/
|
||||
function getParenTokens(node) {
|
||||
switch (node.type) {
|
||||
case "NewExpression":
|
||||
if (!node.arguments.length &&
|
||||
!(
|
||||
astUtils.isOpeningParenToken(sourceCode.getLastToken(node, { skip: 1 })) &&
|
||||
astUtils.isClosingParenToken(sourceCode.getLastToken(node)) &&
|
||||
node.callee.range[1] < node.range[1]
|
||||
)
|
||||
) {
|
||||
|
||||
// If the NewExpression does not have parens (e.g. `new Foo`), return null.
|
||||
return null;
|
||||
}
|
||||
|
||||
// falls through
|
||||
|
||||
case "CallExpression":
|
||||
return {
|
||||
leftParen: sourceCode.getTokenAfter(node.callee, astUtils.isOpeningParenToken),
|
||||
rightParen: sourceCode.getLastToken(node)
|
||||
};
|
||||
|
||||
case "FunctionDeclaration":
|
||||
case "FunctionExpression": {
|
||||
const leftParen = sourceCode.getFirstToken(node, astUtils.isOpeningParenToken);
|
||||
const rightParen = node.params.length
|
||||
? sourceCode.getTokenAfter(node.params[node.params.length - 1], astUtils.isClosingParenToken)
|
||||
: sourceCode.getTokenAfter(leftParen);
|
||||
|
||||
return { leftParen, rightParen };
|
||||
}
|
||||
|
||||
case "ArrowFunctionExpression": {
|
||||
const firstToken = sourceCode.getFirstToken(node, { skip: (node.async ? 1 : 0) });
|
||||
|
||||
if (!astUtils.isOpeningParenToken(firstToken)) {
|
||||
|
||||
// If the ArrowFunctionExpression has a single param without parens, return null.
|
||||
return null;
|
||||
}
|
||||
|
||||
const rightParen = node.params.length
|
||||
? sourceCode.getTokenAfter(node.params[node.params.length - 1], astUtils.isClosingParenToken)
|
||||
: sourceCode.getTokenAfter(firstToken);
|
||||
|
||||
return {
|
||||
leftParen: firstToken,
|
||||
rightParen
|
||||
};
|
||||
}
|
||||
|
||||
case "ImportExpression": {
|
||||
const leftParen = sourceCode.getFirstToken(node, 1);
|
||||
const rightParen = sourceCode.getLastToken(node);
|
||||
|
||||
return { leftParen, rightParen };
|
||||
}
|
||||
|
||||
default:
|
||||
throw new TypeError(`unexpected node with type ${node.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Public
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
[[
|
||||
"ArrowFunctionExpression",
|
||||
"CallExpression",
|
||||
"FunctionDeclaration",
|
||||
"FunctionExpression",
|
||||
"ImportExpression",
|
||||
"NewExpression"
|
||||
]](node) {
|
||||
const parens = getParenTokens(node);
|
||||
let params;
|
||||
|
||||
if (node.type === "ImportExpression") {
|
||||
params = [node.source];
|
||||
} else if (astUtils.isFunction(node)) {
|
||||
params = node.params;
|
||||
} else {
|
||||
params = node.arguments;
|
||||
}
|
||||
|
||||
if (parens) {
|
||||
validateParens(parens, params);
|
||||
|
||||
if (multilineArgumentsOption) {
|
||||
validateArguments(parens, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
209
qwen/nodejs/node_modules/eslint/lib/rules/generator-star-spacing.js
generated
vendored
Normal file
209
qwen/nodejs/node_modules/eslint/lib/rules/generator-star-spacing.js
generated
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
* @fileoverview Rule to check the spacing around the * in generator functions.
|
||||
* @author Jamund Ferguson
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const OVERRIDE_SCHEMA = {
|
||||
oneOf: [
|
||||
{
|
||||
enum: ["before", "after", "both", "neither"]
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
before: { type: "boolean" },
|
||||
after: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
replacedBy: [],
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description: "Enforce consistent spacing around `*` operators in generator functions",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/generator-star-spacing"
|
||||
},
|
||||
|
||||
fixable: "whitespace",
|
||||
|
||||
schema: [
|
||||
{
|
||||
oneOf: [
|
||||
{
|
||||
enum: ["before", "after", "both", "neither"]
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
before: { type: "boolean" },
|
||||
after: { type: "boolean" },
|
||||
named: OVERRIDE_SCHEMA,
|
||||
anonymous: OVERRIDE_SCHEMA,
|
||||
method: OVERRIDE_SCHEMA
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
messages: {
|
||||
missingBefore: "Missing space before *.",
|
||||
missingAfter: "Missing space after *.",
|
||||
unexpectedBefore: "Unexpected space before *.",
|
||||
unexpectedAfter: "Unexpected space after *."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
|
||||
const optionDefinitions = {
|
||||
before: { before: true, after: false },
|
||||
after: { before: false, after: true },
|
||||
both: { before: true, after: true },
|
||||
neither: { before: false, after: false }
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns resolved option definitions based on an option and defaults
|
||||
* @param {any} option The option object or string value
|
||||
* @param {Object} defaults The defaults to use if options are not present
|
||||
* @returns {Object} the resolved object definition
|
||||
*/
|
||||
function optionToDefinition(option, defaults) {
|
||||
if (!option) {
|
||||
return defaults;
|
||||
}
|
||||
|
||||
return typeof option === "string"
|
||||
? optionDefinitions[option]
|
||||
: Object.assign({}, defaults, option);
|
||||
}
|
||||
|
||||
const modes = (function(option) {
|
||||
const defaults = optionToDefinition(option, optionDefinitions.before);
|
||||
|
||||
return {
|
||||
named: optionToDefinition(option.named, defaults),
|
||||
anonymous: optionToDefinition(option.anonymous, defaults),
|
||||
method: optionToDefinition(option.method, defaults)
|
||||
};
|
||||
}(context.options[0] || {}));
|
||||
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Checks if the given token is a star token or not.
|
||||
* @param {Token} token The token to check.
|
||||
* @returns {boolean} `true` if the token is a star token.
|
||||
*/
|
||||
function isStarToken(token) {
|
||||
return token.value === "*" && token.type === "Punctuator";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the generator star token of the given function node.
|
||||
* @param {ASTNode} node The function node to get.
|
||||
* @returns {Token} Found star token.
|
||||
*/
|
||||
function getStarToken(node) {
|
||||
return sourceCode.getFirstToken(
|
||||
(node.parent.method || node.parent.type === "MethodDefinition") ? node.parent : node,
|
||||
isStarToken
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* capitalize a given string.
|
||||
* @param {string} str the given string.
|
||||
* @returns {string} the capitalized string.
|
||||
*/
|
||||
function capitalize(str) {
|
||||
return str[0].toUpperCase() + str.slice(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the spacing between two tokens before or after the star token.
|
||||
* @param {string} kind Either "named", "anonymous", or "method"
|
||||
* @param {string} side Either "before" or "after".
|
||||
* @param {Token} leftToken `function` keyword token if side is "before", or
|
||||
* star token if side is "after".
|
||||
* @param {Token} rightToken Star token if side is "before", or identifier
|
||||
* token if side is "after".
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkSpacing(kind, side, leftToken, rightToken) {
|
||||
if (!!(rightToken.range[0] - leftToken.range[1]) !== modes[kind][side]) {
|
||||
const after = leftToken.value === "*";
|
||||
const spaceRequired = modes[kind][side];
|
||||
const node = after ? leftToken : rightToken;
|
||||
const messageId = `${spaceRequired ? "missing" : "unexpected"}${capitalize(side)}`;
|
||||
|
||||
context.report({
|
||||
node,
|
||||
messageId,
|
||||
fix(fixer) {
|
||||
if (spaceRequired) {
|
||||
if (after) {
|
||||
return fixer.insertTextAfter(node, " ");
|
||||
}
|
||||
return fixer.insertTextBefore(node, " ");
|
||||
}
|
||||
return fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforces the spacing around the star if node is a generator function.
|
||||
* @param {ASTNode} node A function expression or declaration node.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkFunction(node) {
|
||||
if (!node.generator) {
|
||||
return;
|
||||
}
|
||||
|
||||
const starToken = getStarToken(node);
|
||||
const prevToken = sourceCode.getTokenBefore(starToken);
|
||||
const nextToken = sourceCode.getTokenAfter(starToken);
|
||||
|
||||
let kind = "named";
|
||||
|
||||
if (node.parent.type === "MethodDefinition" || (node.parent.type === "Property" && node.parent.method)) {
|
||||
kind = "method";
|
||||
} else if (!node.id) {
|
||||
kind = "anonymous";
|
||||
}
|
||||
|
||||
// Only check before when preceded by `function`|`static` keyword
|
||||
if (!(kind === "method" && starToken === sourceCode.getFirstToken(node.parent))) {
|
||||
checkSpacing(kind, "before", prevToken, starToken);
|
||||
}
|
||||
|
||||
checkSpacing(kind, "after", starToken, nextToken);
|
||||
}
|
||||
|
||||
return {
|
||||
FunctionDeclaration: checkFunction,
|
||||
FunctionExpression: checkFunction
|
||||
};
|
||||
|
||||
}
|
||||
};
|
||||
204
qwen/nodejs/node_modules/eslint/lib/rules/getter-return.js
generated
vendored
Normal file
204
qwen/nodejs/node_modules/eslint/lib/rules/getter-return.js
generated
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
/**
|
||||
* @fileoverview Enforces that a return statement is present in property getters.
|
||||
* @author Aladdin-ADD(hh_2013@foxmail.com)
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const TARGET_NODE_TYPE = /^(?:Arrow)?FunctionExpression$/u;
|
||||
|
||||
/**
|
||||
* Checks all segments in a set and returns true if any are reachable.
|
||||
* @param {Set<CodePathSegment>} segments The segments to check.
|
||||
* @returns {boolean} True if any segment is reachable; false otherwise.
|
||||
*/
|
||||
function isAnySegmentReachable(segments) {
|
||||
|
||||
for (const segment of segments) {
|
||||
if (segment.reachable) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "problem",
|
||||
|
||||
docs: {
|
||||
description: "Enforce `return` statements in getters",
|
||||
recommended: true,
|
||||
url: "https://eslint.org/docs/latest/rules/getter-return"
|
||||
},
|
||||
|
||||
fixable: null,
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
allowImplicit: {
|
||||
type: "boolean",
|
||||
default: false
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
],
|
||||
|
||||
messages: {
|
||||
expected: "Expected to return a value in {{name}}.",
|
||||
expectedAlways: "Expected {{name}} to always return a value."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
|
||||
const options = context.options[0] || { allowImplicit: false };
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
let funcInfo = {
|
||||
upper: null,
|
||||
codePath: null,
|
||||
hasReturn: false,
|
||||
shouldCheck: false,
|
||||
node: null,
|
||||
currentSegments: []
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether or not the last code path segment is reachable.
|
||||
* Then reports this function if the segment is reachable.
|
||||
*
|
||||
* If the last code path segment is reachable, there are paths which are not
|
||||
* returned or thrown.
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkLastSegment(node) {
|
||||
if (funcInfo.shouldCheck &&
|
||||
isAnySegmentReachable(funcInfo.currentSegments)
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
loc: astUtils.getFunctionHeadLoc(node, sourceCode),
|
||||
messageId: funcInfo.hasReturn ? "expectedAlways" : "expected",
|
||||
data: {
|
||||
name: astUtils.getFunctionNameWithKind(funcInfo.node)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a node means a getter function.
|
||||
* @param {ASTNode} node a node to check.
|
||||
* @returns {boolean} if node means a getter, return true; else return false.
|
||||
*/
|
||||
function isGetter(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
if (TARGET_NODE_TYPE.test(node.type) && node.body.type === "BlockStatement") {
|
||||
if (parent.kind === "get") {
|
||||
return true;
|
||||
}
|
||||
if (parent.type === "Property" && astUtils.getStaticPropertyName(parent) === "get" && parent.parent.type === "ObjectExpression") {
|
||||
|
||||
// Object.defineProperty() or Reflect.defineProperty()
|
||||
if (parent.parent.parent.type === "CallExpression") {
|
||||
const callNode = parent.parent.parent.callee;
|
||||
|
||||
if (astUtils.isSpecificMemberAccess(callNode, "Object", "defineProperty") ||
|
||||
astUtils.isSpecificMemberAccess(callNode, "Reflect", "defineProperty")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Object.defineProperties() or Object.create()
|
||||
if (parent.parent.parent.type === "Property" &&
|
||||
parent.parent.parent.parent.type === "ObjectExpression" &&
|
||||
parent.parent.parent.parent.parent.type === "CallExpression") {
|
||||
const callNode = parent.parent.parent.parent.parent.callee;
|
||||
|
||||
return astUtils.isSpecificMemberAccess(callNode, "Object", "defineProperties") ||
|
||||
astUtils.isSpecificMemberAccess(callNode, "Object", "create");
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return {
|
||||
|
||||
// Stacks this function's information.
|
||||
onCodePathStart(codePath, node) {
|
||||
funcInfo = {
|
||||
upper: funcInfo,
|
||||
codePath,
|
||||
hasReturn: false,
|
||||
shouldCheck: isGetter(node),
|
||||
node,
|
||||
currentSegments: new Set()
|
||||
};
|
||||
},
|
||||
|
||||
// Pops this function's information.
|
||||
onCodePathEnd() {
|
||||
funcInfo = funcInfo.upper;
|
||||
},
|
||||
onUnreachableCodePathSegmentStart(segment) {
|
||||
funcInfo.currentSegments.add(segment);
|
||||
},
|
||||
|
||||
onUnreachableCodePathSegmentEnd(segment) {
|
||||
funcInfo.currentSegments.delete(segment);
|
||||
},
|
||||
|
||||
onCodePathSegmentStart(segment) {
|
||||
funcInfo.currentSegments.add(segment);
|
||||
},
|
||||
|
||||
onCodePathSegmentEnd(segment) {
|
||||
funcInfo.currentSegments.delete(segment);
|
||||
},
|
||||
|
||||
// Checks the return statement is valid.
|
||||
ReturnStatement(node) {
|
||||
if (funcInfo.shouldCheck) {
|
||||
funcInfo.hasReturn = true;
|
||||
|
||||
// if allowImplicit: false, should also check node.argument
|
||||
if (!options.allowImplicit && !node.argument) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "expected",
|
||||
data: {
|
||||
name: astUtils.getFunctionNameWithKind(funcInfo.node)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Reports a given function if the last path is reachable.
|
||||
"FunctionExpression:exit": checkLastSegment,
|
||||
"ArrowFunctionExpression:exit": checkLastSegment
|
||||
};
|
||||
}
|
||||
};
|
||||
90
qwen/nodejs/node_modules/eslint/lib/rules/global-require.js
generated
vendored
Normal file
90
qwen/nodejs/node_modules/eslint/lib/rules/global-require.js
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* @fileoverview Rule for disallowing require() outside of the top-level module context
|
||||
* @author Jamund Ferguson
|
||||
* @deprecated in ESLint v7.0.0
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const ACCEPTABLE_PARENTS = new Set([
|
||||
"AssignmentExpression",
|
||||
"VariableDeclarator",
|
||||
"MemberExpression",
|
||||
"ExpressionStatement",
|
||||
"CallExpression",
|
||||
"ConditionalExpression",
|
||||
"Program",
|
||||
"VariableDeclaration",
|
||||
"ChainExpression"
|
||||
]);
|
||||
|
||||
/**
|
||||
* Finds the eslint-scope reference in the given scope.
|
||||
* @param {Object} scope The scope to search.
|
||||
* @param {ASTNode} node The identifier node.
|
||||
* @returns {Reference|null} Returns the found reference or null if none were found.
|
||||
*/
|
||||
function findReference(scope, node) {
|
||||
const references = scope.references.filter(reference => reference.identifier.range[0] === node.range[0] &&
|
||||
reference.identifier.range[1] === node.range[1]);
|
||||
|
||||
if (references.length === 1) {
|
||||
return references[0];
|
||||
}
|
||||
|
||||
/* c8 ignore next */
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given identifier node is shadowed in the given scope.
|
||||
* @param {Object} scope The current scope.
|
||||
* @param {ASTNode} node The identifier node to check.
|
||||
* @returns {boolean} Whether or not the name is shadowed.
|
||||
*/
|
||||
function isShadowed(scope, node) {
|
||||
const reference = findReference(scope, node);
|
||||
|
||||
return reference && reference.resolved && reference.resolved.defs.length > 0;
|
||||
}
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
|
||||
replacedBy: [],
|
||||
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Require `require()` calls to be placed at top-level module scope",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/global-require"
|
||||
},
|
||||
|
||||
schema: [],
|
||||
messages: {
|
||||
unexpected: "Unexpected require()."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
return {
|
||||
CallExpression(node) {
|
||||
const currentScope = sourceCode.getScope(node);
|
||||
|
||||
if (node.callee.name === "require" && !isShadowed(currentScope, node.callee)) {
|
||||
const isGoodRequire = sourceCode.getAncestors(node).every(parent => ACCEPTABLE_PARENTS.has(parent.type));
|
||||
|
||||
if (!isGoodRequire) {
|
||||
context.report({ node, messageId: "unexpected" });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
215
qwen/nodejs/node_modules/eslint/lib/rules/grouped-accessor-pairs.js
generated
vendored
Normal file
215
qwen/nodejs/node_modules/eslint/lib/rules/grouped-accessor-pairs.js
generated
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
/**
|
||||
* @fileoverview Rule to require grouped accessor pairs in object literals and classes
|
||||
* @author Milos Djermanovic
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Typedefs
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Property name if it can be computed statically, otherwise the list of the tokens of the key node.
|
||||
* @typedef {string|Token[]} Key
|
||||
*/
|
||||
|
||||
/**
|
||||
* Accessor nodes with the same key.
|
||||
* @typedef {Object} AccessorData
|
||||
* @property {Key} key Accessor's key
|
||||
* @property {ASTNode[]} getters List of getter nodes.
|
||||
* @property {ASTNode[]} setters List of setter nodes.
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks whether or not the given lists represent the equal tokens in the same order.
|
||||
* Tokens are compared by their properties, not by instance.
|
||||
* @param {Token[]} left First list of tokens.
|
||||
* @param {Token[]} right Second list of tokens.
|
||||
* @returns {boolean} `true` if the lists have same tokens.
|
||||
*/
|
||||
function areEqualTokenLists(left, right) {
|
||||
if (left.length !== right.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < left.length; i++) {
|
||||
const leftToken = left[i],
|
||||
rightToken = right[i];
|
||||
|
||||
if (leftToken.type !== rightToken.type || leftToken.value !== rightToken.value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not the given keys are equal.
|
||||
* @param {Key} left First key.
|
||||
* @param {Key} right Second key.
|
||||
* @returns {boolean} `true` if the keys are equal.
|
||||
*/
|
||||
function areEqualKeys(left, right) {
|
||||
if (typeof left === "string" && typeof right === "string") {
|
||||
|
||||
// Statically computed names.
|
||||
return left === right;
|
||||
}
|
||||
if (Array.isArray(left) && Array.isArray(right)) {
|
||||
|
||||
// Token lists.
|
||||
return areEqualTokenLists(left, right);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a given node is of an accessor kind ('get' or 'set').
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @returns {boolean} `true` if the node is of an accessor kind.
|
||||
*/
|
||||
function isAccessorKind(node) {
|
||||
return node.kind === "get" || node.kind === "set";
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Require grouped accessor pairs in object literals and classes",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/grouped-accessor-pairs"
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
enum: ["anyOrder", "getBeforeSet", "setBeforeGet"]
|
||||
}
|
||||
],
|
||||
|
||||
messages: {
|
||||
notGrouped: "Accessor pair {{ formerName }} and {{ latterName }} should be grouped.",
|
||||
invalidOrder: "Expected {{ latterName }} to be before {{ formerName }}."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const order = context.options[0] || "anyOrder";
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Reports the given accessor pair.
|
||||
* @param {string} messageId messageId to report.
|
||||
* @param {ASTNode} formerNode getter/setter node that is defined before `latterNode`.
|
||||
* @param {ASTNode} latterNode getter/setter node that is defined after `formerNode`.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function report(messageId, formerNode, latterNode) {
|
||||
context.report({
|
||||
node: latterNode,
|
||||
messageId,
|
||||
loc: astUtils.getFunctionHeadLoc(latterNode.value, sourceCode),
|
||||
data: {
|
||||
formerName: astUtils.getFunctionNameWithKind(formerNode.value),
|
||||
latterName: astUtils.getFunctionNameWithKind(latterNode.value)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks accessor pairs in the given list of nodes.
|
||||
* @param {ASTNode[]} nodes The list to check.
|
||||
* @param {Function} shouldCheck – Predicate that returns `true` if the node should be checked.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function checkList(nodes, shouldCheck) {
|
||||
const accessors = [];
|
||||
let found = false;
|
||||
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const node = nodes[i];
|
||||
|
||||
if (shouldCheck(node) && isAccessorKind(node)) {
|
||||
|
||||
// Creates a new `AccessorData` object for the given getter or setter node.
|
||||
const name = astUtils.getStaticPropertyName(node);
|
||||
const key = (name !== null) ? name : sourceCode.getTokens(node.key);
|
||||
|
||||
// Merges the given `AccessorData` object into the given accessors list.
|
||||
for (let j = 0; j < accessors.length; j++) {
|
||||
const accessor = accessors[j];
|
||||
|
||||
if (areEqualKeys(accessor.key, key)) {
|
||||
accessor.getters.push(...node.kind === "get" ? [node] : []);
|
||||
accessor.setters.push(...node.kind === "set" ? [node] : []);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
accessors.push({
|
||||
key,
|
||||
getters: node.kind === "get" ? [node] : [],
|
||||
setters: node.kind === "set" ? [node] : []
|
||||
});
|
||||
}
|
||||
found = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const { getters, setters } of accessors) {
|
||||
|
||||
// Don't report accessor properties that have duplicate getters or setters.
|
||||
if (getters.length === 1 && setters.length === 1) {
|
||||
const [getter] = getters,
|
||||
[setter] = setters,
|
||||
getterIndex = nodes.indexOf(getter),
|
||||
setterIndex = nodes.indexOf(setter),
|
||||
formerNode = getterIndex < setterIndex ? getter : setter,
|
||||
latterNode = getterIndex < setterIndex ? setter : getter;
|
||||
|
||||
if (Math.abs(getterIndex - setterIndex) > 1) {
|
||||
report("notGrouped", formerNode, latterNode);
|
||||
} else if (
|
||||
(order === "getBeforeSet" && getterIndex > setterIndex) ||
|
||||
(order === "setBeforeGet" && getterIndex < setterIndex)
|
||||
) {
|
||||
report("invalidOrder", formerNode, latterNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
ObjectExpression(node) {
|
||||
checkList(node.properties, n => n.type === "Property");
|
||||
},
|
||||
ClassBody(node) {
|
||||
checkList(node.body, n => n.type === "MethodDefinition" && !n.static);
|
||||
checkList(node.body, n => n.type === "MethodDefinition" && n.static);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
76
qwen/nodejs/node_modules/eslint/lib/rules/guard-for-in.js
generated
vendored
Normal file
76
qwen/nodejs/node_modules/eslint/lib/rules/guard-for-in.js
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* @fileoverview Rule to flag for-in loops without if statements inside
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Require `for-in` loops to include an `if` statement",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/guard-for-in"
|
||||
},
|
||||
|
||||
schema: [],
|
||||
messages: {
|
||||
wrap: "The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
|
||||
return {
|
||||
|
||||
ForInStatement(node) {
|
||||
const body = node.body;
|
||||
|
||||
// empty statement
|
||||
if (body.type === "EmptyStatement") {
|
||||
return;
|
||||
}
|
||||
|
||||
// if statement
|
||||
if (body.type === "IfStatement") {
|
||||
return;
|
||||
}
|
||||
|
||||
// empty block
|
||||
if (body.type === "BlockStatement" && body.body.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// block with just if statement
|
||||
if (body.type === "BlockStatement" && body.body.length === 1 && body.body[0].type === "IfStatement") {
|
||||
return;
|
||||
}
|
||||
|
||||
// block that starts with if statement
|
||||
if (body.type === "BlockStatement" && body.body.length >= 1 && body.body[0].type === "IfStatement") {
|
||||
const i = body.body[0];
|
||||
|
||||
// ... whose consequent is a continue
|
||||
if (i.consequent.type === "ContinueStatement") {
|
||||
return;
|
||||
}
|
||||
|
||||
// ... whose consequent is a block that contains only a continue
|
||||
if (i.consequent.type === "BlockStatement" && i.consequent.body.length === 1 && i.consequent.body[0].type === "ContinueStatement") {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
context.report({ node, messageId: "wrap" });
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
};
|
||||
101
qwen/nodejs/node_modules/eslint/lib/rules/handle-callback-err.js
generated
vendored
Normal file
101
qwen/nodejs/node_modules/eslint/lib/rules/handle-callback-err.js
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* @fileoverview Ensure handling of errors when we know they exist.
|
||||
* @author Jamund Ferguson
|
||||
* @deprecated in ESLint v7.0.0
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
|
||||
replacedBy: [],
|
||||
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Require error handling in callbacks",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/handle-callback-err"
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: "string"
|
||||
}
|
||||
],
|
||||
messages: {
|
||||
expected: "Expected error to be handled."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
|
||||
const errorArgument = context.options[0] || "err";
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Checks if the given argument should be interpreted as a regexp pattern.
|
||||
* @param {string} stringToCheck The string which should be checked.
|
||||
* @returns {boolean} Whether or not the string should be interpreted as a pattern.
|
||||
*/
|
||||
function isPattern(stringToCheck) {
|
||||
const firstChar = stringToCheck[0];
|
||||
|
||||
return firstChar === "^";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given name matches the configured error argument.
|
||||
* @param {string} name The name which should be compared.
|
||||
* @returns {boolean} Whether or not the given name matches the configured error variable name.
|
||||
*/
|
||||
function matchesConfiguredErrorName(name) {
|
||||
if (isPattern(errorArgument)) {
|
||||
const regexp = new RegExp(errorArgument, "u");
|
||||
|
||||
return regexp.test(name);
|
||||
}
|
||||
return name === errorArgument;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parameters of a given function scope.
|
||||
* @param {Object} scope The function scope.
|
||||
* @returns {Array} All parameters of the given scope.
|
||||
*/
|
||||
function getParameters(scope) {
|
||||
return scope.variables.filter(variable => variable.defs[0] && variable.defs[0].type === "Parameter");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if we're handling the error object properly.
|
||||
* @param {ASTNode} node The AST node to check.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkForError(node) {
|
||||
const scope = sourceCode.getScope(node),
|
||||
parameters = getParameters(scope),
|
||||
firstParameter = parameters[0];
|
||||
|
||||
if (firstParameter && matchesConfiguredErrorName(firstParameter.name)) {
|
||||
if (firstParameter.references.length === 0) {
|
||||
context.report({ node, messageId: "expected" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
FunctionDeclaration: checkForError,
|
||||
FunctionExpression: checkForError,
|
||||
ArrowFunctionExpression: checkForError
|
||||
};
|
||||
|
||||
}
|
||||
};
|
||||
246
qwen/nodejs/node_modules/eslint/lib/rules/id-blacklist.js
generated
vendored
Normal file
246
qwen/nodejs/node_modules/eslint/lib/rules/id-blacklist.js
generated
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
/**
|
||||
* @fileoverview Rule that warns when identifier names that are
|
||||
* specified in the configuration are used.
|
||||
* @author Keith Cirkel (http://keithcirkel.co.uk)
|
||||
* @deprecated in ESLint v7.5.0
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks whether the given node represents assignment target in a normal assignment or destructuring.
|
||||
* @param {ASTNode} node The node to check.
|
||||
* @returns {boolean} `true` if the node is assignment target.
|
||||
*/
|
||||
function isAssignmentTarget(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
return (
|
||||
|
||||
// normal assignment
|
||||
(
|
||||
parent.type === "AssignmentExpression" &&
|
||||
parent.left === node
|
||||
) ||
|
||||
|
||||
// destructuring
|
||||
parent.type === "ArrayPattern" ||
|
||||
parent.type === "RestElement" ||
|
||||
(
|
||||
parent.type === "Property" &&
|
||||
parent.value === node &&
|
||||
parent.parent.type === "ObjectPattern"
|
||||
) ||
|
||||
(
|
||||
parent.type === "AssignmentPattern" &&
|
||||
parent.left === node
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given node represents an imported name that is renamed in the same import/export specifier.
|
||||
*
|
||||
* Examples:
|
||||
* import { a as b } from 'mod'; // node `a` is renamed import
|
||||
* export { a as b } from 'mod'; // node `a` is renamed import
|
||||
* @param {ASTNode} node `Identifier` node to check.
|
||||
* @returns {boolean} `true` if the node is a renamed import.
|
||||
*/
|
||||
function isRenamedImport(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
return (
|
||||
(
|
||||
parent.type === "ImportSpecifier" &&
|
||||
parent.imported !== parent.local &&
|
||||
parent.imported === node
|
||||
) ||
|
||||
(
|
||||
parent.type === "ExportSpecifier" &&
|
||||
parent.parent.source && // re-export
|
||||
parent.local !== parent.exported &&
|
||||
parent.local === node
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given node is a renamed identifier node in an ObjectPattern destructuring.
|
||||
*
|
||||
* Examples:
|
||||
* const { a : b } = foo; // node `a` is renamed node.
|
||||
* @param {ASTNode} node `Identifier` node to check.
|
||||
* @returns {boolean} `true` if the node is a renamed node in an ObjectPattern destructuring.
|
||||
*/
|
||||
function isRenamedInDestructuring(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
return (
|
||||
(
|
||||
!parent.computed &&
|
||||
parent.type === "Property" &&
|
||||
parent.parent.type === "ObjectPattern" &&
|
||||
parent.value !== node &&
|
||||
parent.key === node
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given node represents shorthand definition of a property in an object literal.
|
||||
* @param {ASTNode} node `Identifier` node to check.
|
||||
* @returns {boolean} `true` if the node is a shorthand property definition.
|
||||
*/
|
||||
function isShorthandPropertyDefinition(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
return (
|
||||
parent.type === "Property" &&
|
||||
parent.parent.type === "ObjectExpression" &&
|
||||
parent.shorthand
|
||||
);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: true,
|
||||
replacedBy: ["id-denylist"],
|
||||
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Disallow specified identifiers",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/id-blacklist"
|
||||
},
|
||||
|
||||
schema: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string"
|
||||
},
|
||||
uniqueItems: true
|
||||
},
|
||||
messages: {
|
||||
restricted: "Identifier '{{name}}' is restricted."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
|
||||
const denyList = new Set(context.options);
|
||||
const reportedNodes = new Set();
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
let globalScope;
|
||||
|
||||
/**
|
||||
* Checks whether the given name is restricted.
|
||||
* @param {string} name The name to check.
|
||||
* @returns {boolean} `true` if the name is restricted.
|
||||
* @private
|
||||
*/
|
||||
function isRestricted(name) {
|
||||
return denyList.has(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given node represents a reference to a global variable that is not declared in the source code.
|
||||
* These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
|
||||
* @param {ASTNode} node `Identifier` node to check.
|
||||
* @returns {boolean} `true` if the node is a reference to a global variable.
|
||||
*/
|
||||
function isReferenceToGlobalVariable(node) {
|
||||
const variable = globalScope.set.get(node.name);
|
||||
|
||||
return variable && variable.defs.length === 0 &&
|
||||
variable.references.some(ref => ref.identifier === node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given node should be checked.
|
||||
* @param {ASTNode} node `Identifier` node.
|
||||
* @returns {boolean} `true` if the node should be checked.
|
||||
*/
|
||||
function shouldCheck(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
/*
|
||||
* Member access has special rules for checking property names.
|
||||
* Read access to a property with a restricted name is allowed, because it can be on an object that user has no control over.
|
||||
* Write access isn't allowed, because it potentially creates a new property with a restricted name.
|
||||
*/
|
||||
if (
|
||||
parent.type === "MemberExpression" &&
|
||||
parent.property === node &&
|
||||
!parent.computed
|
||||
) {
|
||||
return isAssignmentTarget(parent);
|
||||
}
|
||||
|
||||
return (
|
||||
parent.type !== "CallExpression" &&
|
||||
parent.type !== "NewExpression" &&
|
||||
!isRenamedImport(node) &&
|
||||
!isRenamedInDestructuring(node) &&
|
||||
!(
|
||||
isReferenceToGlobalVariable(node) &&
|
||||
!isShorthandPropertyDefinition(node)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports an AST node as a rule violation.
|
||||
* @param {ASTNode} node The node to report.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function report(node) {
|
||||
|
||||
/*
|
||||
* We used the range instead of the node because it's possible
|
||||
* for the same identifier to be represented by two different
|
||||
* nodes, with the most clear example being shorthand properties:
|
||||
* { foo }
|
||||
* In this case, "foo" is represented by one node for the name
|
||||
* and one for the value. The only way to know they are the same
|
||||
* is to look at the range.
|
||||
*/
|
||||
if (!reportedNodes.has(node.range.toString())) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "restricted",
|
||||
data: {
|
||||
name: node.name
|
||||
}
|
||||
});
|
||||
reportedNodes.add(node.range.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Program(node) {
|
||||
globalScope = sourceCode.getScope(node);
|
||||
},
|
||||
|
||||
Identifier(node) {
|
||||
if (isRestricted(node.name) && shouldCheck(node)) {
|
||||
report(node);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
228
qwen/nodejs/node_modules/eslint/lib/rules/id-denylist.js
generated
vendored
Normal file
228
qwen/nodejs/node_modules/eslint/lib/rules/id-denylist.js
generated
vendored
Normal file
@@ -0,0 +1,228 @@
|
||||
/**
|
||||
* @fileoverview Rule that warns when identifier names that are
|
||||
* specified in the configuration are used.
|
||||
* @author Keith Cirkel (http://keithcirkel.co.uk)
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Checks whether the given node represents assignment target in a normal assignment or destructuring.
|
||||
* @param {ASTNode} node The node to check.
|
||||
* @returns {boolean} `true` if the node is assignment target.
|
||||
*/
|
||||
function isAssignmentTarget(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
return (
|
||||
|
||||
// normal assignment
|
||||
(
|
||||
parent.type === "AssignmentExpression" &&
|
||||
parent.left === node
|
||||
) ||
|
||||
|
||||
// destructuring
|
||||
parent.type === "ArrayPattern" ||
|
||||
parent.type === "RestElement" ||
|
||||
(
|
||||
parent.type === "Property" &&
|
||||
parent.value === node &&
|
||||
parent.parent.type === "ObjectPattern"
|
||||
) ||
|
||||
(
|
||||
parent.type === "AssignmentPattern" &&
|
||||
parent.left === node
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given node represents an imported name that is renamed in the same import/export specifier.
|
||||
*
|
||||
* Examples:
|
||||
* import { a as b } from 'mod'; // node `a` is renamed import
|
||||
* export { a as b } from 'mod'; // node `a` is renamed import
|
||||
* @param {ASTNode} node `Identifier` node to check.
|
||||
* @returns {boolean} `true` if the node is a renamed import.
|
||||
*/
|
||||
function isRenamedImport(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
return (
|
||||
(
|
||||
parent.type === "ImportSpecifier" &&
|
||||
parent.imported !== parent.local &&
|
||||
parent.imported === node
|
||||
) ||
|
||||
(
|
||||
parent.type === "ExportSpecifier" &&
|
||||
parent.parent.source && // re-export
|
||||
parent.local !== parent.exported &&
|
||||
parent.local === node
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given node is an ObjectPattern destructuring.
|
||||
*
|
||||
* Examples:
|
||||
* const { a : b } = foo;
|
||||
* @param {ASTNode} node `Identifier` node to check.
|
||||
* @returns {boolean} `true` if the node is in an ObjectPattern destructuring.
|
||||
*/
|
||||
function isPropertyNameInDestructuring(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
return (
|
||||
(
|
||||
!parent.computed &&
|
||||
parent.type === "Property" &&
|
||||
parent.parent.type === "ObjectPattern" &&
|
||||
parent.key === node
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Disallow specified identifiers",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/id-denylist"
|
||||
},
|
||||
|
||||
schema: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string"
|
||||
},
|
||||
uniqueItems: true
|
||||
},
|
||||
messages: {
|
||||
restricted: "Identifier '{{name}}' is restricted.",
|
||||
restrictedPrivate: "Identifier '#{{name}}' is restricted."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
|
||||
const denyList = new Set(context.options);
|
||||
const reportedNodes = new Set();
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
let globalScope;
|
||||
|
||||
/**
|
||||
* Checks whether the given name is restricted.
|
||||
* @param {string} name The name to check.
|
||||
* @returns {boolean} `true` if the name is restricted.
|
||||
* @private
|
||||
*/
|
||||
function isRestricted(name) {
|
||||
return denyList.has(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given node represents a reference to a global variable that is not declared in the source code.
|
||||
* These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
|
||||
* @param {ASTNode} node `Identifier` node to check.
|
||||
* @returns {boolean} `true` if the node is a reference to a global variable.
|
||||
*/
|
||||
function isReferenceToGlobalVariable(node) {
|
||||
const variable = globalScope.set.get(node.name);
|
||||
|
||||
return variable && variable.defs.length === 0 &&
|
||||
variable.references.some(ref => ref.identifier === node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the given node should be checked.
|
||||
* @param {ASTNode} node `Identifier` node.
|
||||
* @returns {boolean} `true` if the node should be checked.
|
||||
*/
|
||||
function shouldCheck(node) {
|
||||
const parent = node.parent;
|
||||
|
||||
/*
|
||||
* Member access has special rules for checking property names.
|
||||
* Read access to a property with a restricted name is allowed, because it can be on an object that user has no control over.
|
||||
* Write access isn't allowed, because it potentially creates a new property with a restricted name.
|
||||
*/
|
||||
if (
|
||||
parent.type === "MemberExpression" &&
|
||||
parent.property === node &&
|
||||
!parent.computed
|
||||
) {
|
||||
return isAssignmentTarget(parent);
|
||||
}
|
||||
|
||||
return (
|
||||
parent.type !== "CallExpression" &&
|
||||
parent.type !== "NewExpression" &&
|
||||
!isRenamedImport(node) &&
|
||||
!isPropertyNameInDestructuring(node) &&
|
||||
!isReferenceToGlobalVariable(node)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports an AST node as a rule violation.
|
||||
* @param {ASTNode} node The node to report.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function report(node) {
|
||||
|
||||
/*
|
||||
* We used the range instead of the node because it's possible
|
||||
* for the same identifier to be represented by two different
|
||||
* nodes, with the most clear example being shorthand properties:
|
||||
* { foo }
|
||||
* In this case, "foo" is represented by one node for the name
|
||||
* and one for the value. The only way to know they are the same
|
||||
* is to look at the range.
|
||||
*/
|
||||
if (!reportedNodes.has(node.range.toString())) {
|
||||
const isPrivate = node.type === "PrivateIdentifier";
|
||||
|
||||
context.report({
|
||||
node,
|
||||
messageId: isPrivate ? "restrictedPrivate" : "restricted",
|
||||
data: {
|
||||
name: node.name
|
||||
}
|
||||
});
|
||||
reportedNodes.add(node.range.toString());
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Program(node) {
|
||||
globalScope = sourceCode.getScope(node);
|
||||
},
|
||||
|
||||
[[
|
||||
"Identifier",
|
||||
"PrivateIdentifier"
|
||||
]](node) {
|
||||
if (isRestricted(node.name) && shouldCheck(node)) {
|
||||
report(node);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
177
qwen/nodejs/node_modules/eslint/lib/rules/id-length.js
generated
vendored
Normal file
177
qwen/nodejs/node_modules/eslint/lib/rules/id-length.js
generated
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* @fileoverview Rule that warns when identifier names are shorter or longer
|
||||
* than the values provided in configuration.
|
||||
* @author Burak Yigit Kaya aka BYK
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const { getGraphemeCount } = require("../shared/string-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../shared/types').Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Enforce minimum and maximum identifier lengths",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/id-length"
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
min: {
|
||||
type: "integer",
|
||||
default: 2
|
||||
},
|
||||
max: {
|
||||
type: "integer"
|
||||
},
|
||||
exceptions: {
|
||||
type: "array",
|
||||
uniqueItems: true,
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
exceptionPatterns: {
|
||||
type: "array",
|
||||
uniqueItems: true,
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
properties: {
|
||||
enum: ["always", "never"]
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
],
|
||||
messages: {
|
||||
tooShort: "Identifier name '{{name}}' is too short (< {{min}}).",
|
||||
tooShortPrivate: "Identifier name '#{{name}}' is too short (< {{min}}).",
|
||||
tooLong: "Identifier name '{{name}}' is too long (> {{max}}).",
|
||||
tooLongPrivate: "Identifier name #'{{name}}' is too long (> {{max}})."
|
||||
}
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const options = context.options[0] || {};
|
||||
const minLength = typeof options.min !== "undefined" ? options.min : 2;
|
||||
const maxLength = typeof options.max !== "undefined" ? options.max : Infinity;
|
||||
const properties = options.properties !== "never";
|
||||
const exceptions = new Set(options.exceptions);
|
||||
const exceptionPatterns = (options.exceptionPatterns || []).map(pattern => new RegExp(pattern, "u"));
|
||||
const reportedNodes = new Set();
|
||||
|
||||
/**
|
||||
* Checks if a string matches the provided exception patterns
|
||||
* @param {string} name The string to check.
|
||||
* @returns {boolean} if the string is a match
|
||||
* @private
|
||||
*/
|
||||
function matchesExceptionPattern(name) {
|
||||
return exceptionPatterns.some(pattern => pattern.test(name));
|
||||
}
|
||||
|
||||
const SUPPORTED_EXPRESSIONS = {
|
||||
MemberExpression: properties && function(parent) {
|
||||
return !parent.computed && (
|
||||
|
||||
// regular property assignment
|
||||
(parent.parent.left === parent && parent.parent.type === "AssignmentExpression" ||
|
||||
|
||||
// or the last identifier in an ObjectPattern destructuring
|
||||
parent.parent.type === "Property" && parent.parent.value === parent &&
|
||||
parent.parent.parent.type === "ObjectPattern" && parent.parent.parent.parent.left === parent.parent.parent)
|
||||
);
|
||||
},
|
||||
AssignmentPattern(parent, node) {
|
||||
return parent.left === node;
|
||||
},
|
||||
VariableDeclarator(parent, node) {
|
||||
return parent.id === node;
|
||||
},
|
||||
Property(parent, node) {
|
||||
|
||||
if (parent.parent.type === "ObjectPattern") {
|
||||
const isKeyAndValueSame = parent.value.name === parent.key.name;
|
||||
|
||||
return (
|
||||
!isKeyAndValueSame && parent.value === node ||
|
||||
isKeyAndValueSame && parent.key === node && properties
|
||||
);
|
||||
}
|
||||
return properties && !parent.computed && parent.key.name === node.name;
|
||||
},
|
||||
ImportDefaultSpecifier: true,
|
||||
RestElement: true,
|
||||
FunctionExpression: true,
|
||||
ArrowFunctionExpression: true,
|
||||
ClassDeclaration: true,
|
||||
FunctionDeclaration: true,
|
||||
MethodDefinition: true,
|
||||
PropertyDefinition: true,
|
||||
CatchClause: true,
|
||||
ArrayPattern: true
|
||||
};
|
||||
|
||||
return {
|
||||
[[
|
||||
"Identifier",
|
||||
"PrivateIdentifier"
|
||||
]](node) {
|
||||
const name = node.name;
|
||||
const parent = node.parent;
|
||||
|
||||
const nameLength = getGraphemeCount(name);
|
||||
|
||||
const isShort = nameLength < minLength;
|
||||
const isLong = nameLength > maxLength;
|
||||
|
||||
if (!(isShort || isLong) || exceptions.has(name) || matchesExceptionPattern(name)) {
|
||||
return; // Nothing to report
|
||||
}
|
||||
|
||||
const isValidExpression = SUPPORTED_EXPRESSIONS[parent.type];
|
||||
|
||||
/*
|
||||
* We used the range instead of the node because it's possible
|
||||
* for the same identifier to be represented by two different
|
||||
* nodes, with the most clear example being shorthand properties:
|
||||
* { foo }
|
||||
* In this case, "foo" is represented by one node for the name
|
||||
* and one for the value. The only way to know they are the same
|
||||
* is to look at the range.
|
||||
*/
|
||||
if (isValidExpression && !reportedNodes.has(node.range.toString()) && (isValidExpression === true || isValidExpression(parent, node))) {
|
||||
reportedNodes.add(node.range.toString());
|
||||
|
||||
let messageId = isShort ? "tooShort" : "tooLong";
|
||||
|
||||
if (node.type === "PrivateIdentifier") {
|
||||
messageId += "Private";
|
||||
}
|
||||
|
||||
context.report({
|
||||
node,
|
||||
messageId,
|
||||
data: { name, min: minLength, max: maxLength }
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user