AST를 이용한 정적 소스 코드 분석 / 트랜스파일링 개념을 이해해 봅니다.
npm에 이보다 더 좋은 라이브러리들이 많음으로 활용하시기 바랍니다.
해당 소스 및 설명은 : https://github.com/kentcdodds/asts-workshop\ 에서 발췌했습니다.
GitHub - kentcdodds/asts-workshop: Improved productivity 💯 with the practical 🤓 use of the power 💪 of Abstract Syntax T
Improved productivity 💯 with the practical 🤓 use of the power 💪 of Abstract Syntax Trees 🌳 to lint ⚠️ and transform 🔀 your code - GitHub - kentcdodds/asts-workshop: Improved productivity 💯 with the...
github.com
아래 글을 읽기 전에 확인해 주세요
[Babel] 바벨 플러그인을 작성하며 AST 배우기
바벨 플러그인을 만들어보며 JS AST(Abstract syntax trees)를 배워봅시다. 해당 글의 번역입니다 : https://dev.to/viveknayyar/revealing-the-magic-of-ast-by-writing-babel-plugins-1h01 Revealing the magic..
itchallenger.tistory.com
AST의 다양한 활용사례
AST : code for computer
- js 뿐만 아니라 graphql, 정규 표현식 등도 json 구조로 분석 가능.
- 파서, 목적에 따라 다양한 구조로 나타날 수 있음
https://resources.jointjs.com/demos/rappid/apps/Ast/index.html
const a = 1;
바벨 : AST를 조작해 다른 AST를 만드는 툴
- 토크나이저(tokenizer)는 일반적으로 공백(탭, 공백, 새 줄)을 찾아 텍스트 스트림을 토큰으로 나눕니다.
- 어휘분석기(lexer)는 기본적으로 토크나이저이지만 일반적으로 토큰에 추가 컨텍스트를 첨부합니다.
- 이 토큰은 숫자이고, 해당 토큰은 문자열 리터럴이며, 다른 토큰은 등호 연산자입니다.
- 파서(parser)는 렉서에서 토큰 스트림을 가져와
- 원본 텍스트가 설명하는 프로그램을 나타내는 추상 구문 트리로 바꿉니다.
- ast가 아닌 다른 출력도 만들 수 있습니다.
Looking for a clear definition of what a "tokenizer", "parser" and "lexers" are and how they are related to each other and used?
I am looking for a clear definition of what a "tokenizer", "parser" and "lexer" are and how they are related to each other (e.g., does a parser use a tokenizer or vice versa)? I need to create a pr...
stackoverflow.com
바벨 플러그인의 다양한 기능
- 트리 셰이킹 (체리피킹)
http://slides.com/kentcdodds/a-beginners-guide-to-asts#/3/2
Writing custom Babel and ESLint plugins
The Abstract Syntax Tree. It sounds a lot worse than it is. It’s actually quite simple and enables some powerful tools. BabelJS uses it to transform your code from ES.Next to ES5. ESLint uses it to lint your code. And with a knowledge of how it works, yo
slides.com
ex) : 로다시를 자동으로 체리피킹 : babel-plugin-lodash
ex) : prop-types 자동 제거 : babel-plugin-transform-react-remove-prop-types
ex) : babel-plugin-preval : 런타임이 아닌 빌드 타임에 해당 값을 평가
https://github.com/kentcdodds/babel-plugin-preval
const x = preval`module.exports = 1`
// ↓ ↓ ↓ ↓ ↓ ↓
const x = 1
const x = preval`
const fs = require('fs')
const val = fs.readFileSync(__dirname + '/fixture1.md', 'utf8')
module.exports = {
val,
getSplit: function(splitDelimiter) {
return x.val.split(splitDelimiter)
}
}
`
// ↓ ↓ ↓ ↓ ↓ ↓
const x = {
val: '# fixture\n\nThis is some file thing...\n',
getSplit: function getSplit(splitDelimiter) {
return x.val.split(splitDelimiter)
},
}
ex) : babel-plugin-module-alias : import 경로를 바꿔줌
ESLINT
정적 자바스크립트 분석 도구
문법적 컨벤션, 잠재적인 오류 발견
- eslint-plugin-import
- eslint-plugin-react
- eslint-plugin-promise
- eslint-plugin-secrity
- eslint-plugin-jsx-a11y
Codemons
super awesome find and replace
특정 코드 구문을 찾아 변경해줌
- react-codemon
- 리액트 자동 업데이트 도구
- 5to6-codemon
- ava-codemons
- ava 테스트 프레임워크를 jest 프레임워크로 변경해줌
- jest-codemons
ESLINT 플러그인 개발하기
1. 직접 개발한 플러그인을 프로젝트에 적용하는 것은 .eslintrc 설정 방법을 확인하면 됩니다.
2. AST Explorer(https://astexplorer.net/)를 켜고 다음과 같이 설정합니다.
- 파서 : espree
- 트랜스폼 :
- Eslint v8
3. 예제 코드를 아래와 같이 입력합니다.
var csl = console
csl.log()
var lcs = csl
lcs.info()
var scl = lcs
scl.warn()
console.log();
console.warn();
4. 아래 코드를 입력해봅니다.
아래 코드가 이해되지 않는 분들은 바벨과 함께 AST 배우기 글을 참조해 주세요.
meta 규칙은 아래 문서를 참고합니다.
https://eslint.org/docs/latest/developer-guide/working-with-rules
Working with Rules - ESLint - Pluggable JavaScript Linter
A pluggable and configurable linter tool for identifying and reporting on patterns in JavaScript. Maintain your code quality with ease.
eslint.org
meta 부분과, identifier의 참조 관계(VariableDeclarator > binding)를 exit에서 처리하는 부분을 제외하면
나머지는 babel 개발 시 visitor 패턴을 사용하는 부분과 매우 유사합니다.
const disallowedMethods = ['log', 'info', 'warn', 'error', 'dir']
module.exports = {
// 메타 설정에 관한 자세한 정보는
// https://eslint.org/docs/latest/developer-guide/working-with-rules 를 참고
meta: {
docs: {
description: 'Disallow use of console',
category: 'Best Practices',
recommended: true,
},
// context.option을 통해 접근 가능
schema: [
{
type: 'object',
properties: {
allowedMethods: {
type: 'array',
items: {
enum: ['log', 'info', 'warn', 'error', 'dir'],
},
minItems: 1,
uniqueItems: true,
},
},
},
],
},
create(context) {
const config = context.options[0] || {}
const allowedMethods = config.allowedMethods || []
const consoleUsage = []
return {
Identifier(node) {
if (node.name !== 'console') {
return
}
consoleUsage.push(node)
},
// identifier 참조 관계는 모든 visitor 처리 완료 후 형성됩니다.
// 따라서 중복 레퍼런스는 해당 부분에서 처리합니다.
'Program:exit'() {
consoleUsage.forEach(identifier => {
if (isDisallowedFunctionCall(identifier)) {
context.report({
node: identifier.parent.property,
message: 'Using console is not allowed',
})
} else {
const variableDeclaratorParent = findParent(
identifier,
parent => parent.type === 'VariableDeclarator',
)
if (variableDeclaratorParent) {
const references = context
.getDeclaredVariables(variableDeclaratorParent)[0]
.references.slice(1)
references.forEach(reference => {
if (
!looksLike(reference, {
identifier: {
parent: {
property: isDisallowedFunctionCall,
},
},
})
) {
return
}
context.report({
node: reference.identifier.parent.property,
message: 'Using console is not allowed',
})
})
}
}
})
},
}
function isDisallowedFunctionCall(identifier) {
return looksLike(identifier, {
parent: {
type: 'MemberExpression',
parent: {type: 'CallExpression'},
property: {
name: val =>
!allowedMethods.includes(val) && disallowedMethods.includes(val),
},
},
})
}
},
}
function findParent(node, test) {
if (test(node)) {
return node
} else if (node.parent) {
return findParent(node.parent, test)
}
return null
}
function looksLike(a, b) {
return (
a &&
b &&
Object.keys(b).every(bKey => {
const bVal = b[bKey]
const aVal = a[bKey]
if (typeof bVal === 'function') {
return bVal(aVal)
}
return isPrimitive(bVal) ? bVal === aVal : looksLike(aVal, bVal)
})
)
}
function isPrimitive(val) {
return val == null || /^[sbn]/.test(typeof val)
}
화면은 다음과 같이 됩니다.
context.report객체를 사용하면, 해당 코드를 삭제하거나 수정하는 것도 가능합니다만, 이는 나중에 기회가 되면 다루어보도록 하겠습니다.
참고
http://slides.com/kentcdodds/a-beginners-guide-to-asts#/6
'FrontEnd' 카테고리의 다른 글
소프트웨어 합성 : 트랜스듀서(Transducers) (0) | 2022.09.20 |
---|---|
소프트웨어 합성 : 리듀서(reducer) (0) | 2022.09.20 |
[React] React.cloneElement 사용 사례 (0) | 2022.09.15 |
리액트 디자인 패턴 : 컴파운드 컴포넌트 패턴 [Compound Component Pattern] 2 (0) | 2022.09.15 |
[Babel] 바벨 플러그인을 작성하며 AST 배우기 (0) | 2022.09.14 |