Skip to content

⬅️ Back to Table of Contents

📄 insertRuleOptions

📊 Analysis Summary

Metric Count
🔧 Functions 4
📦 Imports 6
📊 Variables & Constants 3

📚 Table of Contents

🛠️ File Location:

📂 packages/website/plugins/generated-rule-docs/insertions/insertRuleOptions.ts

📦 Imports

Name Source
JSONSchema4 @typescript-eslint/utils/json-schema
Node unist
RuleDocsPage ../RuleDocsPage
nodeIsHeading ../../utils/nodes
nodeIsMdxFlowExpression ../../utils/nodes
findHeadingIndex ../../utils/rules

Variables & Constants

Name Type Kind Value Exported
knownSkippedRules Set<string> const new Set([ 'array-type', 'ban-ts-comment', 'member-ordering', ])
emptyOptionDefaults Map<unknown, unknown> const new Map<unknown, unknown>([ ['array', []], ['boolean', false], ])
OPTION_COMMENT "/* insert option description */" const /* insert option description */

Functions

insertRuleOptions(page: RuleDocsPage): void

Parameters:

  • page RuleDocsPage

Returns: void

Calls:

  • knownSkippedRules.has
  • Array.isArray
  • getOptionProperties
  • (page.rule.meta.schema as JSONSchema4[]).at
  • Object.entries
  • findHeadingIndex (from ../../utils/rules)
  • findCommentIndexForOption
  • emptyOptionDefaults.get
  • page.spliceChildren
  • formatDefaultValue

Internal Comments:

// Keep accepting deprecated defaultOptions for backward compatibility. (x2)
// eslint-disable-next-line @typescript-eslint/no-deprecated (x2)
// eslint-disable-next-line @typescript-eslint/internal/eqeq-nullish -- I don't know whether this is safe to fix (x3)

Code
export function insertRuleOptions(page: RuleDocsPage): void {
  if (
    knownSkippedRules.has(page.file.stem) ||
    !Array.isArray(page.rule.meta.schema)
  ) {
    return;
  }

  const optionProperties = getOptionProperties(
    (page.rule.meta.schema as JSONSchema4[]).at(0),
  );

  if (!optionProperties) {
    return;
  }

  // Keep accepting deprecated defaultOptions for backward compatibility.
  // eslint-disable-next-line @typescript-eslint/no-deprecated
  const defaultOptions = (page.rule.defaultOptions?.[0] ?? {}) as Record<
    string,
    unknown
  >;

  for (const [optionName, option] of Object.entries(optionProperties)) {
    if (!option.description) {
      if (!page.rule.meta.docs.extendsBaseRule) {
        throw new Error(`Missing description for option ${optionName}.`);
      }
      return;
    }

    const existingHeadingIndex = findHeadingIndex(
      page.children,
      3,
      node => node.type === 'inlineCode' && node.value === optionName,
    );
    if (existingHeadingIndex === -1) {
      if (!page.rule.meta.docs.extendsBaseRule) {
        throw new Error(`Couldn't find h3 for option ${optionName}.`);
      }
      continue;
    }

    const commentInsertionIndex = findCommentIndexForOption(
      page.children,
      existingHeadingIndex,
    );
    if (commentInsertionIndex === -1) {
      throw new Error(
        `[${page.file.stem}] Could not find ${OPTION_COMMENT} under option heading ${optionName}.`,
      );
    }

    const defaultValue =
      defaultOptions[optionName] ?? emptyOptionDefaults.get(option.type);

    page.spliceChildren(
      commentInsertionIndex,
      0,
      // eslint-disable-next-line @typescript-eslint/internal/eqeq-nullish -- I don't know whether this is safe to fix
      defaultValue !== undefined
        ? `${option.description} Default: \`${formatDefaultValue(defaultValue)}\`.`
        : option.description,
    );
  }
}

formatDefaultValue(defaultValue: unknown): string

Parameters:

  • defaultValue unknown

Returns: string

Calls:

  • Array.isArray
  • defaultValue.map(formatDefaultValue).join
  • JSON.stringify(defaultValue, null, 1).replaceAll
Code
function formatDefaultValue(defaultValue: unknown): string {
  if (Array.isArray(defaultValue)) {
    return `[${defaultValue.map(formatDefaultValue).join(', ')}]`;
  }

  return JSON.stringify(defaultValue, null, 1).replaceAll(/\s+/g, ' ');
}

findCommentIndexForOption(children: readonly Node[], headingIndex: number): number

Parameters:

  • children readonly Node[]
  • headingIndex number

Returns: number

Calls:

  • nodeIsMdxFlowExpression (from ../../utils/nodes)
  • nodeIsHeading (from ../../utils/nodes)
Code
function findCommentIndexForOption(
  children: readonly Node[],
  headingIndex: number,
): number {
  for (let i = headingIndex + 1; i < children.length; i += 1) {
    const child = children[i];
    if (nodeIsMdxFlowExpression(child) && child.value === OPTION_COMMENT) {
      return i;
    }

    if (nodeIsHeading(child)) {
      break;
    }
  }

  return -1;
}

getOptionProperties(options: JSONSchema4 | undefined): Record<string, JSONSchema4> | undefined

Parameters:

  • options JSONSchema4 | undefined

Returns: Record<string, JSONSchema4> | undefined

Calls:

  • options.oneOf.reduce
  • getOptionProperties
Code
function getOptionProperties(
  options: JSONSchema4 | undefined,
): Record<string, JSONSchema4> | undefined {
  if (!options) {
    return undefined;
  }

  if (options.type === 'object') {
    return options.properties;
  }

  if (options.oneOf) {
    return options.oneOf.reduce<Record<string, JSONSchema4>>(
      (previous, next) => ({
        ...previous,
        ...getOptionProperties(next),
      }),
      {},
    );
  }

  return undefined;
}

Generated by Syntax Scribe