Skip to content

⬅️ Back to Table of Contents

📄 RulesTable

📊 Analysis Summary

Metric Count
🔧 Functions 13
📦 Imports 21
📊 Variables & Constants 3
💠 JSX Elements 50
📑 Type Aliases 3

📚 Table of Contents

🛠️ File Location:

📂 packages/website/src/components/RulesTable/index.tsx

📦 Imports

Name Source
RulesMeta @site/rulesMeta
RuleRecommendation @typescript-eslint/utils/ts-eslint
Link @docusaurus/Link
useHistory @docusaurus/router
useRulesMeta @site/src/hooks/useRulesMeta
clsx clsx
useMemo react
React react
HistorySelector ../../hooks/useHistorySelector
useHistorySelector ../../hooks/useHistorySelector
getRecommendationWithEmoji ../../theme/MDXComponents/RuleAttributes
CONFIG_EMOJI ../constants
DEPRECATED_RULE_EMOJI ../constants
EXTENSION_RULE_EMOJI ../constants
FIXABLE_EMOJI ../constants
RECOMMENDED_CONFIG_EMOJI ../constants
STRICT_CONFIG_EMOJI ../constants
STYLISTIC_CONFIG_EMOJI ../constants
SUGGESTIONS_EMOJI ../constants
TYPE_INFORMATION_EMOJI ../constants
styles ./styles.module.css

Variables & Constants

Name Type Kind Value Exported
filterModes readonly ["neutral", "include", "excl... const ['neutral', 'include', 'exclude'] as const
neutralFiltersState FiltersState const { deprecated: 'neutral', extension: 'neutral', fixable: 'neutral', recommende...
NEGATION_SYMBOL "x" const 'x'

JSX Elements

Component Type Props Children
code element key={i} {v}
tr element none , , , , ,
td element none
,
,
div element className={styles.ruleNameWrapper} ,
Link component to={new URL(rule.docs.url).pathname}
code element none text: "@typescript-eslint/",
span element none text: "❄️"
br element none none
td element className={styles.attrCol}, title={actualRecommended} {emoji}
td element className={styles.attrCol}, title={ fixable && hasSuggestions ? 'fixable and ... {fixable ? FIXABLE_EMOJI : ''}, {fixable && hasSuggestions ?
: ''}, {h...
br element none none
td element className={styles.attrCol}, title={requiresTypeChecking ? 'requires type info... {requiresTypeChecking ? TYPE_INFORMATION_EMOJI : ''}
td element className={styles.attrCol}, title={extendsBaseRule ? 'extends base rule' : un... {extendsBaseRule ? EXTENSION_RULE_EMOJI : ''}
td element className={styles.attrCol}, title={deprecated ? 'deprecated' : undefined} {deprecated ? DEPRECATED_RULE_EMOJI : ''}
li element className={styles.checkboxListItem}
button element aria-label={Toggle the filter mode. Current: ${mode}}, className={clsx( sty...
,
div element aria-hidden, className={clsx(styles.visual, styles[visual-${mode}])} none
Fragment fragment none
,
,

,

,
div element className={styles.checkboxListArea} ,
    em element none text: "Config Group (", {CONFIG_EMOJI}, text: ")"
    ul element className={clsx('clean-list', styles.checkboxList)} , ,
    RuleFilterCheckBox component label={${RECOMMENDED_CONFIG_EMOJI} recommended}, mode={filters.recommended}... none
    RuleFilterCheckBox component label={${STRICT_CONFIG_EMOJI} strict}, mode={filters.strict}, setMode={(new... none
    RuleFilterCheckBox component label={${STYLISTIC_CONFIG_EMOJI} stylistic}, mode={filters.stylistic}, setM... none
    div element className={styles.checkboxListArea} ,
      em element none text: "Metadata"
      ul element className={clsx('clean-list', styles.checkboxList)} , , , <RuleFilter...
      RuleFilterCheckBox component label={${FIXABLE_EMOJI} fixable}, mode={filters.fixable}, setMode={(newMode... none
      RuleFilterCheckBox component label={${SUGGESTIONS_EMOJI} has suggestions}, mode={filters.suggestions}, s... none
      RuleFilterCheckBox component label={${TYPE_INFORMATION_EMOJI} type checked}, mode={filters.typeInformati... none
      RuleFilterCheckBox component label={${EXTENSION_RULE_EMOJI} extension}, mode={filters.extension}, setMod... none
      RuleFilterCheckBox component label={${DEPRECATED_RULE_EMOJI} deprecated}, mode={filters.deprecated}, set... none
      p element none text: "(These categories are explained in", {' '}, , text: ".)"
      a element href="#filtering" text: "more detail below"
      table element className={styles.rulesTable}
      thead element none
      tr element none , , , , ,
      th element className={styles.ruleCol} text: "Rule"
      th element className={styles.attrCol}
      div element title="The config group that the rule belongs to, if any." {CONFIG_EMOJI}
      th element className={styles.attrCol}
      div element title="Whether the rule has an auto-fixer and/or has suggestions." {FIXABLE_EMOJI}
      th element className={styles.attrCol}
      div element title="Whether the rule requires type information from the TypeScript compiler." {TYPE_INFORMATION_EMOJI}
      th element className={styles.attrCol}
      div element title="Whether the rule is an extension rule (i.e. based on a core ESLint rul... {EXTENSION_RULE_EMOJI}
      th element className={styles.attrCol}
      div element title="Whether the rule is deprecated." {DEPRECATED_RULE_EMOJI}
      tbody element none {relevantRules.map(rule => ( ))}
      RuleRow component key={rule.name}, rule={rule} none

      Functions

      RulesTable(): React.JSX.Element

      Returns: React.JSX.Element

      Calls:

      • useRulesFilters
      • useRulesMeta (from @site/src/hooks/useRulesMeta)
      • useMemo (from react)
      • rules.filter
      • getActualRecommended
      • [ match(filters.recommended, actualRecommended === 'recommended'), match( filters.strict, actualRecommended === 'recommended' || actualRecommended === 'strict', ), match(filters.stylistic, actualRecommended === 'stylistic'), match(filters.fixable, !!r.fixable), match(filters.suggestions, !!r.hasSuggestions), match(filters.typeInformation, !!r.docs.requiresTypeChecking), match(filters.extension, !!r.docs.extendsBaseRule), match(filters.deprecated, !!r.deprecated), ].filter
      • match
      • opinions.every
      • clsx (from clsx)
      • changeFilter
      • relevantRules.map

      Internal Comments:

      // eslint-disable-next-line @typescript-eslint/internal/eqeq-nullish (x2)
      

      Code
      export default function RulesTable(): React.JSX.Element {
        const [filters, changeFilter] = useRulesFilters();
      
        const rules = useRulesMeta();
        const relevantRules = useMemo(
          () =>
            rules.filter(r => {
              const actualRecommended = getActualRecommended(r)[1];
              const opinions = [
                match(filters.recommended, actualRecommended === 'recommended'),
                match(
                  filters.strict,
                  actualRecommended === 'recommended' ||
                    actualRecommended === 'strict',
                ),
                match(filters.stylistic, actualRecommended === 'stylistic'),
                match(filters.fixable, !!r.fixable),
                match(filters.suggestions, !!r.hasSuggestions),
                match(filters.typeInformation, !!r.docs.requiresTypeChecking),
                match(filters.extension, !!r.docs.extendsBaseRule),
                match(filters.deprecated, !!r.deprecated),
              ].filter(
                (o): o is boolean =>
                  // eslint-disable-next-line @typescript-eslint/internal/eqeq-nullish
                  o !== undefined,
              );
              return opinions.every(o => o);
            }),
          [rules, filters],
        );
      
        return (
          <>
            <div className={styles.checkboxListArea}>
              <em>Config Group ({CONFIG_EMOJI})</em>
              <ul className={clsx('clean-list', styles.checkboxList)}>
                <RuleFilterCheckBox
                  label={`${RECOMMENDED_CONFIG_EMOJI} recommended`}
                  mode={filters.recommended}
                  setMode={(newMode): void => changeFilter('recommended', newMode)}
                />
                <RuleFilterCheckBox
                  label={`${STRICT_CONFIG_EMOJI} strict`}
                  mode={filters.strict}
                  setMode={(newMode): void => changeFilter('strict', newMode)}
                />
                <RuleFilterCheckBox
                  label={`${STYLISTIC_CONFIG_EMOJI} stylistic`}
                  mode={filters.stylistic}
                  setMode={(newMode): void => changeFilter('stylistic', newMode)}
                />
              </ul>
            </div>
            <div className={styles.checkboxListArea}>
              <em>Metadata</em>
              <ul className={clsx('clean-list', styles.checkboxList)}>
                <RuleFilterCheckBox
                  label={`${FIXABLE_EMOJI} fixable`}
                  mode={filters.fixable}
                  setMode={(newMode): void => changeFilter('fixable', newMode)}
                />
                <RuleFilterCheckBox
                  label={`${SUGGESTIONS_EMOJI} has suggestions`}
                  mode={filters.suggestions}
                  setMode={(newMode): void => changeFilter('suggestions', newMode)}
                />
                <RuleFilterCheckBox
                  label={`${TYPE_INFORMATION_EMOJI} type checked`}
                  mode={filters.typeInformation}
                  setMode={(newMode): void =>
                    changeFilter('typeInformation', newMode)
                  }
                />
                <RuleFilterCheckBox
                  label={`${EXTENSION_RULE_EMOJI} extension`}
                  mode={filters.extension}
                  setMode={(newMode): void => changeFilter('extension', newMode)}
                />
                <RuleFilterCheckBox
                  label={`${DEPRECATED_RULE_EMOJI} deprecated`}
                  mode={filters.deprecated}
                  setMode={(newMode): void => changeFilter('deprecated', newMode)}
                />
              </ul>
            </div>
            <p>
              (These categories are explained in{' '}
              <a href="#filtering">more detail below</a>.)
            </p>
            <table className={styles.rulesTable}>
              <thead>
                <tr>
                  <th className={styles.ruleCol}>Rule</th>
                  <th className={styles.attrCol}>
                    <div title="The config group that the rule belongs to, if any.">
                      {CONFIG_EMOJI}
                    </div>
                  </th>
                  <th className={styles.attrCol}>
                    <div title="Whether the rule has an auto-fixer and/or has suggestions.">
                      {FIXABLE_EMOJI}
                    </div>
                  </th>
                  <th className={styles.attrCol}>
                    <div title="Whether the rule requires type information from the TypeScript compiler.">
                      {TYPE_INFORMATION_EMOJI}
                    </div>
                  </th>
                  <th className={styles.attrCol}>
                    <div title="Whether the rule is an extension rule (i.e. based on a core ESLint rule).">
                      {EXTENSION_RULE_EMOJI}
                    </div>
                  </th>
                  <th className={styles.attrCol}>
                    <div title="Whether the rule is deprecated.">
                      {DEPRECATED_RULE_EMOJI}
                    </div>
                  </th>
                </tr>
              </thead>
              <tbody>
                {relevantRules.map(rule => (
                  <RuleRow key={rule.name} rule={rule} />
                ))}
              </tbody>
            </table>
          </>
        );
      }
      

      interpolateCode(text: string): string | (string | React.JSX.Element)[]

      Parameters:

      • text string

      Returns: string | (string | React.JSX.Element)[]

      Calls:

      • text.split
      • fragments.map
      Code
      function interpolateCode(
        text: string,
      ): string | (string | React.JSX.Element)[] {
        const fragments = text.split(/`(.*?)`/);
        if (fragments.length === 1) {
          return text;
        }
        return fragments.map((v, i) => (i % 2 === 0 ? v : <code key={i}>{v}</code>));
      }
      

      getActualRecommended({ docs, }: RulesMeta[number]): ['', ''] | [string, RuleRecommendation]

      Parameters:

      • { docs, } RulesMeta[number]

      Returns: ['', ''] | [string, RuleRecommendation]

      Calls:

      • getRecommendationWithEmoji (from ../../theme/MDXComponents/RuleAttributes)
      Code
      function getActualRecommended({
        docs,
      }: RulesMeta[number]): ['', ''] | [string, RuleRecommendation] {
        const recommended = docs.recommended;
        return recommended ? getRecommendationWithEmoji(recommended) : ['', ''];
      }
      

      RuleRow({ rule, }: { rule: RulesMeta[number]; }): React.JSX.Element | null

      Parameters:

      • { rule, } { rule: RulesMeta[number]; }

      Returns: React.JSX.Element | null

      Calls:

      • getActualRecommended
      • interpolateCode
      Code
      function RuleRow({
        rule,
      }: {
        rule: RulesMeta[number];
      }): React.JSX.Element | null {
        if (!rule.docs.url) {
          return null;
        }
        const { deprecated, fixable, hasSuggestions } = rule;
        const { extendsBaseRule, requiresTypeChecking } = rule.docs;
        const [emoji, actualRecommended] = getActualRecommended(rule);
        return (
          <tr>
            <td>
              <div className={styles.ruleNameWrapper}>
                <Link to={new URL(rule.docs.url).pathname}>
                  <code>@typescript-eslint/{rule.name}</code>
                </Link>
                {rule.docs.frozen && <span>❄️</span>}
              </div>
              <br />
              {interpolateCode(rule.docs.description)}
            </td>
            <td className={styles.attrCol} title={actualRecommended}>
              {emoji}
            </td>
            <td
              className={styles.attrCol}
              title={
                fixable && hasSuggestions
                  ? 'fixable and has suggestions'
                  : fixable
                    ? 'fixable'
                    : hasSuggestions
                      ? 'has suggestions'
                      : undefined
              }
            >
              {fixable ? FIXABLE_EMOJI : ''}
              {fixable && hasSuggestions ? <br /> : ''}
              {hasSuggestions ? SUGGESTIONS_EMOJI : ''}
            </td>
            <td
              className={styles.attrCol}
              title={requiresTypeChecking ? 'requires type information' : undefined}
            >
              {requiresTypeChecking ? TYPE_INFORMATION_EMOJI : ''}
            </td>
            <td
              className={styles.attrCol}
              title={extendsBaseRule ? 'extends base rule' : undefined}
            >
              {extendsBaseRule ? EXTENSION_RULE_EMOJI : ''}
            </td>
            <td
              className={styles.attrCol}
              title={deprecated ? 'deprecated' : undefined}
            >
              {deprecated ? DEPRECATED_RULE_EMOJI : ''}
            </td>
          </tr>
        );
      }
      

      RuleFilterCheckBox({ label, mode, setMode, }: { label: string; mode: FilterMode; setM…): React.JSX.Element

      Parameters:

      • { label, mode, setMode, } { label: string; mode: FilterMode; setMode: (mode: FilterMode) => void; }

      Returns: React.JSX.Element

      Calls:

      • setMode
      • filterModes.indexOf
      • clsx (from clsx)
      • toNextMode
      Code
      function RuleFilterCheckBox({
        label,
        mode,
        setMode,
      }: {
        label: string;
        mode: FilterMode;
        setMode: (mode: FilterMode) => void;
      }): React.JSX.Element {
        const toNextMode = (): void =>
          setMode(filterModes[(filterModes.indexOf(mode) + 1) % filterModes.length]);
        return (
          <li className={styles.checkboxListItem}>
            <button
              aria-label={`Toggle the filter mode. Current: ${mode}`}
              className={clsx(
                styles.checkboxLabel,
                mode === 'include' && styles.activated,
                mode === 'exclude' && styles.deactivated,
              )}
              onClick={toNextMode}
              onKeyDown={(e): void => {
                if (e.key === 'Enter') {
                  toNextMode();
                }
              }}
              type="button"
            >
              <div
                aria-hidden
                className={clsx(styles.visual, styles[`visual-${mode}`])}
              />
              {label}
            </button>
          </li>
        );
      }
      

      match(mode: FilterMode, value: boolean): boolean | undefined

      Parameters:

      • mode FilterMode
      • value boolean

      Returns: boolean | undefined

      Code
      function match(mode: FilterMode, value: boolean): boolean | undefined {
        if (mode === 'exclude') {
          return !value;
        }
        if (mode === 'include') {
          return value;
        }
        return undefined;
      }
      

      selectSearch(history: H.History): any

      Parameters:

      • history H.History

      Returns: any

      Code
      history =>
        history.location.search
      

      getServerSnapshot(): string

      Returns: string

      Code
      (): string => ''
      

      useRulesFilters(paramsKey: string): [FiltersState, (category: FilterCategory, mode: FilterMode)…

      Parameters:

      • paramsKey any: Optional. Whether to include rules that match the particular search filter. Defaults to an empty string, which matches all rules.
      Raw JSDoc
      /**
       * @param paramsKey Optional. Whether to include rules that match the particular
       * search filter. Defaults to an empty string, which matches all rules.
       */
      

      Calls:

      • useHistory (from @docusaurus/router)
      • useHistorySelector (from ../../hooks/useHistorySelector)
      • new URLSearchParams(search).get
      • useMemo (from react)
      • parseFiltersState
      • stringifyFiltersState
      • searchParams.set
      • searchParams.delete
      • history.replace
      • searchParams.toString

      Internal Comments:

      // We can't compute this in selectSearch, because we need the snapshot to be (x2)
      // comparable by value. (x2)
      

      Code
      function useRulesFilters(
        paramsKey = '',
      ): [FiltersState, (category: FilterCategory, mode: FilterMode) => void] {
        const history = useHistory();
        const search = useHistorySelector(selectSearch, getServerSnapshot);
      
        const paramValue = new URLSearchParams(search).get(paramsKey) ?? '';
        // We can't compute this in selectSearch, because we need the snapshot to be
        // comparable by value.
        const filtersState = useMemo(
          () => parseFiltersState(paramValue),
          [paramValue],
        );
      
        const changeFilter = (category: FilterCategory, mode: FilterMode): void => {
          const newState = { ...filtersState, [category]: mode };
      
          if (
            category === 'strict' &&
            mode === 'include' &&
            filtersState.recommended === 'include'
          ) {
            newState.recommended = 'exclude';
          } else if (
            category === 'recommended' &&
            mode === 'include' &&
            filtersState.strict === 'include'
          ) {
            newState.strict = 'exclude';
          }
      
          const searchParams = new URLSearchParams(history.location.search);
          const filtersString = stringifyFiltersState(newState);
      
          if (filtersString) {
            searchParams.set(paramsKey, filtersString);
          } else {
            searchParams.delete(paramsKey);
          }
      
          history.replace({ search: searchParams.toString() });
        };
      
        return [filtersState, changeFilter];
      }
      

      stringifyFiltersState(filters: FiltersState): string

      Parameters:

      • filters FiltersState

      Returns: string

      Calls:

      • Object.entries(filters) .map(([key, value]) => value === 'include' ? key : value === 'exclude' ?${NEGATION_SYMBOL}${key}: '', ) .filter(Boolean) .join
      Code
      function stringifyFiltersState(filters: FiltersState): string {
        return Object.entries(filters)
          .map(([key, value]) =>
            value === 'include'
              ? key
              : value === 'exclude'
                ? `${NEGATION_SYMBOL}${key}`
                : '',
          )
          .filter(Boolean)
          .join('-');
      }
      

      parseFiltersState(str: string): FiltersState

      Parameters:

      • str string

      Returns: FiltersState

      Calls:

      • str.split
      • part.startsWith
      • part.slice
      • Object.hasOwn
      Code
      function parseFiltersState(str: string): FiltersState {
        const res: FiltersState = { ...neutralFiltersState };
      
        for (const part of str.split('-')) {
          const exclude = part.startsWith(NEGATION_SYMBOL);
          const key = exclude ? part.slice(1) : part;
          if (Object.hasOwn(neutralFiltersState, key)) {
            res[key as keyof typeof neutralFiltersState] = exclude
              ? 'exclude'
              : 'include';
          }
        }
      
        return res;
      }
      

      Internal helpers

      Declared inside another function in this file.

      toNextMode(): void

      Returns: void

      Calls:

      • setMode
      Code
      (): void =>
          setMode(filterModes[(filterModes.indexOf(mode) + 1) % filterModes.length])
      

      changeFilter(category: FilterCategory, mode: FilterMode): void

      Parameters:

      • category FilterCategory
      • mode FilterMode

      Returns: void

      Calls:

      • stringifyFiltersState
      • searchParams.set
      • searchParams.delete
      • history.replace
      • searchParams.toString
      Code
      (category: FilterCategory, mode: FilterMode): void => {
          const newState = { ...filtersState, [category]: mode };
      
          if (
            category === 'strict' &&
            mode === 'include' &&
            filtersState.recommended === 'include'
          ) {
            newState.recommended = 'exclude';
          } else if (
            category === 'recommended' &&
            mode === 'include' &&
            filtersState.strict === 'include'
          ) {
            newState.strict = 'exclude';
          }
      
          const searchParams = new URLSearchParams(history.location.search);
          const filtersString = stringifyFiltersState(newState);
      
          if (filtersString) {
            searchParams.set(paramsKey, filtersString);
          } else {
            searchParams.delete(paramsKey);
          }
      
          history.replace({ search: searchParams.toString() });
        }
      

      Type Aliases

      FilterMode

      type FilterMode = (typeof filterModes)[number];
      

      FilterCategory

      type FilterCategory = | 'deprecated'
        | 'extension'
        | 'fixable'
        | 'recommended'
        | 'strict'
        | 'stylistic'
        | 'suggestions'
        | 'typeInformation';
      

      FiltersState

      type FiltersState = Record<FilterCategory, FilterMode>;
      

      Generated by Syntax Scribe