@agent-scope/babel-plugin 1.17.1 → 1.17.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +510 -0
- package/package.json +4 -3
package/README.md
ADDED
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
# `@agent-scope/babel-plugin`
|
|
2
|
+
|
|
3
|
+
Build-time AST transform that injects two static properties into every React component definition: `__scopeSource` (file + line + column) and `__scopeProps` (TypeScript prop type metadata). No runtime overhead — all analysis happens at compile time.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install --save-dev @agent-scope/babel-plugin
|
|
9
|
+
# or
|
|
10
|
+
bun add -d @agent-scope/babel-plugin
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## What it does
|
|
16
|
+
|
|
17
|
+
For each detected React component the plugin emits two assignment statements immediately after the component definition:
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
// Input
|
|
21
|
+
interface ButtonProps {
|
|
22
|
+
variant: 'primary' | 'secondary' | 'ghost';
|
|
23
|
+
size?: 'sm' | 'md' | 'lg';
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
children: React.ReactNode;
|
|
26
|
+
onClick?: (e: MouseEvent) => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function Button({ variant, size = 'md', disabled = false, children, onClick }: ButtonProps) {
|
|
30
|
+
return <button type="button" disabled={disabled}>{children}</button>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Output (after transform)
|
|
34
|
+
function Button({ variant, size = 'md', disabled = false, children, onClick }) {
|
|
35
|
+
return /*#__PURE__*/React.createElement("button", { type: "button", disabled }, children);
|
|
36
|
+
}
|
|
37
|
+
Button.__scopeSource = { filePath: "src/Button.tsx", line: 9, column: 0 };
|
|
38
|
+
Button.__scopeProps = {
|
|
39
|
+
variant: { type: "union", required: true, values: ["primary", "secondary", "ghost"] },
|
|
40
|
+
size: { type: "union", required: false, values: ["sm", "md", "lg"], defaultValue: "md" },
|
|
41
|
+
disabled: { type: "boolean", required: false, defaultValue: false },
|
|
42
|
+
children: { type: "ReactNode", required: true },
|
|
43
|
+
onClick: { type: "function", required: false },
|
|
44
|
+
};
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Architecture
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
Program.enter
|
|
53
|
+
└── Pre-collect all TS interface / type alias declarations
|
|
54
|
+
into a WeakMap<Program, Map<typeName, TSTypeElement[]>>
|
|
55
|
+
(before @babel/preset-typescript strips them)
|
|
56
|
+
|
|
57
|
+
FunctionDeclaration / VariableDeclaration / ExportDefaultDeclaration
|
|
58
|
+
└── component-detector.ts
|
|
59
|
+
├── isComponentName() → name starts with uppercase
|
|
60
|
+
└── bodyContainsJSX() → body has a return statement returning JSX
|
|
61
|
+
│
|
|
62
|
+
▼
|
|
63
|
+
source-injector.ts
|
|
64
|
+
└── buildScopeSourceStatement() → AST node for __scopeSource = {...}
|
|
65
|
+
|
|
66
|
+
prop-type-extractor.ts
|
|
67
|
+
└── resolveTypeReferenceName() → look up interface/type in pre-collected map
|
|
68
|
+
extractPropsFromTypeMembers() → PropMap
|
|
69
|
+
extractDefaultValues() → defaults from destructuring
|
|
70
|
+
│
|
|
71
|
+
▼
|
|
72
|
+
props-injector.ts
|
|
73
|
+
└── buildScopePropsStatement() → AST node for __scopeProps = {...}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Why `Program.enter` pre-collection?
|
|
77
|
+
|
|
78
|
+
`@babel/preset-typescript` removes `TSInterfaceDeclaration` and `TSTypeAliasDeclaration` nodes during the same traversal pass. Because Babel traverses top-down, interface declarations (defined before the component) are visited and removed **before** the `FunctionDeclaration` visitor fires. The plugin collects all type declarations at `Program.enter` — before any child nodes are visited or removed — and stores them in a `WeakMap` keyed by the Program node.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Component detection rules
|
|
83
|
+
|
|
84
|
+
A function is treated as a React component if:
|
|
85
|
+
|
|
86
|
+
1. **Name starts with an uppercase letter** — `isComponentName(name)` checks `/^[A-Z]/`
|
|
87
|
+
2. **Body returns JSX** — `bodyContainsJSX()` checks for a `ReturnStatement` whose argument is a `JSXElement` or `JSXFragment` (including via ternary and logical expressions one level deep)
|
|
88
|
+
|
|
89
|
+
Detected patterns:
|
|
90
|
+
|
|
91
|
+
| Pattern | Example |
|
|
92
|
+
|---|---|
|
|
93
|
+
| Named function declaration | `function Button() { return <div/>; }` |
|
|
94
|
+
| Arrow function variable | `const Card = () => <div/>;` |
|
|
95
|
+
| Function expression variable | `const Card = function() { return <div/>; };` |
|
|
96
|
+
| `React.memo()` wrapper | `const Memo = React.memo(() => <div/>);` |
|
|
97
|
+
| `React.forwardRef()` wrapper | `const Ref = React.forwardRef((ref, p) => <input ref={ref}/>);` |
|
|
98
|
+
| Named default export | `export default function Page() { return <main/>; }` |
|
|
99
|
+
| Anonymous default export (arrow/fn) | `export default () => <div/>;` |
|
|
100
|
+
|
|
101
|
+
Not detected (intentionally excluded):
|
|
102
|
+
|
|
103
|
+
- Lowercase-named functions: `function formatDate(d)` → skipped
|
|
104
|
+
- React hooks: `function useCounter()` → skipped (lowercase 'u')
|
|
105
|
+
- Functions that don't return JSX: `function GetData() { return fetch(...); }` → skipped
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## `__scopeSource` injection
|
|
110
|
+
|
|
111
|
+
**Injected shape**
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
ComponentName.__scopeSource = {
|
|
115
|
+
filePath: string; // relative path (or opts.filePath override)
|
|
116
|
+
line: number; // 1-based line number of the component declaration
|
|
117
|
+
column: number; // 0-based column number
|
|
118
|
+
};
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**`filePath` resolution** (from `plugin.test.ts`)
|
|
122
|
+
|
|
123
|
+
Pass `filePath` in plugin options to override Babel's automatic filename. This is critical in test environments where absolute paths vary by machine:
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
// babel.config.js
|
|
127
|
+
module.exports = {
|
|
128
|
+
plugins: [['@agent-scope/babel-plugin', { filePath: 'src/Button.tsx' }]],
|
|
129
|
+
};
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Without `filePath`, Babel's `state.filename` is used (often an absolute path).
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## `__scopeProps` injection
|
|
137
|
+
|
|
138
|
+
**Injected shape**
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
ComponentName.__scopeProps = {
|
|
142
|
+
[propName: string]: {
|
|
143
|
+
type: PropTypeString; // simplified type category
|
|
144
|
+
required: boolean; // true if prop has no '?'
|
|
145
|
+
values?: string[]; // only for 'union' type — the literal string values
|
|
146
|
+
defaultValue?: unknown; // extracted from destructuring default, if present
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**`PropTypeString` values**
|
|
152
|
+
|
|
153
|
+
| Value | TypeScript types that map to it |
|
|
154
|
+
|---|---|
|
|
155
|
+
| `"string"` | `string` |
|
|
156
|
+
| `"number"` | `number` |
|
|
157
|
+
| `"boolean"` | `boolean` |
|
|
158
|
+
| `"union"` | A union of all string/number literals: `'a' \| 'b' \| 'c'` |
|
|
159
|
+
| `"function"` | `() => void`, `MouseEvent`, `ChangeEvent`, `React.*Handler` |
|
|
160
|
+
| `"ReactNode"` | `React.ReactNode`, `ReactNode`, `ReactElement`, `React.ReactElement` |
|
|
161
|
+
| `"object"` | `{ ... }` (TSTypeLiteral), mapped types |
|
|
162
|
+
| `"array"` | `T[]`, tuples |
|
|
163
|
+
| `"unknown"` | Intersection, conditional, generic references, `any`, `never`, etc. |
|
|
164
|
+
|
|
165
|
+
**Default value extraction**
|
|
166
|
+
|
|
167
|
+
Destructuring defaults are extracted from the first parameter:
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
function Button({ size = 'md', disabled = false, count = 0 }: ButtonProps) { ... }
|
|
171
|
+
// → size.defaultValue: "md", disabled.defaultValue: false, count.defaultValue: 0
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Supported literal default types: `string`, `number`, `boolean`, `null`, and negative numbers (`-1`).
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Configuration
|
|
179
|
+
|
|
180
|
+
### `babel.config.js`
|
|
181
|
+
|
|
182
|
+
```javascript
|
|
183
|
+
module.exports = {
|
|
184
|
+
plugins: ['@agent-scope/babel-plugin'],
|
|
185
|
+
};
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### `babel.config.js` with `filePath` override
|
|
189
|
+
|
|
190
|
+
```javascript
|
|
191
|
+
module.exports = {
|
|
192
|
+
plugins: [
|
|
193
|
+
['@agent-scope/babel-plugin', { filePath: 'src/MyComponent.tsx' }],
|
|
194
|
+
],
|
|
195
|
+
};
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Vite (`vite.config.ts`)
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { defineConfig } from 'vite';
|
|
202
|
+
import react from '@vitejs/plugin-react';
|
|
203
|
+
|
|
204
|
+
export default defineConfig({
|
|
205
|
+
plugins: [
|
|
206
|
+
react({
|
|
207
|
+
babel: {
|
|
208
|
+
plugins: ['@agent-scope/babel-plugin'],
|
|
209
|
+
},
|
|
210
|
+
}),
|
|
211
|
+
],
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Next.js (`next.config.js`)
|
|
216
|
+
|
|
217
|
+
```javascript
|
|
218
|
+
module.exports = {
|
|
219
|
+
experimental: { forceSwcTransforms: false },
|
|
220
|
+
babel: {
|
|
221
|
+
plugins: ['@agent-scope/babel-plugin'],
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Transform examples (from test fixtures)
|
|
229
|
+
|
|
230
|
+
### Simple function component (`simple-component.tsx`)
|
|
231
|
+
|
|
232
|
+
**Input**
|
|
233
|
+
|
|
234
|
+
```tsx
|
|
235
|
+
function Button({ label }: { label: string }) {
|
|
236
|
+
return <button type="button">{label}</button>;
|
|
237
|
+
}
|
|
238
|
+
export { Button };
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**Output** (TypeScript stripped, JSX transformed)
|
|
242
|
+
|
|
243
|
+
```javascript
|
|
244
|
+
function Button({ label }) {
|
|
245
|
+
return /*#__PURE__*/React.createElement("button", { type: "button" }, label);
|
|
246
|
+
}
|
|
247
|
+
Button.__scopeSource = { filePath: "src/simple-component.tsx", line: 1, column: 0 };
|
|
248
|
+
Button.__scopeProps = {
|
|
249
|
+
label: { type: "string", required: true },
|
|
250
|
+
};
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
### Arrow function component (`arrow-component.tsx`)
|
|
256
|
+
|
|
257
|
+
**Input**
|
|
258
|
+
|
|
259
|
+
```tsx
|
|
260
|
+
const Card = ({ title }: { title: string }) => <div className="card">{title}</div>;
|
|
261
|
+
export { Card };
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Output**
|
|
265
|
+
|
|
266
|
+
```javascript
|
|
267
|
+
const Card = ({ title }) => /*#__PURE__*/React.createElement("div", { className: "card" }, title);
|
|
268
|
+
Card.__scopeSource = { filePath: "src/arrow-component.tsx", line: 1, column: 0 };
|
|
269
|
+
Card.__scopeProps = {
|
|
270
|
+
title: { type: "string", required: true },
|
|
271
|
+
};
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
### `React.memo` wrapper (`memo-component.tsx`)
|
|
277
|
+
|
|
278
|
+
**Input**
|
|
279
|
+
|
|
280
|
+
```tsx
|
|
281
|
+
import React from "react";
|
|
282
|
+
const MemoButton = React.memo(({ label }: { label: string }) => (
|
|
283
|
+
<button type="button">{label}</button>
|
|
284
|
+
));
|
|
285
|
+
export { MemoButton };
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**Output**
|
|
289
|
+
|
|
290
|
+
```javascript
|
|
291
|
+
const MemoButton = React.memo(({ label }) =>
|
|
292
|
+
/*#__PURE__*/React.createElement("button", { type: "button" }, label)
|
|
293
|
+
);
|
|
294
|
+
MemoButton.__scopeSource = { filePath: "src/memo-component.tsx", line: 3, column: 0 };
|
|
295
|
+
MemoButton.__scopeProps = { label: { type: "string", required: true } };
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
### `React.forwardRef` wrapper (`forwardref-component.tsx`)
|
|
301
|
+
|
|
302
|
+
**Input**
|
|
303
|
+
|
|
304
|
+
```tsx
|
|
305
|
+
import React from "react";
|
|
306
|
+
const FancyInput = React.forwardRef<HTMLInputElement, { placeholder: string }>(
|
|
307
|
+
({ placeholder }, ref) => <input ref={ref} placeholder={placeholder} />,
|
|
308
|
+
);
|
|
309
|
+
export { FancyInput };
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Output**
|
|
313
|
+
|
|
314
|
+
```javascript
|
|
315
|
+
const FancyInput = React.forwardRef(({ placeholder }, ref) =>
|
|
316
|
+
/*#__PURE__*/React.createElement("input", { ref, placeholder })
|
|
317
|
+
);
|
|
318
|
+
FancyInput.__scopeSource = { filePath: "src/forwardref-component.tsx", line: 3, column: 0 };
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
### Named default export (`default-export.tsx`)
|
|
324
|
+
|
|
325
|
+
**Input**
|
|
326
|
+
|
|
327
|
+
```tsx
|
|
328
|
+
export default function HomePage() {
|
|
329
|
+
return <main>Hello World</main>;
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
**Output**
|
|
334
|
+
|
|
335
|
+
```javascript
|
|
336
|
+
export default function HomePage() {
|
|
337
|
+
return /*#__PURE__*/React.createElement("main", null, "Hello World");
|
|
338
|
+
}
|
|
339
|
+
HomePage.__scopeSource = { filePath: "src/default-export.tsx", line: 1, column: 0 };
|
|
340
|
+
// __scopeSource injected exactly once
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
### Full typed component (`typed-function-component.tsx`)
|
|
346
|
+
|
|
347
|
+
**Input**
|
|
348
|
+
|
|
349
|
+
```tsx
|
|
350
|
+
interface ButtonProps {
|
|
351
|
+
variant: "primary" | "secondary" | "ghost";
|
|
352
|
+
size?: "sm" | "md" | "lg";
|
|
353
|
+
disabled?: boolean;
|
|
354
|
+
children: React.ReactNode;
|
|
355
|
+
onClick?: (e: MouseEvent) => void;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function Button({
|
|
359
|
+
variant: _variant,
|
|
360
|
+
size: _size = "md",
|
|
361
|
+
disabled = false,
|
|
362
|
+
children,
|
|
363
|
+
onClick: _onClick,
|
|
364
|
+
}: ButtonProps) {
|
|
365
|
+
return <button type="button" disabled={disabled}>{children}</button>;
|
|
366
|
+
}
|
|
367
|
+
export { Button };
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
**Output** (`__scopeProps` excerpt)
|
|
371
|
+
|
|
372
|
+
```javascript
|
|
373
|
+
Button.__scopeSource = { filePath: "src/button.tsx", line: 9, column: 0 };
|
|
374
|
+
Button.__scopeProps = {
|
|
375
|
+
variant: { type: "union", required: true, values: ["primary", "secondary", "ghost"] },
|
|
376
|
+
size: { type: "union", required: false, values: ["sm", "md", "lg"], defaultValue: "md" },
|
|
377
|
+
disabled: { type: "boolean", required: false, defaultValue: false },
|
|
378
|
+
children: { type: "ReactNode", required: true },
|
|
379
|
+
onClick: { type: "function", required: false },
|
|
380
|
+
};
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
### `React.FC<Props>` arrow component (`typed-arrow-fc.tsx`)
|
|
386
|
+
|
|
387
|
+
**Input**
|
|
388
|
+
|
|
389
|
+
```tsx
|
|
390
|
+
import type React from "react";
|
|
391
|
+
|
|
392
|
+
interface CardProps {
|
|
393
|
+
title: string;
|
|
394
|
+
count: number;
|
|
395
|
+
active?: boolean;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const Card: React.FC<CardProps> = ({ title, count = 0, active = false }) => (
|
|
399
|
+
<div className={active ? "active" : ""}>{title}: {count}</div>
|
|
400
|
+
);
|
|
401
|
+
export { Card };
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
**Output** (`__scopeProps` excerpt)
|
|
405
|
+
|
|
406
|
+
```javascript
|
|
407
|
+
Card.__scopeSource = { filePath: "src/card.tsx", line: 9, column: 0 };
|
|
408
|
+
Card.__scopeProps = {
|
|
409
|
+
title: { type: "string", required: true },
|
|
410
|
+
count: { type: "number", required: true, defaultValue: 0 },
|
|
411
|
+
active: { type: "boolean", required: false, defaultValue: false },
|
|
412
|
+
};
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
---
|
|
416
|
+
|
|
417
|
+
### No-props component (`no-props-component.tsx`)
|
|
418
|
+
|
|
419
|
+
**Input**
|
|
420
|
+
|
|
421
|
+
```tsx
|
|
422
|
+
function Header() {
|
|
423
|
+
return <header>Hello World</header>;
|
|
424
|
+
}
|
|
425
|
+
export { Header };
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
**Output**
|
|
429
|
+
|
|
430
|
+
```javascript
|
|
431
|
+
function Header() {
|
|
432
|
+
return /*#__PURE__*/React.createElement("header", null, "Hello World");
|
|
433
|
+
}
|
|
434
|
+
Header.__scopeSource = { filePath: "src/header.tsx", line: 1, column: 0 };
|
|
435
|
+
// __scopeProps is NOT injected — no typed props
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
### Union type extraction (from `prop-extraction.test.ts`)
|
|
441
|
+
|
|
442
|
+
```tsx
|
|
443
|
+
type StatusProps = { status: 'active' | 'inactive' | 'pending' };
|
|
444
|
+
function StatusBadge({ status }: StatusProps) { return <span>{status}</span>; }
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
**Output**
|
|
448
|
+
|
|
449
|
+
```javascript
|
|
450
|
+
StatusBadge.__scopeProps = {
|
|
451
|
+
status: { type: "union", required: true, values: ["active", "inactive", "pending"] },
|
|
452
|
+
};
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
457
|
+
### Inline type annotation (from `prop-extraction.test.ts`)
|
|
458
|
+
|
|
459
|
+
```tsx
|
|
460
|
+
function Avatar({ src, alt }: { src: string; alt?: string }) {
|
|
461
|
+
return <img src={src} alt={alt} />;
|
|
462
|
+
}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
**Output**
|
|
466
|
+
|
|
467
|
+
```javascript
|
|
468
|
+
Avatar.__scopeProps = {
|
|
469
|
+
src: { type: "string", required: true },
|
|
470
|
+
alt: { type: "string", required: false },
|
|
471
|
+
};
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
### Complex types fall back to `"unknown"` (from `prop-extraction.test.ts`)
|
|
477
|
+
|
|
478
|
+
```tsx
|
|
479
|
+
interface ComplexProps { combo: A & B; } // → type: "unknown" (intersection)
|
|
480
|
+
interface GenericProps { data: Array<string>; } // → type: "unknown" (generic TSTypeReference)
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
485
|
+
## Plugin options
|
|
486
|
+
|
|
487
|
+
```typescript
|
|
488
|
+
interface PluginOptions {
|
|
489
|
+
/** Override the file path injected into __scopeSource.
|
|
490
|
+
* Defaults to Babel's `state.filename`.
|
|
491
|
+
* Pass this explicitly in tests to avoid absolute-path mismatches. */
|
|
492
|
+
filePath?: string;
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
**Test setup best practice** (from KB: Babel Plugin Test Assertions): Always pass `filePath` as a plugin option in tests and assert against relative paths. Relying on Babel's automatic filename resolution produces absolute paths that break across machines:
|
|
497
|
+
|
|
498
|
+
```javascript
|
|
499
|
+
transformSync(code, {
|
|
500
|
+
plugins: [['@agent-scope/babel-plugin', { filePath: 'src/Button.tsx' }]],
|
|
501
|
+
});
|
|
502
|
+
expect(output).toContain('filePath: "src/Button.tsx"'); // ✓ portable
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
|
|
507
|
+
## Used by
|
|
508
|
+
|
|
509
|
+
- `@agent-scope/runtime` — reads `Component.__scopeSource` and `Component.__scopeProps` at runtime to enrich `PageReport` nodes with source location and prop type metadata
|
|
510
|
+
- `@agent-scope/cli` — uses prop type metadata to drive `scope render` matrix axis generation
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-scope/babel-plugin",
|
|
3
|
-
"version": "1.17.
|
|
3
|
+
"version": "1.17.2",
|
|
4
4
|
"description": "Babel plugin for automatic Scope instrumentation via AST transforms",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"module": "./dist/index.js",
|
|
21
21
|
"types": "./dist/index.d.ts",
|
|
22
22
|
"files": [
|
|
23
|
-
"dist"
|
|
23
|
+
"dist",
|
|
24
|
+
"README.md"
|
|
24
25
|
],
|
|
25
26
|
"scripts": {
|
|
26
27
|
"build": "tsup",
|
|
@@ -30,7 +31,7 @@
|
|
|
30
31
|
},
|
|
31
32
|
"dependencies": {
|
|
32
33
|
"@babel/core": "^7.29.0",
|
|
33
|
-
"@agent-scope/core": "1.17.
|
|
34
|
+
"@agent-scope/core": "1.17.2"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@babel/preset-react": "^7.28.5",
|