Babel plugin that enables compile-time code transformation through macros with a standardized interface
npx @tessl/cli install tessl/npm-babel-plugin-macros@3.1.0babel-plugin-macros is a Babel plugin that enables compile-time code transformation through macros with a standardized interface. It allows library authors to create compile-time transformations without requiring users to configure individual Babel plugins, providing a unified macro system for the JavaScript ecosystem.
npm install --save-dev babel-plugin-macrosFor macro authors creating macros:
const { createMacro, MacroError } = require("babel-plugin-macros");For Babel configuration (users installing the plugin):
// babel.config.js
module.exports = {
plugins: ["macros"]
};For macro consumers (importing and using macros):
// ES6 imports
import myMacro from "some-library.macro";
import { namedMacro } from "some-library.macro";
// CommonJS requires
const myMacro = require("some-library.macro");
const { namedMacro } = require("some-library.macro");const { createMacro } = require("babel-plugin-macros");
module.exports = createMacro(myMacro);
function myMacro({ references, state, babel }) {
// Transform the AST using the references
references.default.forEach(referencePath => {
// Perform compile-time transformation
referencePath.replaceWithSourceString('"transformed at compile time"');
});
}import transform from "./my.macro";
// This gets transformed at compile time
const result = transform("some input");Add to .babelrc or babel.config.js:
{
"plugins": ["macros"]
}By default, macro import/require statements are removed after macro processing:
import myMacro from "./my.macro";
// This import will be removed after macro processingMacros can return {keepImports: true} to preserve the import statement:
// In your macro implementation
function myMacro({ references }) {
// Process the macro...
// Return this to keep the import statement
return { keepImports: true };
}This is useful for macros that need to leave runtime code in place alongside their compile-time transformations.
Creates a macro function with proper error handling and validation.
function createMacro(macro: MacroFunction, options?: MacroOptions): WrappedMacro;
interface MacroOptions {
configName?: string;
}
interface MacroFunction {
(args: MacroArgs): MacroResult | undefined;
}
interface MacroArgs {
references: { [importName: string]: NodePath[] };
source: string;
state: BabelState;
babel: BabelTypes;
config: any;
isBabelMacrosCall: boolean;
}
interface MacroResult {
keepImports?: boolean;
}
interface WrappedMacro {
(args: MacroArgs): MacroResult | undefined;
isBabelMacro: true;
options: MacroOptions;
}Custom error class for macro-specific errors with proper stack traces.
class MacroError extends Error {
constructor(message: string);
name: "MacroError";
}Main Babel plugin function that processes macro imports and requires.
function macrosPlugin(babel: BabelCore, options?: PluginOptions): BabelPlugin;
interface PluginOptions {
require?: (path: string) => any;
resolvePath?: (source: string, basedir: string) => string;
isMacrosName?: (name: string) => boolean;
[configName: string]: any;
}
interface BabelPlugin {
name: "macros";
visitor: {
Program: (path: NodePath, state: BabelState) => void;
};
}babel-plugin-macros supports configuration through multiple methods:
package.json with babelMacros property.babel-plugin-macrosrc (JSON or YAML).babel-plugin-macrosrc.json.babel-plugin-macrosrc.yaml.babel-plugin-macrosrc.yml.babel-plugin-macrosrc.jsbabel-plugin-macros.config.jsConfiguration can be passed through Babel plugin options:
// babel.config.js
module.exports = {
plugins: [
["macros", {
myMacroConfig: {
option1: "value1",
option2: "value2"
}
}]
]
};Macros can access their configuration through the config parameter:
function myMacro({ config }) {
// Access configuration specific to this macro
const options = config || {};
}
module.exports = createMacro(myMacro, { configName: "myMacroConfig" });For a file to be recognized as a macro, it must match the pattern:
const macrosRegex = /[./]macro(\.c?js)?$/;Valid macro names:
my.macromy.macro.jsmy.macro.cjsmy/macromy/macro.jsmy/macro.cjsmy/macro/index.js (imported as my/macro)Invalid macro names:
my-macromy.macro.is-sweetmy/macro/rocksThe plugin resolves macros with the following extensions:
.js.ts.tsx.mjs.cjs.jsximport macro from "my.macro"; // Default import
import { namedMacro } from "my.macro"; // Named import
import macro, { namedMacro } from "my.macro"; // Mixed importconst macro = require("my.macro"); // Default require
const { namedMacro } = require("my.macro"); // Destructured requireinterface BabelState {
file: {
opts: {
filename?: string;
};
scope: any;
};
}
interface BabelCore {
types: BabelTypes;
template: any;
traverse: any;
}
interface BabelTypes {
// Babel types object with AST node constructors and utilities
[key: string]: any;
}
interface NodePath {
node: any;
scope: any;
remove(): void;
replaceWith(node: any): void;
replaceWithSourceString(code: string): void;
// Additional Babel NodePath methods
}