ESLint Custom Rule 만들기

장유진·2024년 3월 27일
2

ESLint

목록 보기
2/2
post-thumbnail

ESLint 공식문서의 Custom Rules 페이지의 일부 내용을 정리해보았다.

규칙을 적용할 Node AST 타입 확인하기

1. Rule Structure

// basic format
module.exports = {
    meta: {
        type: "suggestion",
        docs: {
            description: "Description of the rule",
        },
        fixable: "code",
        schema: [] // no options
    },
    create: function(context) {
        return {
            // callback functions
        };
    }
};
  • meta - 규칙의 metadata를 포함
    • type: string - 규칙의 타입 명시
      • "problem" - 에러나 혼란을 일으킬 수 있는 코드에 대한 규칙
      • "suggestion" - 그대로 두어도 상관없지만 더 좋은 방향으로 수정할 수 있는 코드에 대한 규칙
      • "layout" - 코드의 스타일에 대한 규칙
    • docs: object - Custom rule에서는 사용해도 되고 사용하지 않아도 되는 옵션. 규칙에 대한 정보를 포함함
      • description: string - 규칙에 대한 간략한 설명
      • recommended: boolean - eslint:recommended에 포함되는 규칙일 경우 true
      • url: string - 규칙에 대한 정보가 있는 문서의 URL
    • fixable: string - context.report에서 fix 함수를 사용할 경우 사용하는 옵션
      • "whitespace" - 공백에 관련된 코드를 고침
      • "code" - 그 외 모든 코드를 고침
    • hasSuggestions: boolean - context.report에서 suggestion 함수를 사용할 경우 사용하는 옵션
    • schema: object | array - 유효하지 않은 규칙 설정을 막기 위한 옵션
    • deprecated: boolean
    • replacedBy: array - deprecated되었을 경우 대체하여 사용 가능한 규칙들
  • create(context) - AST를 탐색하여 ESLint가 대상 노드에 접근하는 method를 포함한 객체 리턴
    • key가 node type이나 selector일 경우 해당하는 노드를 위에서 아래로 탐색하는 visitor 함수 호출
    • key가 :exit을 붙인 node type이나 selector일 경우 해당하는 노드를 위에서 아래로 탐색하는 visitor 함수 호출
    • key가 이벤트 이름일 경우 handler 함수 호출

2. Context 객체

context 객체는 create 함수의 유일한 인자이다.

context 객체의 파라미터 (deprecated 제외)

  • id - 규칙의 ID
  • filename - 해당 코드가 위치한 파일의 이름
  • physicalFilename - 해당 코드가 위차한 파일의 전체 경로
  • cwd - current working directory 경로
  • options - 이 규칙을 설정하는 데 사용된 옵션들
  • sourcecode - 말 그대로 소스 코드
  • settings - config에서 공유된 setting 목록
  • parserPath - config에 설정된 parser의 이름
  • parserOptions - config에 설정돈 parserOptions

context 객체의 method (deprecated 제외)

  • report(descriptor) - 코드의 문제 보고

3. context.report() 사용하기

report 함수는 아래 속성을 포함하는 하나의 객체를 인자로 받는다. node 또는 loc 둘 중에 하나는 반드시 지정되어야 한다.

  • message - 문제에 대한 간략한 설명
  • node - 문제와 관련된 AST 노드. loc이 지정되지 않았다면 이 노드의 시작 위치가 문제의 시작 위치이다.
  • loc - 문제의 위치를 지정한다. loc과 node가 둘 다 지정되었다면 loc이 우선시된다.
    • start: 시작 위치
      • line
      • column
    • end: 끝 위치
      • line
      • column
  • data - message에서 사용할 변수들
  • fix(fixer) - 문제를 고치는 함수

3-1. Message placeholder

data에 설정된 변수(placeholder)를 사용하여 message를 작성할 수 있다. message의 string 안에 {{변수}} 형태로 사용해주면 된다.

context.report({
    node: node,
    message: "Unexpected identifier: {{ identifier }}",
    data: {
        identifier: node.name
    }
});

3-2. messageId

meta에 설정된 messages를 사용하여 message를 작성할 수 있다. messageId에 해당하는 message를 지정해주면 된다.

module.exports = {
    meta: {
        messages: {
            avoidName: "Avoid using variables named '{{ name }}'"
        }
    },
    create(context) {
        return {
            Identifier(node) {
                if (node.name === "foo") {
                    context.report({
                        node,
                        messageId: "avoidName",
                        data: {
                            name: "foo",
                        }
                    });
                }
            }
        };
    }
};

3-3. fix 함수 적용하기

잘못 작성된 코드를 자동으로 수정하는 기능이다.
반드시 meta.fixable 프로퍼티가 설정되어야 fix 함수를 사용할 수 있다.
fix 함수는 fixer 객체를 인자로 받는다.

fixer 객체의 method

  • insertTextAfter(nodeOrToken, text)
  • insertTextAfterRange([start, end), text)
  • insertTextBefore(nodeOrToken, text)
  • insertTextBeforeRange([start, end), text)
  • remove(nodeOrToken)
  • removeRange([start, end))
  • replaceText(nodeOrToken, text)
  • replaceTextRange([start, end), text)

위 메서드들은 fixing 객체를 반환하며, fix 함수는 아래 항목들을 리턴할 수 있다.

  • fixing 객체
  • fixing 객체를 담은 배열
  • fixing 객체를 열거하는 iterable 객체. 이 경우에 fix 함수는 generator 함수가 될 수 있다.
fix(fixer) {
    return fixer.insertTextAfter(node, ";");
}
 
*fix(fixer) {
    yield fixer.replaceText(node, replacementText);
 
    // extend range of the fix to the range of `node.parent`
    yield fixer.insertTextBefore(node.parent, "");
    yield fixer.insertTextAfter(node.parent, "");
}

3-4. suggestion 적용하기

fix가 기능을 변경할 가능성이 있거나 규칙을 수정할 다양한 방법이 있다면 fix 대신에 suggestion을 사용할 수 있다.

반드시 meta.hasSuggestions 프로퍼티가 설정되어야 suggest 옵션을 사용할 수 있다.

report 객체에 여러 개의 suggestion 객체를 포함한 배열인 suggest 옵션을 추가해준다. 각 suggestion 객체는 desc나 messageId 둘 중에 하나와 fix key를 가진다.

context.report({
    node: node,
    message: "Unnecessary escape character: \\{{character}}.",
    data: { character },
    suggest: [
        {
            desc: "Remove the `\\`. This maintains the current functionality.",
            fix: function(fixer) {
                return fixer.removeRange(range);
            }
        },
        {
            desc: "Replace the `\\` with `\\\\` to include the actual backslash character.",
            fix: function(fixer) {
                return fixer.insertTextBeforeRange(range, "\\");
            }
        }
    ]
});

4. Options Schema

schema 객체를 사용하여 규칙의 config option 구조를 설정할 수 있다.

// Valid configuration:
// "yoda": "warn"
// "yoda": ["error"]
// "yoda": ["error", "always"]
// "yoda": ["error", "never", { "exceptRange": true }]
// Invalid configuration:
// "yoda": ["warn", "never", { "exceptRange": true }, 5]
// "yoda": ["error", { "exceptRange": true }, "never"]
module.exports = {
    meta: {
        schema: [
            {
                enum: ["always", "never"]
            },
            {
                type: "object",
                properties: {
                    exceptRange: { type: "boolean" }
                    hello: {type: "string"}
                },
                required: ["exceptRange"],
                additionalProperties: false
            }
        ]
    }
};
profile
프론트엔드 개발자

0개의 댓글