编写你自己的代码转换的乐趣和利润

51 阅读3分钟

如果你还没有听说过。 babel-plugin-macros"启用零配置、可导入的babel插件"。几个月前,我在babel官方博客上发表了一篇关于它的博文。"使用babel-plugin-macros的零配置代码转换"

从那时起,就有了一些令人兴奋的发展。

到目前为止,虽然有相当多的人在使用越来越多的现有宏,但只有早期采用者才尝试编写宏。你可以用babel-plugin-macros ,有很多了不起的事情可以做,我想用这篇简讯来告诉你如何开始玩写自己的宏。

让我们从一个精心设计的宏开始,它可以分割一串文本,并用🐶 替换每个空格。我们把它叫做gemmafy ,因为我的狗的名字是 "Gemma"。汪!

  1. 转到astexplorer.net
  2. 确保语言被设置为JavaScript
  3. 确保分析器被设置为babylon7
  4. 启用转换,并将其设置为babel-macros (或在合并后尽快设置为babel-plugin-macros )。

然后在源码(左上角)代码面板中复制/粘贴这个:

import gemmafy from 'gemmafy.macro'

console.log(gemmafy('hello world'))

并在转换(左下角)代码面板中复制/粘贴这个:

module.exports = createMacro(gemmafyMacro)

function gemmafyMacro({references, state, babel}) {
  references.default.forEach(referencePath => {
    const [firstArgumentPath] = referencePath.parentPath.get('arguments')
    const stringValue = firstArgumentPath.node.value
    const gemmafied = stringValue.split(' ').join(' 🐶 ')
    const gemmafyFunctionCallPath = firstArgumentPath.parentPath
    const gemmafiedStringLiteralNode = babel.types.stringLiteral(gemmafied)
    gemmafyFunctionCallPath.replaceWith(gemmafiedStringLiteralNode)
  })
}

或者,你也可以打开这个

TADA 🎉!你已经通过一个宏编写了你的(可能是)第一个babel插件

这是你应该看到的输出(在右下角面板):

console.log('hello 🐶 world')

你会注意到,babel-plugin-macros 会帮你删除文件顶部的导入,而我们的宏用字符串替换了gemmafy 的调用。

所以这是你的挑战,尝试添加这个:

console.log(gemmafy('hello world', 'world goodbye'))

现在,这将转译为:

console.log('hello 🐶 world')

你的任务是让它改成这样:

console.log('hello 🐶 world', 'goodbye 🐶 world')

从这里开始,你可以用它来做很多有趣的事情。

如果你想看到更多的功能,那么在源码中复制这个(左上角):

import myMacro, {JSXMacro} from 'AnyNameThatEndsIn.macro'
// (note: in reality, the AnyNameThatEndsIn.macro should be the name of your package
// for example: `codegen.macro`)
const functionCall = myMacro('Awesome')
const jsx = Hi!
const templateLiteral = myMacro`hi ${'there'}`
literallyAnythingWorks(myMacro)

并在转换(左下)代码面板中复制/粘贴这个:

module.exports = createMacro(myMacro)

function myMacro({references, state, babel}) {
  // `state` is the second argument you're passed to a visitor in a
  // normal babel plugin. `babel` is the `@babel/core` module.
  // do whatever you like to the AST paths you find in `references`.
  // open up the console to see what's logged and start playing around!

  // references.default refers to the default import (`myMacro` above)
  // references.JSXMacro refers to the named import of `JSXMacro`
  const {JSXMacro = [], default: defaultImport = []} = references

  defaultImport.forEach(referencePath => {
    if (referencePath.parentPath.type === 'TaggedTemplateExpression') {
      console.log(
        'template literal contents',
        referencePath.parentPath.get('quasi'),
      )
    } else if (referencePath.parentPath.type === 'CallExpression') {
      if (referencePath === referencePath.parentPath.get('callee')) {
        console.log(
          'function call arguments (as callee)',
          referencePath.parentPath.get('arguments'),
        )
      } else if (
        referencePath.parentPath.get('arguments').includes(referencePath)
      ) {
        console.log(
          'function call arguments (as argument)',
          referencePath.parentPath.get('arguments'),
        )
      }
    } else {
      // throw a helpful error message or something :)
    }
  })

  JSXMacro.forEach(referencePath => {
    if (referencePath.parentPath.type === 'JSXOpeningElement') {
      console.log('jsx props', {
        attributes: referencePath.parentPath.get('attributes'),
        children: referencePath.parentPath.parentPath.get('children'),
      })
    } else {
      // throw a helpful error message or something :)
    }
  })
}

接下来,打开你的开发者控制台,看看控制台的日志。玩得开心点!

或者,你可以直接去这里

我认为我们可以利用这项技术去很多非常酷的地方。我没有在这篇简讯中花任何时间谈论宏背后的原因或给你一些想法。下面我将链接到一些想法的资源。基本的想法是,如果有一种方法可以预先编译你的一些操作,那么你就可以改善你的应用程序的运行时性能/捆绑大小。此外,这允许你在构建时做一些事情,因为你可以访问文件系统。这种可能性真的是无穷无尽的,而我们才刚刚开始呢好好享受吧!