parse-function
Version:
Parse a function into an object using espree, acorn or babylon parsers. Extensible through Smart Plugins
181 lines (145 loc) • 4.62 kB
JavaScript
import { parseExpression } from '@babel/parser';
const arrify = value => {
if (value === null || value === undefined) {
return [];
}
if (Array.isArray(value)) {
return value;
}
if (typeof value === 'string') {
return [value];
}
if (typeof value[Symbol.iterator] === 'function') {
return [...value];
}
return [value];
};
var arrify_1 = arrify;
const utils = {};
utils.arrayify = arrify_1;
utils.setDefaults = function setDefaults(code) {
const result = {
name: null,
body: '',
args: [],
params: ''
};
if (typeof code === 'function') {
code = code.toString('utf8');
}
if (typeof code !== 'string') {
code = '';
}
return utils.setHiddenDefaults(result, code || '');
};
utils.setHiddenDefaults = function setHiddenDefaults(result, code) {
result.defaults = {};
result.value = code;
result.isValid = code.length > 0;
result.isArrow = false;
result.isAsync = false;
result.isNamed = false;
result.isAnonymous = false;
result.isGenerator = false;
result.isExpression = false;
return result;
};
utils.getNode = function getNode(result, opts) {
if (typeof opts.parse === 'function') {
result.value = `(${result.value})`;
const ast = opts.parse(result.value, opts);
const body = ast.program && ast.program.body || ast.body;
return body[0].expression;
}
return parseExpression(result.value, opts);
};
const body = (() => (node, result) => {
result.body = result.value.slice(node.body.start, node.body.end);
const openCurly = result.body.charCodeAt(0) === 123;
const closeCurly = result.body.charCodeAt(result.body.length - 1) === 125;
if (openCurly && closeCurly) {
result.body = result.body.slice(1, -1);
}
return result;
});
const props = (() => (node, result) => {
result.isArrow = node.type.startsWith('Arrow');
result.isAsync = node.async || false;
result.isGenerator = node.generator || false;
result.isExpression = node.expression || false;
result.isAnonymous = node.id === null;
result.isNamed = !result.isAnonymous;
result.name = result.isAnonymous ? null : node.id.name;
return result;
});
const params = (() => (node, result) => {
node.params.forEach(param => {
const defaultArgsName = param.type === 'AssignmentPattern' && param.left && param.left.name;
const restArgName = param.type === 'RestElement' && param.argument && param.argument.name;
const name = param.name || defaultArgsName || restArgName;
result.args.push(name);
if (param.right && param.right.type === 'SequenceExpression') {
const lastExpression = param.right.expressions.pop();
result.defaults[name] = result.value.slice(lastExpression.start, lastExpression.end);
} else {
result.defaults[name] = param.right ? result.value.slice(param.right.start, param.right.end) : undefined;
}
});
result.params = result.args.join(', ');
return result;
});
const initial = (app => (node, result) => {
const isFn = node.type.endsWith('FunctionExpression');
const isMethod = node.type === 'ObjectExpression';
if (!isFn && !isMethod) {
return;
}
node = isMethod ? node.properties[0] : node;
node.id = isMethod ? node.key : node.id;
if (node.type === 'Property') {
const id = node.key;
node = node.value;
node.id = id;
}
result = props()(node, result);
result = params()(node, result);
result = body()(node, result);
return result;
});
function parseFunction(opts = {}) {
const plugins = [];
const app = {
parse(code, options) {
const result = utils.setDefaults(code);
if (!result.isValid) {
return result;
}
const mergedOptions = { ...opts,
...options
};
const isFunction = result.value.startsWith('function');
const isAsyncFn = result.value.startsWith('async function');
const isAsync = result.value.startsWith('async');
const isArrow = result.value.includes('=>');
const isAsyncArrow = isAsync && isArrow;
const isMethod = /^\*?.+\([\s\S\w\W]*\)\s*\{/i.test(result.value);
if (!(isFunction || isAsyncFn || isAsyncArrow) && isMethod) {
result.value = `{ ${result.value} }`;
}
const node = utils.getNode(result, mergedOptions);
return plugins.reduce((res, fn) => fn(node, res) || res, result);
},
use(fn) {
const ret = fn(app);
if (typeof ret === 'function') {
plugins.push(ret);
}
return app;
},
define: utils.define
};
app.use(initial);
return app;
}
export default parseFunction;
//# sourceMappingURL=index.js.map