Skip to content

How to enforce using multilines with React components? #272

@Vadorequest

Description

@Vadorequest
  • prettier-eslint-cli version: 5.0.0
  • prettier version: 2.0.5
  • eslint version: 7.0.0

Relevant code/config.

import isEmpty from 'lodash.isempty';
import map from 'lodash.map';
import Link from 'next/link';
import React, { ReactNode } from 'react';
import useI18n, { I18n } from '../../hooks/useI18n';
import { I18nRoute, resolveI18nRoute } from '../../utils/app/router';

type Props = {
  as?: string;
  children: ReactNode;
  href: string;
  locale?: string; // The locale can be specified, but it'll fallback to the current locale if unspecified
  params?: { [key: string]: string | number };
  passHref?: boolean;
  prefetch?: boolean;
  replace?: boolean;
  scroll?: boolean;
  shallow?: boolean;
  wrapChildrenAsLink?: boolean; // Helper to avoid writing redundant code
}

/**
 * Wrapper around the native Next.js <Link> component. Handles localised links.
 *
 * Use the current active locale by default
 *
 * @example Recommended usage
 *  <I18nLink href={`/`}>Homepage</I18nLink>
 *
 * @example When specifying the locale to use (overrides default)
 *  <I18nLink href={`/`} locale={'fr-FR'}>Homepage in "fr-FR" locale</I18nLink>
 *
 * @example The recommended usage is equivalent to this (wrapChildrenAsLink is true, by default)
 *  <I18nLink href={`/`} wrapChildrenAsLink={false}><a>Homepage</a></I18nLink>
 *
 * @example When using children that use a <a> tag, you must set wrapChildrenAsLink to false to avoid using a link within a link
 *   <I18nLink
 *     href={`/`}
 *     wrapChildrenAsLink={false}
 *   >
 *      <NavLink>Homepage</NavLink>
 *   </I18nLink>
 *
 * @example When using route params (other than "locale")
 *   <I18nLink
 *     href={'/products/[id]'}
 *     params={{
 *       id: 5,
 *     }}
 *   >
 *      Go to product 5
 *   </I18nLink>
 *
 * @param props
 */
const I18nLink: React.FunctionComponent<Props> = (props): JSX.Element => {
  const { locale: currentLocale }: I18n = useI18n();
  const {
    as,
    children,
    href,
    locale = currentLocale,
    params,
    passHref = true,
    wrapChildrenAsLink = true,
    ...rest // Should only contain valid next/Link props
  } = props;
  let {
    i18nHref, // eslint-disable-line prefer-const
    i18nAs,
  }: I18nRoute = resolveI18nRoute({ as, href, locale });

  if (!isEmpty(params)) {
    // If any params are provided, replace their name by the provided value
    map(params, (value: string, key: string | number) => {
      i18nAs = i18nAs.replace(`[${key}]`, value);
    });
  }

  return (
    <Link
      href={i18nHref}
      as={i18nAs}
      {...rest}
      passHref={passHref}
    >
      {
        wrapChildrenAsLink ? (
          // eslint-disable-next-line jsx-a11y/anchor-is-valid
          <a>{children}</a>
        ) : React.cloneElement(children as React.ReactElement)
      }
    </Link>
  );
};

export default I18nLink;

Running the following:
prettier-eslint --write "$PWD/src/components/i18n/I18nLink.{js,jsx,ts,tsx}" --eslint-config-path .eslintrc.yml --trailing-comma es5 --bracket-spacing

I get a result shown in this diff

image

How can I force prettier-eslint-cli to:

  • Keep properties in <Link always multiline
  • Have react {} always multiline

The end result should look like this:

  return (
    <Link
      href={i18nHref}
      as={i18nAs}
      {...rest}
      passHref={passHref}
    >
      {
        wrapChildrenAsLink ? (
          // eslint-disable-next-line jsx-a11y/anchor-is-valid
          <a>{children}</a>
        ) : (
          React.cloneElement(children as React.ReactElement)
        )
      }
    </Link>
  );
Details

.eslintrc

---
# XXX Must read to understand how prettier/eslint work together - https://blog.theodo.com/2019/08/empower-your-dev-environment-with-eslint-prettier-and-editorconfig-with-no-conflicts/
# XXX EditorConfig provides everyone with the same editor configuration (prettier inherits from it) - All configuration related to the editor (end of line, indent style, indent size...) should be handled by EditorConfig
# XXX No "code formatting" should be done by ESLint - Everything related to code formatting should be handled by Prettier
# XXX EsLint handles our code quality only - The rest (code quality) should be handled by ESLint
env:
  browser: true
  commonjs: true
  es6: true
  node: true
extends:
  - airbnb-typescript-prettier # https://github.com/toshi-toma/eslint-config-airbnb-typescript-prettier#readme
plugins:
  - jest
parserOptions:
  project: ./tsconfig.json
globals:
  Atomics: readonly
  SharedArrayBuffer: readonly
settings:
  react:
    version: detect
rules: # See https://eslint.org/docs/rules
#  Managed by prettier
#  semi
#  quotes
#  indent
#  max-len
  comma-spacing:
    - error
    - before: false
      after: true
  arrow-parens:
    - error
    - always
  strict: 'off'
  no-console: 1 # Shouldn't use "console", but "logger" instead
  allowArrowFunctions: 0
  no-unused-vars: 0 # Disabled, already handled by @typescript-eslint/no-unused-vars
  import/prefer-default-export: 0 # When there is only a single export from a module, don't enforce a default export, but rather let developer choose what's best
  no-else-return: 0 # Don't enforce, let developer choose. Sometimes we like to specifically use "return" for the sake of comprehensibility and avoid ambiguity
  no-underscore-dangle: 0 # Allow _ before/after variables and functions, convention for something meant to be "private"
  arrow-body-style: 0 # Don't enforce, let developer choose. Sometimes we like to specifically use "return" for ease of debugging and printing
  quote-props:
    - warn
    - consistent-as-needed # Enforce consistency with quotes on props, either all must be quoted, or all unquoted for a given object
  no-return-await: 0 # Useful before, but recent node.js enhancements make it useless on node 12+ (we use 10, but still, for consistency) - Read https://stackoverflow.com/questions/44806135/why-no-return-await-vs-const-x-await
  no-extra-boolean-cast: 0 # Don't enforce, let developer choose. Using "!!!" is sometimes useful (edge cases), and has a semantic value (dev intention)
  array-element-newline: # https://eslint.org/docs/rules/array-element-newline
    - error
    - consistent
  object-curly-newline:
    - warn
    - ObjectExpression:
        multiline: true
        minProperties: 5
        consistent: true
      ObjectPattern:
        multiline: true
        minProperties: 5
        consistent: true
      ImportDeclaration:
        multiline: true
        minProperties: 8 # Doesn't play so well with webstorm, which wraps based on the number of chars in the row, not based on the number of props #sucks
        consistent: true
      ExportDeclaration:
        multiline: true
        minProperties: 5
        consistent: true
  react-hooks/rules-of-hooks: error
  react-hooks/exhaustive-deps: warn
  react/jsx-no-target-blank: warn # Not using "noreferrer" is not a security risk, but "noopener" should always be used indeed
  react/prop-types: warn # Should be handled with TS instead
  react/no-unescaped-entities: 0 # Causes text mismatch when enabled
  jsx-a11y/anchor-is-valid: warn
  linebreak-style:
    - error
    - unix
  '@typescript-eslint/ban-ts-ignore': warn # ts-ignore are sometimes the only way to bypass a TS issue, we trust we will use them for good and not abuse them
  '@typescript-eslint/no-use-before-define': warn
  '@typescript-eslint/no-unused-vars':
    - warn
    -
      vars: 'all' # We don't want unused variables (noise) - XXX Note that this will be a duplicate of "no-unused-vars" rule
      args: 'none' # Sometimes it's useful to have unused arguments for later use, such as describing what args are available (DX)
      ignoreRestSiblings: true # Sometimes it's useful to have unused props for later use, such as describing what props are available (DX)
overrides:
  - files: ['**/*.tsx']
    rules:
      'react/prop-types': 'off'

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions