Skip to content

Commit e9e8ca1

Browse files
committed
fix: transformation of index accessed functions wraps code correctly
1 parent b73e15d commit e9e8ca1

File tree

7 files changed

+275
-69
lines changed

7 files changed

+275
-69
lines changed

src/index.ts

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,24 +20,70 @@ import { getLoadedConfig } from './core/config'
2020
import { CaptureInvocation } from './exports'
2121
import { createHumanLog } from './core/errors'
2222

23-
export function uff<T extends AnyFunction>(
23+
/* export function ufc<T extends AnyFunction>(
2424
func: T,
25-
// context: any = null,
26-
id: string | null = null
27-
): T {
25+
context: any = null
26+
) {
2827
return function (this: any) {
28+
// @ts-ignore
29+
return func.apply(context ?? this, arguments)
30+
} as T;
31+
} */
32+
export function ufc<T extends AnyFunction>(
33+
functionOrNamespace: T,
34+
opts: FlytrapCallOptions
35+
): ReturnType<T> {
36+
try {
37+
saveFunctionCall(opts)
38+
39+
const execFunctionCall = <T>(fn: T, name: string, args: any[]) => {
40+
log.info('call-execution', `Executing function ${name}()`, { args })
41+
// @ts-ignore
42+
if (fn?.[name]) {
43+
if (typeof fn === 'object' && typeof (fn as any)?.[name] === 'function') {
44+
// @ts-ignore
45+
return fn[name].call(fn, ...args)
46+
}
47+
48+
// @ts-ignore
49+
return fn[name](...args)
50+
} else {
51+
// @ts-ignore
52+
return fn(...args)
53+
}
54+
}
55+
56+
const output = execFunctionCall(functionOrNamespace, opts.name, opts.args)
57+
saveOutputForFunctionCall(opts.id, output)
58+
return output
59+
} catch (error) {
60+
/**
61+
* Oops! We found a bug, let's send the current
62+
* executing function along with its data to the
63+
* Flytrap API.
64+
*/
65+
saveErrorForFunctionCall(opts.id, error as Error)
66+
log.info('capture', `Captured error in function call with ID "${opts.id}".`, { error })
67+
throw error
68+
}
69+
}
70+
71+
export function uff<T extends AnyFunction>(func: T, id: string | null = null): T {
72+
return function (this: any, ...args: any[]) {
2973
if (id) {
3074
addFunctionInvocation(id, {
3175
// eslint-disable-next-line
32-
args: Array.from(arguments),
76+
// args: Array.from(arguments),
77+
args,
3378
timestamp: Date.now()
3479
})
3580
}
3681
try {
3782
const context = null
3883
// @ts-ignore
3984
// eslint-disable-next-line
40-
const functionOutput = func.apply(context ?? this, arguments)
85+
// const functionOutput = func.apply(context ?? this, arguments)
86+
const functionOutput = func.apply(context ?? this, args)
4187
if (id) {
4288
saveOutputForFunction(id, functionOutput)
4389
}

src/transform/imports.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ import { parseCode } from './parser'
77
import { extname } from '../core/util'
88

99
export function getRequiredExportsForCapture(): string[] {
10-
// return ['useFlytrapCall', 'useFlytrapCallAsync', 'useFlytrapFunction', 'setFlytrapConfig', 'uff']
11-
return ['uff']
10+
return ['uff', 'ufc', 'setFlytrapConfig']
1211
}
1312

1413
export function getCoreExports(): string[] {

src/transform/index.ts

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@ import {
1717
objectExpression,
1818
V8IntrinsicIdentifier,
1919
ObjectProperty,
20-
VariableDeclarator
20+
VariableDeclarator,
21+
MemberExpression,
22+
isMemberExpression,
23+
isNumericLiteral,
24+
isStringLiteral,
25+
Identifier
2126
} from '@babel/types'
22-
import { parse } from '@babel/parser'
2327
import generate from '@babel/generator'
2428

2529
import { getCoreExports } from './imports'
@@ -37,11 +41,36 @@ import { _babelInterop } from './util'
3741
import { parseCode } from './parser'
3842
import { createHumanLog } from '../core/errors'
3943

44+
export function getCalleeAndAccessorKey(node: MemberExpression | Identifier) {
45+
if (!isMemberExpression(node)) {
46+
return {
47+
callee: node,
48+
accessorKey: undefined
49+
}
50+
}
51+
52+
// @ts-expect-error
53+
const callee: MemberExpression | Identifier = node.object
54+
55+
let accessorKey = node.property
56+
57+
if (!node.computed) {
58+
if (isIdentifier(node.property)) {
59+
accessorKey = stringLiteral(node.property.name)
60+
}
61+
if (isNumericLiteral(node.property) || isStringLiteral(node.property)) {
62+
accessorKey = stringLiteral(String(node.property.value))
63+
}
64+
}
65+
66+
return { callee, accessorKey }
67+
}
68+
4069
export function shouldBeWrappedUff(path: NodePath) {
4170
if (
4271
isCallExpression(path.parent) &&
4372
isIdentifier(path.parent.callee) &&
44-
['uff'].includes(path.parent.callee.name)
73+
['uff', 'ufc'].includes(path.parent.callee.name)
4574
) {
4675
return false
4776
}
@@ -98,10 +127,6 @@ export function flytrapTransformUff(
98127
? findIgnoredImports(code, config.packageIgnores)
99128
: undefined
100129

101-
// What gets transformed and captured is defined in the config.
102-
// const shouldTransformFunctions = config?.captureDataFrom === "calls" ? false : true;
103-
// const shouldTransformCalls = config?.captureDataFrom === 'functions' ? false : true;
104-
105130
try {
106131
_babelInterop(babelTraverse)(ast, {
107132
...(!config?.transformOptions?.disableTransformation?.includes('arrow-function') && {
@@ -188,30 +213,40 @@ export function flytrapTransformUff(
188213
return
189214
}
190215

191-
const functionCallName = extractFunctionCallName(path.node)
216+
const fullFunctionCallName = _babelInterop(generate)({
217+
...path.node,
218+
arguments: []
219+
}).code.replaceAll('()', '')
220+
221+
if (fullFunctionCallName === 'this' || fullFunctionCallName.split('.').at(0) === 'this') {
222+
return
223+
}
224+
const functionCallName = fullFunctionCallName.split('.').at(-1)!
192225
const scopes = extractCurrentScope(path)
193226
const functionCallId = extractFunctionCallId(path, filePath, functionCallName, scopes)
194-
const useFunctionName = isAwaitExpression(path.parent)
195-
? 'useFlytrapCallAsync'
196-
: 'useFlytrapCall'
197-
198-
function transformCallee(callee: V8IntrinsicIdentifier | Expression) {
199-
if (callee.type === 'MemberExpression') {
200-
return callee.object
201-
}
202-
return callee
227+
const useFunctionName = isAwaitExpression(path.parent) ? 'ufc' : 'ufc'
228+
229+
// @ts-ignore
230+
const { callee, accessorKey } = getCalleeAndAccessorKey(path.node.callee)
231+
232+
if (!callee) {
233+
throw new Error('Callee is undefined. CODE: ' + generate(path.node).code)
203234
}
204235

205236
const newNode = callExpression(identifier(useFunctionName), [
206-
// @ts-ignore
207-
transformCallee(path.node.callee),
237+
callee,
208238
objectExpression([
209239
objectProperty(identifier('id'), stringLiteral(functionCallId)),
210240
// @ts-ignore
211241
objectProperty(identifier('args'), arrayExpression(path.node.arguments)),
212-
objectProperty(identifier('name'), stringLiteral(functionCallName))
242+
objectProperty(
243+
identifier('name'),
244+
// @ts-expect-error
245+
accessorKey ? accessorKey : stringLiteral(functionCallName)
246+
)
213247
])
214248
])
249+
215250
path.replaceWith(newNode)
216251
}
217252
})

test/testUtils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
export const addAsync = (isAsync: boolean) => (isAsync ? 'async ' : '')
22
export const addAwait = (isAsync: boolean) => (isAsync ? 'await ' : '')
3+
4+
export function toOneLine(code: string) {
5+
return code.split('\n').join('').replace(/\s+/g, '').replaceAll("'", '"').replaceAll(';', '')
6+
}

0 commit comments

Comments
 (0)