Just some notes on macros in the JavaScript ecosystem.
- Babel
- Babel plugins basically function as macros, as compile-time transformations. They are also capable of doing literally anything: JSX, TypeScript, and new JS syntaxes all have implementations as Babel plugins. Creating and maintaining a plugin for each macro is cumbersome, though: it's not at all as simple as a
defmacro
. - babel-plugin-macros
- Appears to be adopted widely. Macros are modules exporting a main compile-time function - this function receives some compilation state and writes into that state.
- Sweet.js
- Compile-time functions returning syntax objects. Can implement new constructs like cond-case. Can define new operators. Has a cli for transpilation as well as a Babel backend.
- Bun macros
- Compile-time (bundle-time) functions. Written as normal functions marked as macros at import. Cannot alter syntax or do metaprogramming - return value must be serializable and cannot be functions.
- decky
- Bun also supports esbuild plugins that only use the onLoad and onResolve hooks. This means it probably also supports decky, which uses decorator syntax for compile-time functions. They receive a custom AST and return a string.
- Parcel macros
- Almost exactly like Bun macros, except (a) macros can generate assets and tell Parcel about them and (b) they can return functions, albeit only those created with the Function constructor; and a few other minor differences.
2025-03-09T21:12:36+0900: Gotta love it when I find an old blog post sitting uncommitted in an old checkout that I'm only coming back to now. This is a reason why I cannot use LLMs: I can't know what I wrote and what I didn't write otherwise.