Here is my method for this use-case:
Grabbing the top level parameters of functional code blocks ie: lambda expressions and functions, named and anonymous.
If this is what you're wanting to extract from:
({ device: { name, brand }, settings }, { brightness = 50, volume = 70 }) => {};
And you are looking for this:
['{ device: { name,brand },settings }', '{ brightness = 50,volume = 70 }']
Then you can utilize this:
function getFirstLayerParams(func) {
const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm;
const paramSectionRegex = /\(([^)]+)\)/;
let fnString = func.toString();
fnString = fnString.replace(STRIP_COMMENTS, '');
const paramsMatch = fnString.toString().match(paramSectionRegex);
const theParams = [];
if (paramsMatch) {
const paramsString = paramsMatch[1];
const params = paramsString.split(',').map((param) => param.trim());
let depth = 0;
let currentParam = '';
for (const param of params) {
currentParam += param;
depth += (param.match(/{/g) || []).length - (param.match(/}/g) || []).length;
if (depth === 0 && currentParam.trim().length > 0) {
theParams.push(currentParam.trim());
currentParam = '';
} else {
currentParam += ',';
}
}
}
return theParams;
}
As of right now, this will not work if you include defaults of functions/expressions ie: errorHandler = () => console.error('Error occurred') }
Of course, resolving this last bit might not be that difficult, as you can just walk the parameters, and as you encounter them, identify the content to be a function of some sort and go from there. I'm not doing that because I don't expect to ever have defaults of function bodies, and I don't want to pull my hair out right now.
This came about after doing research of my own in order to build my approach to lift required parameter signatures like we need in test frameworks such as RTL and Playwright, I saw that maybe, I could call 'use' for fixtures on behalf of the developer (and to enable other features of my own) to shift the requirement to only passing fixture dependency parameters.
const funcArgs = getFirstLayerParams(func);
const paramsResult = funcArgs[0];
const paramSection = `${paramsResult ?? '{}'},`;
const nestedFunctionParamResult = funcArgs[0] ?? '{}'
const useThisFunc = new Function(
`const func = arguments[0]; return (${paramSection} use) => { use(func(${nestedFunctionParamResult}));};`,
).bind(null, func)();
This is the basic aspect. I added more, such as injecting other parameters that weren't specified in implementation by inserting right before the final closing curly brace.
Also, if you want to extract the function body, all you have to do is take this code, and find the last index of characters where the parameter section would stop, then slice whatever you need.
arguments
keyword (inside a function it is an array-like object werearguments[0]
is the first argument, etc). This isn't an answer because I am too lazy to go find a good reference. However, this only exposes the values, not the names. Depending upon a particular "To String" implementation for a Function, the names can be extracted via that and parsing magic. – Baldhead