Repository: eslint/eslint
File: lib/rule-tester/rule-tester.js (JavaScript)

Code before:
 * @param {Function} prototype The prototype with the original method to call.
 * @returns {Function} The function that throws the error.
 */
function throwForbiddenMethodError(methodName, prototype) {

    const original = prototype[methodName];

    return function(...args) {

        const called = forbiddenMethodCalls.get(methodName);

        /* eslint-disable no-invalid-this -- needed to operate as a method. */
        if (!called.has(this)) {
            called.add(this);

            return original.apply(this, args);
        }
        /* eslint-enable no-invalid-this -- not needed past this point */

        throw new Error(
            `\`SourceCode#${methodName}()\` cannot be called inside a rule.`
        );
    };
}

const messagePlaceholderRegex = /\{\{([^{}]+?)\}\}/gu;

/**
 * Extracts names of missing {{ placeholders }} from the reported message.
 * @param   {string} message Reported message
 * @returns {string[]} Array of missing placeholder names
 */
function getMissingMessagePlaceholders(message) {
    return Array.from(message.matchAll(messagePlaceholderRegex), ([, name]) => name.trim());
}

const metaSchemaDescription = `
\t- If the rule has options, set \`meta.schema\` to an array or non-empty object to enable options validation.
\t- If the rule doesn't have options, omit \`meta.schema\` to enforce that no options can be passed to the rule.
\t- You can also set \`meta.schema\` to \`false\` to opt-out of options validation (not recommended).

\thttps://eslint.org/docs/latest/extend/custom-rules#options-schemas
`;

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

// default separators for testing
const DESCRIBE = Symbol("describe");
const IT = Symbol("it");

Review comment (suggestion):
I do think this regexp should be exported by a shared util file so it can be used in multiple places.

Code after:
 * @param {string} methodName The name of the method to forbid.
 * @param {Function} prototype The prototype with the original method to call.
 * @returns {Function} The function that throws the error.
 */
function throwForbiddenMethodError(methodName, prototype) {

    const original = prototype[methodName];

    return function(...args) {

        const called = forbiddenMethodCalls.get(methodName);

        /* eslint-disable no-invalid-this -- needed to operate as a method. */
        if (!called.has(this)) {
            called.add(this);

            return original.apply(this, args);
        }
        /* eslint-enable no-invalid-this -- not needed past this point */

        throw new Error(
            `\`SourceCode#${methodName}()\` cannot be called inside a rule.`
        );
    };
}

/**
 * Extracts names of {{ placeholders }} from the reported message.
 * @param   {string} message Reported message
 * @returns {string[]} Array of placeholder names
 */
function getMessagePlaceholders(message) {
    const matcher = getPlaceholderMatcher();

    return Array.from(message.matchAll(matcher), ([, name]) => name.trim());
}

/**
 * Returns the placeholders in the reported messages but
 * only includes the placeholders available in the raw message and not in the provided data.
 * @param {string} message The reported message
 * @param {string} raw The raw message specified in the rule meta.messages
 * @param {undefined|Record<unknown, unknown>} data The passed
 * @returns {string[]} Missing placeholder names
 */
function getUnsubstitutedMessagePlaceholders(message, raw, data = {}) {
    const unsubstituted = getMessagePlaceholders(message);

    if (unsubstituted.length === 0) {
        return [];
    }