use-macro
Version:
ESBuild plugin that allows you to execute functions at compile time and inline the output
182 lines • 16.1 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { runInNewContext } from 'vm';
import * as ts from 'typescript';
import { dirname } from 'path';
import { createRequire } from 'module';
import { parse } from '@babel/parser';
import _traverse from '@babel/traverse';
import * as t from '@babel/types';
import _generate from '@babel/generator';
import { stringify } from './stringify.js';
const traverse = _traverse.default;
const generate = _generate.default;
// Helper to get parser options based on file extension
const getParserOptions = (filepath) => {
const isTS = /\.tsx?$/.test(filepath);
const isJSX = /\.jsx$/.test(filepath) || /\.tsx$/.test(filepath);
const isModule = /^m/.test(filepath);
return {
sourceType: isModule ? 'module' : 'unambiguous',
plugins: [
...(isTS ? ['typescript'] : []),
...(isJSX ? ['jsx'] : []),
'decorators-legacy',
'classProperties',
'optionalChaining',
'nullishCoalescingOperator',
],
};
};
// Helper to transpile TypeScript to JavaScript
const transpileTypeScript = (code, filePath) => {
const result = ts.transpileModule(code, {
compilerOptions: {
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ESNext,
},
fileName: filePath,
});
return result.outputText;
};
// Helper to execute macro at compile time
const executeMacro = (macroFunction) => {
var _a, _b;
const macroCode = generate(macroFunction.ast).code;
const transpiledCode = /\.(m|c|d)?tsx?$/.test(macroFunction.path)
? transpileTypeScript(macroCode, macroFunction.path)
: macroCode;
const macroModule = `
${transpiledCode}
// do not export this as it is evaluated in vm which directly returns the result
${(_a = macroFunction.ast.id) === null || _a === void 0 ? void 0 : _a.name}(${macroFunction.args.join(', ')})`;
const result = runInNewContext(`${macroModule}`, Object.assign(Object.assign({}, Object.fromEntries(Object.entries(globalThis))), { process,
Buffer,
URL,
URLSearchParams,
fetch,
Response,
Headers,
Request,
FormData,
console,
setTimeout,
clearTimeout,
setInterval,
clearInterval,
setImmediate,
AbortController,
AbortSignal, __dirname: dirname(macroFunction.path), __filename: macroFunction.path, require: createRequire(macroFunction.path), import: (modulePath) => import(modulePath), isMacroContext: true }));
return (_b = result === null || result === void 0 ? void 0 : result.default) !== null && _b !== void 0 ? _b : result;
};
export class MacroTransformer {
constructor() {
this.cache = new Map();
this.functions = new Map();
}
transform(contents, filePath) {
return __awaiter(this, void 0, void 0, function* () {
const macroFunctions = this.functions;
const macroCache = this.cache;
const ast = parse(contents, getParserOptions(filePath));
const state = {
modifications: [],
macrosToRemove: new Set(),
macrosToExecute: [],
};
const runMacro = (name, fn) => __awaiter(this, void 0, void 0, function* () {
if (macroCache.has(name)) {
return macroCache.get(name);
}
const result = yield executeMacro(fn);
const stringifiedResult = yield stringify(result);
macroCache.set(name, stringifiedResult);
return stringifiedResult;
});
traverse(ast, {
'FunctionDeclaration|FunctionExpression|ArrowFunctionExpression'(path) {
var _a, _b;
const node = path.node;
if (!t.isFunction(node))
return;
const body = node.body;
if (!t.isBlockStatement(body))
return;
if (body.directives) {
const directive = body.directives.find((d) => d.value.value === 'use macro');
if (directive) {
let functionName;
if (t.isFunctionDeclaration(node) && node.id) {
functionName = node.id.name;
}
else if (path.parent &&
t.isVariableDeclarator(path.parent) &&
t.isIdentifier(path.parent.id)) {
functionName = path.parent.id.name;
}
if (!functionName) {
const err = new Error(`Macro function at at ${filePath}:${(_a = node.loc) === null || _a === void 0 ? void 0 : _a.start.line}:${(_b = node.loc) === null || _b === void 0 ? void 0 : _b.start.column} must have a name. If you are using an IIFE, it is not supported.`);
throw err;
}
const macroFunction = {
ast: t.isFunctionDeclaration(node)
? node
: t.functionDeclaration(t.identifier(functionName),
// @ts-ignore
node.params, body),
path: filePath,
args: node.params,
};
macroFunctions.set(functionName, macroFunction);
// Add to removal queue
const nodeToRemove = t.isFunctionDeclaration(node)
? path
: path.parentPath;
if (nodeToRemove) {
state.macrosToRemove.add(nodeToRemove);
}
}
}
},
CallExpression(path) {
if (t.isIdentifier(path.node.callee)) {
const functionName = path.node.callee.name;
const fn = macroFunctions.get(functionName);
if (!fn)
return;
state.modifications.push(() => __awaiter(this, void 0, void 0, function* () {
const value = yield runMacro(functionName, fn);
const newNode = t.identifier(value);
t.addComment(newNode, 'leading', ` @__MACRO__ ${functionName} `);
path.replaceWith(newNode);
}));
}
},
});
// Apply all modifications in a single pass
for (const modify of state.modifications) {
yield modify();
}
// Remove all macro definitions
state.macrosToRemove.forEach((path) => path.remove());
const { code } = generate(ast, {
retainLines: true,
compact: false,
sourceMaps: true,
sourceFileName: filePath,
});
return {
contents: code,
loader: filePath.split('.').pop(),
};
});
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJhbnNmb3JtZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvdHJhbnNmb3JtZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBQUEsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLElBQUksQ0FBQztBQUNyQyxPQUFPLEtBQUssRUFBRSxNQUFNLFlBQVksQ0FBQztBQUNqQyxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQy9CLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFFdkMsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN0QyxPQUFPLFNBQVMsTUFBTSxpQkFBaUIsQ0FBQztBQUN4QyxPQUFPLEtBQUssQ0FBQyxNQUFNLGNBQWMsQ0FBQztBQUNsQyxPQUFPLFNBQVMsTUFBTSxrQkFBa0IsQ0FBQztBQUd6QyxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFVM0MsTUFBTSxRQUFRLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQztBQUNuQyxNQUFNLFFBQVEsR0FBRyxTQUFTLENBQUMsT0FBTyxDQUFDO0FBRW5DLHVEQUF1RDtBQUN2RCxNQUFNLGdCQUFnQixHQUFHLENBQUMsUUFBZ0IsRUFBRSxFQUFFO0lBQzVDLE1BQU0sSUFBSSxHQUFHLFNBQVMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDdEMsTUFBTSxLQUFLLEdBQUcsUUFBUSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxRQUFRLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ2pFLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7SUFFckMsT0FBTztRQUNMLFVBQVUsRUFBRSxRQUFRLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsYUFBYTtRQUMvQyxPQUFPLEVBQUU7WUFDUCxH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