directive-to-hof
Version:
Transform directives into a higher order function
183 lines • 16.2 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createDirectiveTransformer = createDirectiveTransformer;
const parser = __importStar(require("@babel/parser"));
const traverse_1 = __importDefault(require("@babel/traverse"));
const generator_1 = __importDefault(require("@babel/generator"));
const t = __importStar(require("@babel/types"));
// @ts-ignore
const traverse = traverse_1.default.default || traverse_1.default;
// @ts-ignore
const generate = generator_1.default.default || generator_1.default;
const generateRandomString = (length = 6) => {
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
return Array.from({ length }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
};
/**
* Creates a transformer that looks for a specific directive in the code and rewrites it accordingly by
* wrapping the function in a call to the imported higher-order function.
* @param options The options for the transformer.
* @returns A function that takes the source code and arguments and returns the transformed code.
*/
function createDirectiveTransformer(options) {
if (!options) {
throw new Error('Options are required');
}
if (!options.directive) {
throw new Error('Directive is required');
}
if (!options.importPath) {
throw new Error('Import path is required');
}
if (!options.importName) {
throw new Error('Import name is required');
}
if (typeof options.directive !== 'string') {
throw new Error('Directive must be a string');
}
const IMPORT_PATH = options.importPath;
const DIRECTIVE = options.directive;
const IDENTIFIER = options.importName;
const ASYNC_ONLY = options.asyncOnly ?? true;
const transformer = async (source, args) => {
const ast = parser.parse(source, {
sourceType: 'module',
plugins: ['typescript', 'jsx'],
});
let state = {
needsImport: false,
hasExistingImport: false,
cacheIdentifierName: IDENTIFIER,
modifications: [],
};
// First pass: check for naming collisions and collect modifications
traverse(ast, {
Program: {
enter(path) {
const binding = path.scope.getBinding(IDENTIFIER);
if (binding) {
state.cacheIdentifierName = `${IDENTIFIER}_${generateRandomString()}`;
}
},
},
ImportDeclaration(path) {
if (path.node.source.value === IMPORT_PATH &&
path.node.specifiers.some((spec) => t.isImportSpecifier(spec) &&
// @ts-ignore
spec.imported.name === IDENTIFIER)) {
state.hasExistingImport = true;
if (state.cacheIdentifierName !== IDENTIFIER) {
// @ts-ignore
state.modifications.push(() => {
path.node.specifiers.forEach((spec) => {
if (t.isImportSpecifier(spec) &&
// @ts-ignore
spec.imported.name === IDENTIFIER) {
spec.local.name = state.cacheIdentifierName;
}
});
});
}
}
},
'FunctionDeclaration|FunctionExpression|ArrowFunctionExpression|ObjectMethod'(path) {
const body = t.isBlockStatement(path.node.body) ? path.node.body : null;
const hasUseCache = body?.directives?.some((d) => d.value.value === DIRECTIVE);
if (!hasUseCache && !t.isBlockStatement(path.node.body)) {
const parentFunction = path.findParent((p) => (p.isFunction() || p.isProgram()) && 'directives' in p.node);
if (!parentFunction?.node.directives?.some((d) => d.value.value === DIRECTIVE)) {
return;
}
}
if (hasUseCache || !t.isBlockStatement(path.node.body)) {
// Check if the function is async
if (ASYNC_ONLY && !path.node.async) {
throw new Error(`"${DIRECTIVE}" directive may only be used in async functions at ${args.path}\n\n${path.toString()}\n^^^^${'-'.repeat(6)} This function must be async`);
}
state.needsImport = true;
const isDeclaration = t.isFunctionDeclaration(path.node);
const name = isDeclaration ? path.node.id?.name : undefined;
// Create a new body without the 'use cache' directive
const newBody = t.isBlockStatement(path.node.body)
? t.blockStatement(path.node.body.body, path.node.body.directives.filter((d) => d.value.value !== DIRECTIVE))
: path.node.body;
const wrapped = t.callExpression(t.identifier(state.cacheIdentifierName), [
t.arrowFunctionExpression(path.node.params, newBody, path.node.async),
]);
// @ts-ignore
state.modifications.push(() => {
if (t.isObjectMethod(path.node)) {
path.replaceWith(t.objectProperty(t.identifier(path.node.key.name), wrapped));
}
else if (name) {
path.replaceWith(t.variableDeclaration('const', [
t.variableDeclarator(t.identifier(name), wrapped),
]));
}
else if (!t.isVariableDeclarator(path.parent)) {
path.replaceWith(wrapped);
}
else {
path.parent.init = wrapped;
}
});
}
},
});
// Apply all collected modifications
if (state.modifications.length > 0) {
// Add import if needed
if (state.needsImport && !state.hasExistingImport) {
ast.program.body.unshift(t.importDeclaration([
t.importSpecifier(t.identifier(state.cacheIdentifierName), t.identifier(IDENTIFIER)),
], t.stringLiteral(IMPORT_PATH)));
}
// Apply collected modifications
// @ts-ignore
state.modifications.forEach((modify) => modify());
}
const { code } = generate(ast);
return {
contents: code,
loader: args.path?.split('.').pop(),
};
};
return transformer;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJhbnNmb3JtZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvdHJhbnNmb3JtZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUEyQ0EsZ0VBZ01DO0FBM09ELHNEQUF3QztBQUN4QywrREFBd0M7QUFDeEMsaUVBQXlDO0FBQ3pDLGdEQUFrQztBQUVsQyxhQUFhO0FBQ2IsTUFBTSxRQUFRLEdBQUcsa0JBQVMsQ0FBQyxPQUFPLElBQUksa0JBQVMsQ0FBQztBQUNoRCxhQUFhO0FBQ2IsTUFBTSxRQUFRLEdBQUcsbUJBQVMsQ0FBQyxPQUFPLElBQUksbUJBQVMsQ0FBQztBQUVoRCxNQUFNLG9CQUFvQixHQUFHLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxFQUFFO0lBQzFDLE1BQU0sS0FBSyxHQUFHLHNEQUFzRCxDQUFDO0lBQ3JFLE9BQU8sS0FBSyxDQUFDLElBQUksQ0FDZixFQUFFLE1BQU0sRUFBRSxFQUNWLEdBQUcsRUFBRSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsR0FBRyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FDdEQsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7QUFDYixDQUFDLENBQUM7QUFxQkY7Ozs7O0dBS0c7QUFDSCxTQUFnQiwwQkFBMEIsQ0FDeEMsT0FBb0M7SUFFcEMsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ2IsTUFBTSxJQUFJLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO0lBQzFDLENBQUM7SUFFRCxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQ3ZCLE1BQU0sSUFBSSxLQUFLLENBQUMsdUJBQXVCLENBQUMsQ0FBQztJQUMzQyxDQUFDO0lBRUQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUN4QixNQUFNLElB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