team logo icon

[초기세팅] ESLint, Prettier, Stylelint 설치

eslint, prettier, stylelint 설정 가보자구

ESLINT란?

코딩 컨벤션에 위배되는 코드나 안티 패턴을 자동 검출하는 정적 코드 분석 도구로 개발자가 자신의 스타일 가이드를 작성하며 일정한 코딩 퀄리티를 보장할 수 있도록 하는 툴이다.


[VSCode Eslint 확장 설치]


[yarn에 eslint add]

yarn add -D eslint


[eslint 패키치 추가]

yarn add -D eslint eslint-config-prettier eslint-plugin-prettier eslint-plugin-import 
eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-simple-import-sort 
@typescript-eslint/eslint-plugin @typescript-eslint/parser


eslint-config-prettier 

: prettier 설정과 충돌하는 eslint 규칙 비활성화 

eslint-plugin-prettier

: eslint 내 prettier 검사 실행 

eslint-plugin-import

: import/export 문법의 린팅을 지원하고 파일 경로, import 이름의 오타 예방

eslint-plugin-react

: 리액트와 관련된 룰을 정의

eslint-plugin-react-hooks

: 리액트 훅과 관련된 룰을 정의

eslint-plugin-simple-import-sort

: 자동으로 import문 정렬

@typescript-eslint/eslint-plugin @typescript-eslint/parser

: 타입스크립트 코드 린팅해 주는 도구


[.eslintrc.json 파일 추가]

{
  "env": {
    "browser": true,
    "es2021": true,
    "node": true,
    "es6": true
  },
  "plugins": [
    "react",
    "react-hooks",
    "@typescript-eslint",
    "@typescript-eslint/eslint-plugin",
    "simple-import-sort",
    "prettier",
    "import"
  ],
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:@typescript-eslint/eslint-recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:react-hooks/recommended",
    "plugin:prettier/recommended",
    "plugin:import/recommended",
    "plugin:import/typescript",
  ],
  "rules": {
    "quotes": ["off", "single"],
    "semi": ["error", "always"],
    "no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 0 }],
    "no-multi-spaces": "error",
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "off",
    "react/react-in-jsx-scope": "off",
    "arrow-parens": ["error", "always"],
    "no-duplicate-imports": "error",
    "simple-import-sort/imports": "error",
    "simple-import-sort/exports": "error",
    "no-unused-vars": "error",
    "no-undef": "error",
    "indent": "off",
    "import/no-unresolved": "off",
    "no-console": ["warn", { "allow": ["warn", "error", "info"] }],
    "prettier/prettier": "error",

    "import/order": [
      "error",
      {
        "groups": [["builtin", "external"], "internal", ["parent", "sibling"], "index"],
        "pathGroups": [
          {
            "pattern": "react*",
            "group": "external",
            "position": "before"
          }
        ],
        "alphabetize": {
          "order": "asc",
          "caseInsensitive": true
        }
      }
    ]
  },
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    },
    "ecmaVersion": "latest",
    "sourceType": "module"
  },
  "ignorePatterns": ["build", "dist", "public"],

  "settings": {
    "import/resolver": {
      "node": {
        "moduleDirectory": ["node_modules", "src/"],
        "extensions": [".js", ".jsx", ".ts", ".tsx", ".d.ts", ".svg"]
      }
    },
    "react": {
      "version": "detect"
    },
    "import/parsers": {
      "@typescript-eslint/parser": [".ts", ".tsx", ".js"]
    }
  }
}


[.eslintrc 규칙 설정]


quotes:  쌍따옴표가 아닌 홑따옴표 사용

semi: semi colon을 강제

no-multiple-empty-lines/no-multi-spaces: 빈 라인/스페이스 여러개 금지

react-hooks/rules-of-hooks: 리액트 훅의 순서 지정

arrow-parens: 화살표 함수 괄호 사용 방식

arrow-function 인자가 2개 이상이면 괄호 필수

no-duplicate-imports: 중복 Import 금지

simple-import-sort/imports: import 정렬 강제

simple-import-sort/exports: export 정렬 강제

no-unused-vars: 사용하지 않는 변수 에러 처리

no-undef: 정의 안 한 변수 사용 금지

indent: 프리티어 충돌 방지

import/no-unresolved: 타입스크립트에서 경로를 제대로 잡지 못할 때 사용 

no-console: 콘솔은 확인 뒤 삭제

prettier/prettier: error ESLint 내 Prettier를 실행 규칙


Prettier란?

코드 구현과는 관련없는, 일관된 텍스트 작성을 도와주는 도구로 탭의 줄 간격이나, 줄바꿈 정도 등 텍스트 작성에 관련하여 프로젝트 내 일관된 형식을 가지게 한다.


[VSCode Prettier 확장 설치]



yarn에 Prettier add

yarn add -D prettier


prettier 이 잘 적용되지 않는다면 crtml+` 를 들어가 아래 설정을 nont->code prettier로 바꿔주면 된다! 



[.prettierrc 파일 추가] (추가하지 않을 시 prettier root에 있는 기본 설정으로 들어감) 

{
  "printWidth": 120,
  "tabWidth": 2,
  "semi": true,
  "singleQuote": true,
  "trailingComma": "all",
  "requirePragma": false,
  "arrowParens": "always",
  "bracketSameLine": true,
  "endOfLine": "auto"
}


[.prettierrc 설정 목록 ]


printWidth: 줄 바꿈 폭 길이

semi: 세미콜론 사용 여부

trailingComma:  후행 콤마 사용 방식

requirePragma: 파일 상단에 미리 정의된 주석을 작성 및 Pragma 포맷팅 사용 여부 지정

arrowParens: 화살표 함수 괄호 사용 방식

bracketSpacing: 객체 리터럴에서 괄호에 공백 삽입 여부 

endOfLine: 텍스트의 한 줄이 끝남을 표시하는 문자열 (EoF 방식, OS별로 처리 방식이 다름)


Stylelint란?

css 정적 분석 도구 중 하나로 최신 css 문법을 지원하며 쉽게 규칙을 추가할 수 있도록 돕는 도구이다.


[VSCode Stylelint 확장 설치]


[Stylelint 패키지 설치]

yarn add -D stylelint stylelint-config-recommended stylelint-config-styled-components 
postcss postcss-syntax @stylelint/postcss-css-in-js stylelint-config-clean-order


stylelint 

: stylelint의 사용을 가능토록 해 준다 

stylelint-config-recommended

: stylelint에서 제공하 오류 방지에 도움이 되는 규칙들

stylelint-config-styled-components

: 스타일 컴포넌트에서 stylelint가 사용될 수 있도록 

postcss postcss-syntax @stylelint/postcss-css-in-js

: stylelint 14버전의 호환에 맞게 설치하도록 해준다고 한

stylelint-config-clean-order

: stylelint의 순서에 맞춰 order 설정



[.stylelintrc 설정]

-> extends는 package.json에 나열된 순서대로 작성!

{

  "plugins": ["stylelint-order"],

  "extends": ["stylelint-config-clean-order", "stylelint-config-recommended", "stylelint-config-styled-components"],

  "overrides": [

    {

      "files": ["**/*.{ts,tsx}"],

      "customSyntax": "@stylelint/postcss-css-in-js"

    }

}


<rules 설정>

https://stylelint.io/user-guide/rules/#whitespace-inside 참고! 

declaration-empty-line-before

: 빈 줄을 허용하지 않음

order/order

: 속성 나열 순서 지정 (custom 먼저)

order/properties-order

: 속성 순서

declaration-property-unit-allowed-list

: 선언 내에서 허용되는 속성 및 단위 쌍 목록을 지정 (border은 px, padding이나 gap은 rem)

unit-allowed-list

: 허용되는 단위 목록을 지정 ( %, deg, px, rem, ms)

color-named

: never로 지정해 색상을 이름이 아닌 #00000 형식으로 작성하게 함 

property-no-vendor-prefix

: 속성에 대해 허용하지 없는 접두사

length-zero-no-unit

: 단위가 0인 경우 속성을 허용하지 않음

 

a { -webkit-transform: scale(1); }
/**  ↑
 * This prefix */
  "rules": {
    "declaration-empty-line-before": [
      "never",
      {
        "ignore": ["after-comment", "after-declaration", "inside-single-line-block"]
      }
    ],
    "order/order": ["custom-properties", "declarations"],

    "order/properties-order": [
      {
        "groupName": "Positioning",
        "noEmptyLineBetween": true,
        "properties": ["position", "top", "right", "bottom", "left", "z-index"]
      },

      {
        "groupName": "BoxModel",
        "emptyLineBefore": "always",
        "noEmptyLineBetween": true,
        "properties": [
          "display",
          "flex",
          "flex-basis",
          "flex-direction",
          "flex-flow",
          "flex-grow",
          "flex-shrink",
          "flex-wrap",
          "grid",
          "grid-area",
          "grid-auto-rows",
          "grid-auto-columns",
          "grid-auto-flow",
          "grid-gap",
          "grid-row",
          "grid-row-start",
          "grid-row-end",
          "grid-row-gap",
          "grid-column",
          "grid-column-start",
          "grid-column-end",
          "grid-column-gap",
          "grid-template",
          "grid-template-areas",
          "grid-template-rows",
          "grid-template-columns",
          "gap",
          "align-content",
          "align-items",
          "align-self",
          "justify-content",
          "justify-items",
          "justify-self",
          "order",
          "float",
          "clear",
          "box-sizing",
          "width",
          "min-width",
          "max-width",
          "height",
          "min-height",
          "max-height",
          "margin",
          "margin-top",
          "margin-right",
          "margin-bottom",
          "margin-left",
          "padding",
          "padding-top",
          "padding-right",
          "padding-bottom",
          "padding-left",
          "object-fit",
          "object-position",
          "overflow",
          "overflow-x",
          "overflow-y"
        ]
      },
      {
        "groupName": "Typography",
        "emptyLineBefore": "always",
        "noEmptyLineBetween": true,
        "properties": [
          "color",
          "font",
          "font-weight",
          "font-size",
          "font-family",
          "font-style",
          "font-variant",
          "font-size-adjust",
          "font-stretch",
          "font-effect",
          "font-emphasize",
          "font-emphasize-position",
          "font-emphasize-style",
          "font-smooth",
          "line-height",
          "direction",
          "letter-spacing",
          "white-space",
          "text-align",
          "text-align-last",
          "text-transform",
          "text-decoration",
          "text-emphasis",
          "text-emphasis-color",
          "text-emphasis-style",
          "text-emphasis-position",
          "text-indent",
          "text-justify",
          "text-outline",
          "text-wrap",
          "text-overflow",
          "text-overflow-ellipsis",
          "text-overflow-mode",
          "text-orientation",
          "text-shadow",
          "vertical-align",
          "word-wrap",
          "word-break",
          "word-spacing",
          "overflow-wrap",
          "tab-size",
          "hyphens",
          "unicode-bidi",
          "columns",
          "column-count",
          "column-fill",
          "column-gap",
          "column-rule",
          "column-rule-color",
          "column-rule-style",
          "column-rule-width",
          "column-span",
          "column-width",
          "page-break-after",
          "page-break-before",
          "page-break-inside",
          "src"
        ]
      },
      {
        "groupName": "Visual",
        "emptyLineBefore": "always",
        "noEmptyLineBetween": true,
        "properties": [
          "list-style",
          "list-style-position",
          "list-style-type",
          "list-style-image",
          "table-layout",
          "empty-cells",
          "caption-side",
          "background",
          "background-color",
          "background-image",
          "background-repeat",
          "background-position",
          "background-position-x",
          "background-position-y",
          "background-size",
          "background-clip",
          "background-origin",
          "background-attachment",
          "background-blend-mode",
          "outline",
          "outline-width",
          "outline-style",
          "outline-color",
          "outline-offset",
          "box-shadow",
          "box-decoration-break",
          "transform",
          "transform-origin",
          "transform-style",
          "backface-visibility",
          "perspective",
          "perspective-origin",
          "visibility",
          "cursor",
          "opacity",
          "filter",
          "isolation",
          "backdrop-filter",
          "mix-blend-mode",
          "border",
          "border-color",
          "border-style",
          "border-width",
          "border-top",
          "border-top-color",
          "border-top-width",
          "border-top-style",
          "border-right",
          "border-right-color",
          "border-right-width",
          "border-right-style",
          "border-bottom",
          "border-bottom-color",
          "border-bottom-width",
          "border-bottom-style",
          "border-left",
          "border-left-color",
          "border-left-width",
          "border-left-style",
          "border-radius",
          "border-top-left-radius",
          "border-top-right-radius",
          "border-bottom-right-radius",
          "border-bottom-left-radius",
          "border-image",
          "border-image-source",
          "border-image-slice",
          "border-image-width",
          "border-image-outset",
          "border-image-repeat",
          "border-collapse",
          "border-spacing"
        ]
      },
      {
        "groupName": "Animation",
        "emptyLineBefore": "always",
        "noEmptyLineBetween": true,
        "properties": [
          "transition",
          "transition-delay",
          "transition-timing-function",
          "transition-duration",
          "transition-property",
          "animation",
          "animation-name",
          "animation-duration",
          "animation-play-state",
          "animation-timing-function",
          "animation-delay",
          "animation-iteration-count",
          "animation-direction",
          "animation-fill-mode"
        ]
      },
      {
        "groupName": "Misc",
        "emptyLineBefore": "always",
        "noEmptyLineBetween": true,
        "properties": [
          "appearance",
          "content",
          "clip",
          "clip-path",
          "counter-reset",
          "counter-increment",
          "resize",
          "user-select",
          "nav-index",
          "nav-up",
          "nav-right",
          "nav-down",
          "nav-left",
          "pointer-events",
          "quotes",
          "touch-action",
          "will-change",
          "zoom",
          "fill",
          "fill-rule",
          "clip-rule",
          "stroke"
        ]
      }
    ],

    "declaration-property-unit-allowed-list": {
      "/^border/": ["px"],
      "/^padding|^gap/": ["rem"]
    },

    "unit-allowed-list": ["%", "deg", "px", "rem", "ms"],

    "color-named": "never",
    "property-no-vendor-prefix": null
  }
}


설정하던 중 에러를 마주함...



no-missing-end-of-source-newline 에러가 자꾸 나는 것! ㅜㅜ 

스타일드 컴포넌트만 적용하려고 하면 발생하는 에러에 급하게 구글링 시작



Unknow Error은 스타일린트 버전 문제이거나 규칙이 올바르게 작성되지 않아서 발생하는 오류라고 한다. .stylelintrc 내에 no-missing-end-of-source-newline를 작성하지 않았던 터라 오타 문제는 아닐테고... no-missing-end-of-source-newline: null로 추가해 보아도 에러는 그대로였고, 찾아 보니 no-missing-end-of-source-newline는 최신 버전으로 업데이트 되면서 사용되지 않는 규칙으로 확인됐다.


그럼 작성하지도 않은 코드에 대해 왜 에러가 나타나지...?! stylelint-config-styled-components 의 최신 업데이트가 6년 전인데, no-missing-end-of-source-newline는 이보다 최근에 stylelint에서 삭제된 규칙으로 보인다 따라서 두 패키지의 버전 차이로 인한 오류로 판단하고 .stylelintrc에서 stylelint-config-styled-components를 삭제했더니 에러가 말끔히 사라졌다!!



해결 시 참고

https://stackoverflow.com/questions/75346028/unexpected-missing-end-of-source-newline

최신 아티클
lighthouse에 대해
문성희
|
2024.05.13
lighthouse에 대해
lighthouse에 대해
prettier, eslint, styleLint에 대해
이진
|
2024.05.10
prettier, eslint, styleLint에 대해
4주차 공유과제
Article Thumbnail
박채연
|
2024.05.10
Prettier, ESLint, StyleLint
prettier, eslint, stylelint