간단한 babel 매크로 작성 방법

12064 단어 babelmacrojavascript
매크로는 트랜스파일(컴파일) 시 애플리케이션의 소스 코드를 조작하기 위해 작성할 수 있는 작은 프로그램입니다. 컴파일러가 작동하는 방식을 조정하는 방법으로 생각하십시오.
babel-plugin-macros는 JavaScript(또는 Flow)용 매크로를 작성하기 위한 babel용 플러그인입니다. 여기서 중요한 부분은 babel-plugin-macros가 포함되자마자 매크로를 사용하기 위해 babel 구성을 건드릴 필요가 없다는 것입니다(다른 babel 플러그인과 달리). 이것은 Create React App과 같은 잠긴 설정에서 매우 유용합니다. 또한 명시적이라는 점이 마음에 듭니다. 매크로가 사용되는 위치를 명확하게 볼 수 있습니다.



매크로로 쉽게 풀 수 있는 장난감 크기 문제를 가져왔습니다.

Webpack에서 동적import을 사용하면 1.chunk.js , 2.chunk.js 와 같이 청크에 대해 읽기 어려운 이름을 생성합니다(적어도 CRA에서 수행하는 작업입니다). 이 문제를 해결하기 위해 매직 주석 /* webpackChunkName: MyComponent */ 을 사용할 수 있으므로 MyComponent.chunk.js 를 얻을 수 있지만 매번 이 주석을 손으로 두는 것은 성가신 일입니다. 이것을 수정하기 위해 정확히 babel 매크로를 작성해봅시다.

우리는 다음과 같은 코드를 원합니다:

import wcImport from "webpack-comment-import.macro";

const asyncModule = wcImport("./MyComponent");


로 변환하려면

const asyncModule = import(/* webpackChunkName: MyComponent */ "./MyComponent");


상용구



그래서 바로 코딩으로 넘어가고 싶기 때문에 상용구에 시간을 들이지 않겠습니다. 초기 코드를 볼 수 있는 tag boilerplate GitHub 리포지토리가 있습니다.

export default createMacro(webpackCommentImportMacros);
function webpackCommentImportMacros({ references, state, babel }) {
  // lets walk through all calls of the macro
  references.default.map(referencePath => {
    // check if it is call expression e.g. someFunction("blah-blah")
    if (referencePath.parentPath.type === "CallExpression") {
      // call our macro
      requireWebpackCommentImport({ referencePath, state, babel });
    } else {
      // fail otherwise
      throw new Error(
        `This is not supported: \`${referencePath
          .findParent(babel.types.isExpression)
          .getSource()}\`. Please see the webpack-comment-import.macro documentation`,
      );
    }
  });
}
function requireWebpackCommentImport({ referencePath, state, babel }) {
  // Our macro which we need to implement
}


테스트 및 빌드 스크립트도 구성되어 있습니다. 처음부터 쓴게 아닙니다. raw.macro 에서 복사했습니다.

코딩하자



우선 babel.types 을 얻습니다. 여기에 거래가 있습니다. 매크로로 작업할 때 주로 하는 것은 AST(소스 코드 표현)를 조작하는 것이며 babel.types에는 babel AST에서 사용되는 가능한 모든 유형의 표현식에 대한 참조가 포함되어 있습니다. babel.types readme babel AST로 작업하려는 경우 가장 유용한 참조입니다.

function requireWebpackCommentImport({ referencePath, state, babel }) {
  const t = babel.types;

referencePathwcImport 에서 const asyncModule = wcImport("./MyComponent"); 이므로 레벨을 더 높여야 합니다. wcImport("./MyComponent") .

  const callExpressionPath = referencePath.parentPath;
  let webpackCommentImportPath;


이제 우리는 우리 함수가 호출된 인수를 얻을 수 있습니다. 재미있는 일이 일어나지 않도록 하기 위해 try/catch 를 사용합시다. 함수 호출의 첫 번째 인수는 가져오기 경로로 가정합니다. "./MyComponent" .

  try {
    webpackCommentImportPath = callExpressionPath.get("arguments")[0].evaluate()
      .value;
  } catch (err) {
    // swallow error, print better error below
  }

  if (webpackCommentImportPath === undefined) {
    throw new Error(
      `There was a problem evaluating the value of the argument for the code: ${callExpressionPath.getSource()}. ` +
        `If the value is dynamic, please make sure that its value is statically deterministic.`,
    );
  }


마지막으로 AST 조작 - wcImport("./MyComponent")import("./MyComponent");로 바꾸자.

  referencePath.parentPath.replaceWith(
    t.callExpression(t.identifier("import"), [
      t.stringLiteral(webpackCommentImportPath),
    ]),
  );


경로의 마지막 부분을 구합시다. a/b/cc로 변환합니다.

  const webpackCommentImportPathParts = webpackCommentImportPath.split("/");
  const identifier =
    webpackCommentImportPathParts[webpackCommentImportPathParts.length - 1];


그리고 가져오기의 첫 번째 인수 앞에 매직 구성 요소를 넣습니다.

  referencePath.parentPath
    .get("arguments")[0]
    .addComment("leading", ` webpackChunkName: ${identifier} `);
}


그리고 이것이다. 나는 그것을 짧게 유지하려고 노력했다. 나는 많은 세부 사항에 뛰어 들지 않았고 질문을하십시오.

추신



Babel 문서는 약간 어렵습니다. 가장 쉬운 방법은 다음과 같습니다.
  • console.log(referencePath.parentPath.type)로 표현식의 유형을 검사하고 babel.types에서 이에 대해 읽으십시오.
  • 비슷한 일을 하는 다른 babel-plugin의 소스코드를 읽어라

  • 전체 소스 코드는 here

    도움이 되기를 바랍니다. 시도 해봐. 어떻게 되는지 알려주세요. 또는 단순히 babel 매크로에 대한 아이디어를 공유하십시오.

    github에서 나를 따르십시오.

    좋은 웹페이지 즐겨찾기