Skip to content

Commit d728ba5

Browse files
committed
Refactor the long parameter lists to use a context object
1 parent d208ae1 commit d728ba5

File tree

1 file changed

+77
-109
lines changed
  • packages/babel-plugin-component-annotate/src

1 file changed

+77
-109
lines changed

packages/babel-plugin-component-annotate/src/index.ts

Lines changed: 77 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,24 @@ interface AnnotationPluginPass extends PluginPass {
6363

6464
type AnnotationPlugin = PluginObj<AnnotationPluginPass>;
6565

66+
// Shared context object for all JSX processing functions
67+
interface JSXProcessingContext {
68+
/** Whether to annotate React fragments */
69+
annotateFragments: boolean;
70+
/** Babel types object */
71+
t: typeof Babel.types;
72+
/** Name of the React component */
73+
componentName: string;
74+
/** Source file name (optional) */
75+
sourceFileName?: string;
76+
/** Array of attribute names [component, element, sourceFile] */
77+
attributeNames: string[];
78+
/** Array of component names to ignore */
79+
ignoredComponents: string[];
80+
/** Fragment context for identifying React fragments */
81+
fragmentContext?: FragmentContext;
82+
}
83+
6684
// We must export the plugin as default, otherwise the Babel loader will not be able to resolve it when configured using its string identifier
6785
export default function componentNameAnnotatePlugin({ types: t }: typeof Babel): AnnotationPlugin {
6886
return {
@@ -81,16 +99,8 @@ export default function componentNameAnnotatePlugin({ types: t }: typeof Babel):
8199
return;
82100
}
83101

84-
functionBodyPushAttributes(
85-
state.opts["annotate-fragments"] === true,
86-
t,
87-
path,
88-
path.node.id.name,
89-
sourceFileNameFromState(state),
90-
attributeNamesFromState(state),
91-
state.opts.ignoredComponents ?? [],
92-
state.sentryFragmentContext
93-
);
102+
const context = createJSXProcessingContext(state, t, path.node.id.name);
103+
functionBodyPushAttributes(context, path);
94104
},
95105
ArrowFunctionExpression(path, state) {
96106
// We're expecting a `VariableDeclarator` like `const MyComponent =`
@@ -110,16 +120,8 @@ export default function componentNameAnnotatePlugin({ types: t }: typeof Babel):
110120
return;
111121
}
112122

113-
functionBodyPushAttributes(
114-
state.opts["annotate-fragments"] === true,
115-
t,
116-
path,
117-
parent.id.name,
118-
sourceFileNameFromState(state),
119-
attributeNamesFromState(state),
120-
state.opts.ignoredComponents ?? [],
121-
state.sentryFragmentContext
122-
);
123+
const context = createJSXProcessingContext(state, t, parent.id.name);
124+
functionBodyPushAttributes(context, path);
123125
},
124126
ClassDeclaration(path, state) {
125127
const name = path.get("id");
@@ -132,7 +134,7 @@ export default function componentNameAnnotatePlugin({ types: t }: typeof Babel):
132134
return;
133135
}
134136

135-
const ignoredComponents = state.opts.ignoredComponents ?? [];
137+
const context = createJSXProcessingContext(state, t, name.node?.name || "");
136138

137139
render.traverse({
138140
ReturnStatement(returnStatement) {
@@ -142,32 +144,41 @@ export default function componentNameAnnotatePlugin({ types: t }: typeof Babel):
142144
return;
143145
}
144146

145-
processJSX(
146-
state.opts["annotate-fragments"] === true,
147-
t,
148-
arg,
149-
name.node && name.node.name,
150-
sourceFileNameFromState(state),
151-
attributeNamesFromState(state),
152-
ignoredComponents,
153-
state.sentryFragmentContext
154-
);
147+
processJSX(context, arg);
155148
},
156149
});
157150
},
158151
},
159152
};
160153
}
161154

162-
function functionBodyPushAttributes(
163-
annotateFragments: boolean,
155+
/**
156+
* Creates a JSX processing context from the plugin state
157+
*/
158+
function createJSXProcessingContext(
159+
state: AnnotationPluginPass,
164160
t: typeof Babel.types,
165-
path: Babel.NodePath<Babel.types.Function>,
166-
componentName: string,
167-
sourceFileName: string | undefined,
168-
attributeNames: string[],
169-
ignoredComponents: string[],
170-
fragmentContext?: FragmentContext
161+
componentName: string
162+
): JSXProcessingContext {
163+
return {
164+
annotateFragments: state.opts["annotate-fragments"] === true,
165+
t,
166+
componentName,
167+
sourceFileName: sourceFileNameFromState(state),
168+
attributeNames: attributeNamesFromState(state),
169+
ignoredComponents: state.opts.ignoredComponents ?? [],
170+
fragmentContext: state.sentryFragmentContext,
171+
};
172+
}
173+
174+
/**
175+
* Processes the body of a function to add Sentry tracking attributes to JSX elements.
176+
* Handles various function body structures including direct JSX returns, conditional expressions,
177+
* and nested JSX elements.
178+
*/
179+
function functionBodyPushAttributes(
180+
context: JSXProcessingContext,
181+
path: Babel.NodePath<Babel.types.Function>
171182
): void {
172183
let jsxNode: Babel.NodePath;
173184

@@ -209,29 +220,11 @@ function functionBodyPushAttributes(
209220
if (arg.isConditionalExpression()) {
210221
const consequent = arg.get("consequent");
211222
if (consequent.isJSXFragment() || consequent.isJSXElement()) {
212-
processJSX(
213-
annotateFragments,
214-
t,
215-
consequent,
216-
componentName,
217-
sourceFileName,
218-
attributeNames,
219-
ignoredComponents,
220-
fragmentContext
221-
);
223+
processJSX(context, consequent);
222224
}
223225
const alternate = arg.get("alternate");
224226
if (alternate.isJSXFragment() || alternate.isJSXElement()) {
225-
processJSX(
226-
annotateFragments,
227-
t,
228-
alternate,
229-
componentName,
230-
sourceFileName,
231-
attributeNames,
232-
ignoredComponents,
233-
fragmentContext
234-
);
227+
processJSX(context, alternate);
235228
}
236229
return;
237230
}
@@ -247,45 +240,36 @@ function functionBodyPushAttributes(
247240
return;
248241
}
249242

250-
processJSX(
251-
annotateFragments,
252-
t,
253-
jsxNode,
254-
componentName,
255-
sourceFileName,
256-
attributeNames,
257-
ignoredComponents,
258-
fragmentContext
259-
);
243+
processJSX(context, jsxNode);
260244
}
261245

246+
/**
247+
* Recursively processes JSX elements to add Sentry tracking attributes.
248+
* Handles both JSX elements and fragments, applying appropriate attributes
249+
* based on configuration and component context.
250+
*/
262251
function processJSX(
263-
annotateFragments: boolean,
264-
t: typeof Babel.types,
252+
context: JSXProcessingContext,
265253
jsxNode: Babel.NodePath,
266-
componentName: string | null,
267-
sourceFileName: string | undefined,
268-
attributeNames: string[],
269-
ignoredComponents: string[],
270-
fragmentContext?: FragmentContext
254+
componentName?: string | null
271255
): void {
272256
if (!jsxNode) {
273257
return;
274258
}
259+
260+
// Use provided componentName or fall back to context componentName
261+
const currentComponentName = componentName !== undefined ? componentName : context.componentName;
262+
275263
// NOTE: I don't know of a case where `openingElement` would have more than one item,
276264
// but it's safer to always iterate
277265
const paths = jsxNode.get("openingElement");
278266
const openingElements = Array.isArray(paths) ? paths : [paths];
279267

280268
openingElements.forEach((openingElement) => {
281269
applyAttributes(
282-
t,
270+
context,
283271
openingElement as Babel.NodePath<Babel.types.JSXOpeningElement>,
284-
componentName,
285-
sourceFileName,
286-
attributeNames,
287-
ignoredComponents,
288-
fragmentContext
272+
currentComponentName
289273
);
290274
});
291275

@@ -296,7 +280,7 @@ function processJSX(
296280
children = [children];
297281
}
298282

299-
let shouldSetComponentName = annotateFragments;
283+
let shouldSetComponentName = context.annotateFragments;
300284

301285
children.forEach((child) => {
302286
// Happens for some node types like plain text
@@ -314,40 +298,24 @@ function processJSX(
314298

315299
if (shouldSetComponentName && openingElement && openingElement.node) {
316300
shouldSetComponentName = false;
317-
processJSX(
318-
annotateFragments,
319-
t,
320-
child,
321-
componentName,
322-
sourceFileName,
323-
attributeNames,
324-
ignoredComponents,
325-
fragmentContext
326-
);
301+
processJSX(context, child, currentComponentName);
327302
} else {
328-
processJSX(
329-
annotateFragments,
330-
t,
331-
child,
332-
null,
333-
sourceFileName,
334-
attributeNames,
335-
ignoredComponents,
336-
fragmentContext
337-
);
303+
processJSX(context, child, null);
338304
}
339305
});
340306
}
341307

308+
/**
309+
* Applies Sentry tracking attributes to a JSX opening element.
310+
* Adds component name, element name, and source file attributes while
311+
* respecting ignore lists and fragment detection.
312+
*/
342313
function applyAttributes(
343-
t: typeof Babel.types,
314+
context: JSXProcessingContext,
344315
openingElement: Babel.NodePath<Babel.types.JSXOpeningElement>,
345-
componentName: string | null,
346-
sourceFileName: string | undefined,
347-
attributeNames: string[],
348-
ignoredComponents: string[],
349-
fragmentContext?: FragmentContext
316+
componentName: string | null
350317
): void {
318+
const { t, attributeNames, ignoredComponents, fragmentContext, sourceFileName } = context;
351319
const [componentAttributeName, elementAttributeName, sourceFileAttributeName] = attributeNames;
352320

353321
// e.g., Raw JSX text like the `A` in `<h1>a</h1>`

0 commit comments

Comments
 (0)