vite plugin 만들면서 이틀 날렸던 원인

3 minute read
2024-07-17

ast 파싱을 위해 acorn 과 babel 을 계속 사용했다. 근데 문제는 내가 원하는 JSXAttribute 가 확인이 안되는 것이였다.


결론적으로 가장 중요한 설정을 누락해서였다.


NOTE
export function myPlugin(): Plugin {
  return {
    name: 'my-plugin',
    enforce: 'pre',
    // ...
  }
}


다른 플러그인 보다 먼저 처리되도록 우선순위를 높여야 했다.


(하도 안되서 잠시 미쳤었다…)


image-20240717132346744


이젠 제대로 된 설정을 했으니 자세한 코드를 봐보자.


Plugin

vite.config.ts
export default defineConfig({
  plugins: [react(), myPlugin()],
});


vite 플러그인은 기본적으로 왼쪽에서 오른쪽으로 차례대로 실행된다.


transform hook

코드에 대한 접근을 위해서는 transform 훅을 사용해야한다. 이름은 변환과 밀접한 훅이지만, AST 구조체 단위로 접근하기 편리하다.


이처럼 훅 설정을 해준다.


transform hook
export function myPlugin() {
  name: 'my-plugin',
  transform(code: string, id: string) {
    // ...
  },
}


상세 로직

code 는 코드 문자 자체를 가지고 있고 id는 파일의 전체 (절대) 경로를 가지고 있다.


난 여기서 tsx 만 처리하기 위해 아래와 같은 분기를 태웠다.


if (!id.endsWith('.tsx')) {
  return; // 변환 없이 코드 유지
}


이제 핵심 로직이다.


export function findDataGTM(code: string): void {
  const ast: ParseResult<File> = parse(code, {
    sourceType: 'module',
    plugins: ['typescript', 'jsx'],
  });
 
  traverse(ast, {
    JSXAttribute(path: NodePath<JSXAttribute>): void {
      const attrName: string = <string>path.node.name.name;
      if (attrName !== 'data-gtm') {
        return;
      }
      let attrValue = '';
      if (isStringLiteralOfJSXAttr(path)) {
        attrValue = path.node.value.value;
      }
 
      console.log({
        attrName,
        attrValue,
      });
    },
  });
}


여기서 이제 슬랙으로 노티를 준다거나 할 수 있을 듯 하다.


특이사항

babel traverse type error
import _traverse, { type NodePath } from '@babel/traverse';
 
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const a = (_traverse as any).default;
const traverse: typeof _traverse = a;


일단 @babel/traverse 의 타입이 올바르지 않다.


tsconfig.node.json
{
  "compilerOptions": {
    "esModuleInterop": false, // 기본값
  }
}


물론 설정을 참으로 해서