Skip to content

Instantly share code, notes, and snippets.

@uladkasach
Created December 16, 2022 05:32
Show Gist options
  • Save uladkasach/bdcbad1a7a5152af7d6ceed06a1661eb to your computer and use it in GitHub Desktop.
Save uladkasach/bdcbad1a7a5152af7d6ceed06a1661eb to your computer and use it in GitHub Desktop.
getFirstFunctionNameFromCallStack
import { getFirstFunctionNameFromCallStack } from './getFirstFunctionNameFromCallStack';
export const someSuperCoolAnonymousFunctionAssignedToAVariableIWantTheNameOf =
() => {
const methodName = getFirstFunctionNameFromCallStack();
return { methodName };
};
export const someSuperCoolNamedFunctionIWantTheNameOfWhichHappensToAlsoBeAssignedToAVariable =
function someSuperCoolNamedFunctionIWantTheNameOf() {
const methodName = getFirstFunctionNameFromCallStack();
return { methodName };
};
describe('getFirstMethodNameFromCallStack', () => {
it('should return the name of the variable an anonymous function was assigned to, when run in an anonymous function', async () => {
const { methodName } =
someSuperCoolAnonymousFunctionAssignedToAVariableIWantTheNameOf();
expect(methodName).toEqual(
'someSuperCoolAnonymousFunctionAssignedToAVariableIWantTheNameOf',
);
});
it('returns the declared name of the function, ignoring the name of the variable it was assigned to', () => {
const { methodName } =
someSuperCoolNamedFunctionIWantTheNameOfWhichHappensToAlsoBeAssignedToAVariable();
expect(methodName).toEqual('someSuperCoolNamedFunctionIWantTheNameOf');
});
});
/* eslint-disable no-restricted-syntax */
/**
* returns the first name found in the call stack at runtime
*
* relevance
* - find the name of the variable that an anonymous function was assigned to
*
* note
* - uses Error.prepareStackTrace and Error.captureStackTrace to access the stack trace at runtime
* - ⚠️ Error.captureStackTrace is an expensive operation; the results of this operation should be cached
* - ⚠️ if the function you want the name of is defined in a jest test file, this method will always return `next`. (try defining your function in a different file and importing it instead)
*
* ref
* - https://v8.dev/docs/stack-trace-api#customizing-stack-traces
* - https://stackoverflow.com/a/21666875/3068233
*/
export const getFirstFunctionNameFromCallStack = (): string | null => {
const obj = { stack: [] as any[] };
const prepare = Error.prepareStackTrace;
Error.prepareStackTrace = (_, stack) => stack;
try {
Error.captureStackTrace(obj);
obj.stack.forEach((stackEntry, index) => {
console.log({
methodName: stackEntry.getMethodName(),
functionName: stackEntry.getFunctionName(),
index,
});
});
return (
obj.stack
.find((stackEntry: any, index: number) => {
// if its the first index, ignore it, since it will always show `getFirstMethodNameFromCallStack`
if (index === 0) return false;
// return the first stackEntry who's name is not null
return stackEntry.getFunctionName() !== null;
})
?.getFunctionName() ?? null // now grab the name, and default to null if no item was found
);
} finally {
Error.prepareStackTrace = prepare; // ensure to _always_ reset the prepare function. otherwise, we would have borked all error stack tracing going forward 😬
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment