.
This commit is contained in:
		
							
								
								
									
										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
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
}());
 | 
			
		||||
		Reference in New Issue
	
	Block a user