@clayroach/effect-unplugin 4.0.0-effect4-transformer.1
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 +384 -0
- package/dist/annotateEffects.d.ts +17 -0
- package/dist/annotateEffects.d.ts.map +1 -0
- package/dist/annotateEffects.js +125 -0
- package/dist/annotateEffects.js.map +1 -0
- package/dist/esbuild.d.ts +3 -0
- package/dist/esbuild.d.ts.map +1 -0
- package/dist/esbuild.js +18 -0
- package/dist/esbuild.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +86 -0
- package/dist/index.js.map +1 -0
- package/dist/rollup.d.ts +3 -0
- package/dist/rollup.d.ts.map +1 -0
- package/dist/rollup.js +18 -0
- package/dist/rollup.js.map +1 -0
- package/dist/sourceTrace.d.ts +18 -0
- package/dist/sourceTrace.d.ts.map +1 -0
- package/dist/sourceTrace.js +451 -0
- package/dist/sourceTrace.js.map +1 -0
- package/dist/types.d.ts +145 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/vite.d.ts +3 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +19 -0
- package/dist/vite.js.map +1 -0
- package/dist/webpack.d.ts +3 -0
- package/dist/webpack.d.ts.map +1 -0
- package/dist/webpack.js +18 -0
- package/dist/webpack.js.map +1 -0
- package/package.json +102 -0
- package/src/esbuild.ts +18 -0
- package/src/index.ts +97 -0
- package/src/rollup.ts +18 -0
- package/src/sourceTrace.ts +667 -0
- package/src/types.ts +162 -0
- package/src/vite.ts +19 -0
- package/src/webpack.ts +18 -0
package/README.md
ADDED
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
# @clayroach/effect-unplugin
|
|
2
|
+
|
|
3
|
+
Build-time AST transformer for Effect source location tracing and automatic span instrumentation.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Source Tracing**: Transforms `yield*` expressions inside `Effect.gen()` to inject stack frame information
|
|
8
|
+
- **Span Instrumentation**: Auto-wraps Effect combinators with `Effect.withSpan()` for distributed tracing
|
|
9
|
+
- **OpenTelemetry Semantic Conventions**: Adds `code.filepath`, `code.lineno`, `code.column`, `code.function` attributes
|
|
10
|
+
- **Configurable Span Names**: Three formats - function-based, location-based, or full
|
|
11
|
+
- **Granular Control**: Depth-based or override-based filtering strategies
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add -D @clayroach/effect-unplugin
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
### Vite
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
// vite.config.ts
|
|
25
|
+
import { defineConfig } from 'vite'
|
|
26
|
+
import effectPlugin from '@clayroach/effect-unplugin/vite'
|
|
27
|
+
|
|
28
|
+
export default defineConfig({
|
|
29
|
+
plugins: [
|
|
30
|
+
effectPlugin({
|
|
31
|
+
sourceTrace: true,
|
|
32
|
+
spans: { enabled: true }
|
|
33
|
+
})
|
|
34
|
+
]
|
|
35
|
+
})
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### ESBuild
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import effectPlugin from '@clayroach/effect-unplugin/esbuild'
|
|
42
|
+
import { build } from 'esbuild'
|
|
43
|
+
|
|
44
|
+
build({
|
|
45
|
+
plugins: [effectPlugin({ sourceTrace: true, spans: { enabled: true } })],
|
|
46
|
+
// ... other options
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Webpack / Rollup
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import effectPlugin from '@clayroach/effect-unplugin/webpack'
|
|
54
|
+
// or
|
|
55
|
+
import effectPlugin from '@clayroach/effect-unplugin/rollup'
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Configuration
|
|
59
|
+
|
|
60
|
+
### Basic Options
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
effectPlugin({
|
|
64
|
+
// Enable source tracing for yield* expressions (default: true)
|
|
65
|
+
sourceTrace: true,
|
|
66
|
+
|
|
67
|
+
// Extract function names from yield* arguments (default: true)
|
|
68
|
+
extractFunctionName: true,
|
|
69
|
+
|
|
70
|
+
// File patterns to include (default: .js, .ts, .jsx, .tsx)
|
|
71
|
+
include: /\.[jt]sx?$/,
|
|
72
|
+
|
|
73
|
+
// File patterns to exclude (default: node_modules)
|
|
74
|
+
exclude: /node_modules/,
|
|
75
|
+
|
|
76
|
+
// Span instrumentation options
|
|
77
|
+
spans: {
|
|
78
|
+
enabled: true,
|
|
79
|
+
include: ["gen", "fork", "all", "forEach"],
|
|
80
|
+
nameFormat: "function" // "function" | "location" | "full"
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Span Name Formats
|
|
86
|
+
|
|
87
|
+
Control how span names appear in traces:
|
|
88
|
+
|
|
89
|
+
**`"function"` (default)**: Use function/variable names
|
|
90
|
+
```
|
|
91
|
+
effect.gen (fetchUser)
|
|
92
|
+
effect.all
|
|
93
|
+
effect.forEach (processItems)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**`"location"`: Use file locations
|
|
97
|
+
```
|
|
98
|
+
effect.gen (index.ts:23)
|
|
99
|
+
effect.all (index.ts:49)
|
|
100
|
+
effect.forEach (index.ts:59)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**`"full"`: Include both
|
|
104
|
+
```
|
|
105
|
+
effect.gen (fetchUser @ index.ts:23)
|
|
106
|
+
effect.all (index.ts:49)
|
|
107
|
+
effect.forEach (processItems @ index.ts:59)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
All formats include full source location in span attributes (`code.filepath`, `code.lineno`, etc.)
|
|
111
|
+
|
|
112
|
+
## Instrumentation Strategies
|
|
113
|
+
|
|
114
|
+
Reduce overhead with fine-grained control over which Effect calls get instrumented.
|
|
115
|
+
|
|
116
|
+
### Depth Strategy
|
|
117
|
+
|
|
118
|
+
Limit instrumentation by nesting depth:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
spans: {
|
|
122
|
+
enabled: true,
|
|
123
|
+
strategy: {
|
|
124
|
+
type: "depth",
|
|
125
|
+
maxDepth: 2 // 0 = top-level only, 1 = one level deep, etc.
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Per-combinator depth limits:**
|
|
131
|
+
```typescript
|
|
132
|
+
spans: {
|
|
133
|
+
enabled: true,
|
|
134
|
+
strategy: {
|
|
135
|
+
type: "depth",
|
|
136
|
+
perCombinator: {
|
|
137
|
+
fork: 0, // Only top-level forks
|
|
138
|
+
gen: 1, // Gen + one level deep
|
|
139
|
+
all: Infinity // No limit on Effect.all
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Override Strategy
|
|
146
|
+
|
|
147
|
+
Filter by file patterns and function names:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
spans: {
|
|
151
|
+
enabled: true,
|
|
152
|
+
strategy: {
|
|
153
|
+
type: "overrides",
|
|
154
|
+
rules: {
|
|
155
|
+
// Only instrument forks in worker files
|
|
156
|
+
fork: {
|
|
157
|
+
files: "src/workers/**"
|
|
158
|
+
},
|
|
159
|
+
// Skip gen in tests and private functions
|
|
160
|
+
gen: {
|
|
161
|
+
excludeFiles: "**/*.test.ts",
|
|
162
|
+
excludeFunctions: "^_.*"
|
|
163
|
+
},
|
|
164
|
+
// Only instrument forEach in API handlers
|
|
165
|
+
forEach: {
|
|
166
|
+
files: ["src/api/**", "src/handlers/**"],
|
|
167
|
+
functions: "^handle.*"
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Filter Options:**
|
|
175
|
+
- `files`: Glob patterns to include (single string or array)
|
|
176
|
+
- `excludeFiles`: Glob patterns to exclude
|
|
177
|
+
- `functions`: Regex patterns for function names to include
|
|
178
|
+
- `excludeFunctions`: Regex patterns for function names to exclude
|
|
179
|
+
|
|
180
|
+
## Examples
|
|
181
|
+
|
|
182
|
+
### Example 1: Reduce Fork Overhead
|
|
183
|
+
|
|
184
|
+
Only instrument top-level forks to avoid noisy background task traces:
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
spans: {
|
|
188
|
+
enabled: true,
|
|
189
|
+
strategy: {
|
|
190
|
+
type: "depth",
|
|
191
|
+
perCombinator: {
|
|
192
|
+
fork: 0,
|
|
193
|
+
forkDaemon: 0,
|
|
194
|
+
forkScoped: 0
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Example 2: Production-Only Instrumentation
|
|
201
|
+
|
|
202
|
+
Skip instrumentation in tests and development utilities:
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
spans: {
|
|
206
|
+
enabled: true,
|
|
207
|
+
strategy: {
|
|
208
|
+
type: "overrides",
|
|
209
|
+
rules: {
|
|
210
|
+
gen: {
|
|
211
|
+
excludeFiles: [
|
|
212
|
+
"**/*.test.ts",
|
|
213
|
+
"**/__tests__/**",
|
|
214
|
+
"src/dev/**"
|
|
215
|
+
]
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Example 3: Selective High-Value Traces
|
|
223
|
+
|
|
224
|
+
Only instrument critical paths:
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
spans: {
|
|
228
|
+
enabled: true,
|
|
229
|
+
strategy: {
|
|
230
|
+
type: "overrides",
|
|
231
|
+
rules: {
|
|
232
|
+
gen: {
|
|
233
|
+
files: [
|
|
234
|
+
"src/api/**",
|
|
235
|
+
"src/workers/**",
|
|
236
|
+
"src/processors/**"
|
|
237
|
+
]
|
|
238
|
+
},
|
|
239
|
+
fork: {
|
|
240
|
+
files: "src/workers/**",
|
|
241
|
+
functions: "^(background|worker).*"
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## How It Works
|
|
249
|
+
|
|
250
|
+
### Source Tracing
|
|
251
|
+
|
|
252
|
+
Transforms:
|
|
253
|
+
```typescript
|
|
254
|
+
const fetchUser = (id: string) =>
|
|
255
|
+
Effect.gen(function* () {
|
|
256
|
+
yield* Console.log(`Fetching ${id}`)
|
|
257
|
+
return { id, name: `User ${id}` }
|
|
258
|
+
})
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Into:
|
|
262
|
+
```typescript
|
|
263
|
+
const _sf0 = { name: "log", stack: () => "index.ts:3:4", parent: undefined }
|
|
264
|
+
|
|
265
|
+
const fetchUser = (id: string) =>
|
|
266
|
+
Effect.gen(function* () {
|
|
267
|
+
yield* Effect.updateService(
|
|
268
|
+
Console.log(`Fetching ${id}`),
|
|
269
|
+
References.CurrentStackFrame,
|
|
270
|
+
(parent) => ({ ..._sf0, parent })
|
|
271
|
+
)
|
|
272
|
+
return { id, name: `User ${id}` }
|
|
273
|
+
})
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Span Instrumentation
|
|
277
|
+
|
|
278
|
+
Transforms:
|
|
279
|
+
```typescript
|
|
280
|
+
const program = Effect.gen(function* () {
|
|
281
|
+
const users = yield* Effect.all([
|
|
282
|
+
fetchUser('alice'),
|
|
283
|
+
fetchUser('bob')
|
|
284
|
+
])
|
|
285
|
+
})
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Into:
|
|
289
|
+
```typescript
|
|
290
|
+
const program = Effect.withSpan(
|
|
291
|
+
Effect.gen(function* () {
|
|
292
|
+
const users = yield* Effect.withSpan(
|
|
293
|
+
Effect.all([fetchUser('alice'), fetchUser('bob')]),
|
|
294
|
+
"effect.all",
|
|
295
|
+
{
|
|
296
|
+
attributes: {
|
|
297
|
+
"code.filepath": "src/index.ts",
|
|
298
|
+
"code.lineno": 5,
|
|
299
|
+
"code.column": 23,
|
|
300
|
+
"code.function": "effect.all"
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
)
|
|
304
|
+
}),
|
|
305
|
+
"effect.gen (program)",
|
|
306
|
+
{
|
|
307
|
+
attributes: {
|
|
308
|
+
"code.filepath": "src/index.ts",
|
|
309
|
+
"code.lineno": 3,
|
|
310
|
+
"code.column": 16,
|
|
311
|
+
"code.function": "program"
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
)
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## Supported Combinators
|
|
318
|
+
|
|
319
|
+
- `gen` - Effect.gen()
|
|
320
|
+
- `fork` - Effect.fork()
|
|
321
|
+
- `forkDaemon` - Effect.forkDaemon()
|
|
322
|
+
- `forkScoped` - Effect.forkScoped()
|
|
323
|
+
- `all` - Effect.all()
|
|
324
|
+
- `forEach` - Effect.forEach()
|
|
325
|
+
- `filter` - Effect.filter()
|
|
326
|
+
- `reduce` - Effect.reduce()
|
|
327
|
+
- `iterate` - Effect.iterate()
|
|
328
|
+
- `loop` - Effect.loop()
|
|
329
|
+
|
|
330
|
+
## API Reference
|
|
331
|
+
|
|
332
|
+
### SourceTraceOptions
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
interface SourceTraceOptions {
|
|
336
|
+
include?: string | RegExp | Array<string | RegExp>
|
|
337
|
+
exclude?: string | RegExp | Array<string | RegExp>
|
|
338
|
+
sourceTrace?: boolean
|
|
339
|
+
extractFunctionName?: boolean
|
|
340
|
+
spans?: SpanInstrumentationOptions
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### SpanInstrumentationOptions
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
interface SpanInstrumentationOptions {
|
|
348
|
+
enabled?: boolean
|
|
349
|
+
include?: Array<InstrumentableEffect>
|
|
350
|
+
exclude?: Array<InstrumentableEffect>
|
|
351
|
+
nameFormat?: "function" | "location" | "full"
|
|
352
|
+
strategy?: DepthInstrumentationStrategy | OverrideInstrumentationStrategy
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### DepthInstrumentationStrategy
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
interface DepthInstrumentationStrategy {
|
|
360
|
+
type: "depth"
|
|
361
|
+
maxDepth?: number
|
|
362
|
+
perCombinator?: Partial<Record<InstrumentableEffect, number>>
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### OverrideInstrumentationStrategy
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
interface OverrideInstrumentationStrategy {
|
|
370
|
+
type: "overrides"
|
|
371
|
+
rules: Partial<Record<InstrumentableEffect, CombinatorFilter>>
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
interface CombinatorFilter {
|
|
375
|
+
files?: string | Array<string>
|
|
376
|
+
excludeFiles?: string | Array<string>
|
|
377
|
+
functions?: string | Array<string> // Regex patterns
|
|
378
|
+
excludeFunctions?: string | Array<string> // Regex patterns
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## License
|
|
383
|
+
|
|
384
|
+
MIT
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 0.0.1
|
|
3
|
+
* @category models
|
|
4
|
+
*/
|
|
5
|
+
export interface AnnotateResult {
|
|
6
|
+
readonly code: string;
|
|
7
|
+
readonly map?: unknown;
|
|
8
|
+
readonly transformed: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Transforms source code to add pure annotations to Effect calls.
|
|
12
|
+
*
|
|
13
|
+
* @since 0.0.1
|
|
14
|
+
* @category transform
|
|
15
|
+
*/
|
|
16
|
+
export declare function annotateEffects(code: string, id: string): AnnotateResult;
|
|
17
|
+
//# sourceMappingURL=annotateEffects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"annotateEffects.d.ts","sourceRoot":"","sources":["../src/annotateEffects.ts"],"names":[],"mappings":"AA2EA;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;CAC9B;AA0ED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,cAAc,CA8CxE"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effect tree-shaking annotations transformer.
|
|
3
|
+
*
|
|
4
|
+
* Adds `\/* @__PURE__ *\/` comments to Effect calls for bundler tree-shaking.
|
|
5
|
+
*
|
|
6
|
+
* @since 0.0.1
|
|
7
|
+
*/
|
|
8
|
+
import { parse } from "@babel/parser";
|
|
9
|
+
import * as _traverse from "@babel/traverse";
|
|
10
|
+
import * as _generate from "@babel/generator";
|
|
11
|
+
import * as t from "@babel/types";
|
|
12
|
+
// Handle CommonJS/ESM interop for babel packages
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
const traverseModule = _traverse;
|
|
15
|
+
const traverse = typeof traverseModule === "function" ? traverseModule : typeof traverseModule.default === "function" ? traverseModule.default : traverseModule.default?.default ?? traverseModule;
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
+
const generateModule = _generate;
|
|
18
|
+
const generate = typeof generateModule === "function" ? generateModule : typeof generateModule.default === "function" ? generateModule.default : generateModule.default?.default ?? generateModule;
|
|
19
|
+
/**
|
|
20
|
+
* Effect module names that should have pure annotations.
|
|
21
|
+
*/
|
|
22
|
+
const EFFECT_MODULES = /*#__PURE__*/new Set(["Effect", "Option", "Either", "Data", "Schema", "Array", "Chunk", "HashMap", "HashSet", "List", "Queue", "Stream", "Layer", "Scope", "Ref", "SynchronizedRef", "SubscriptionRef", "Duration", "Schedule", "Cause", "Exit", "Match", "Boolean", "Number", "String", "Struct", "Tuple", "Function", "Predicate", "Order", "Equivalence", "Context", "Brand", "Types"]);
|
|
23
|
+
/**
|
|
24
|
+
* Checks if a call expression already has a pure annotation.
|
|
25
|
+
*/
|
|
26
|
+
function hasPureAnnotation(node) {
|
|
27
|
+
const comments = node.leadingComments;
|
|
28
|
+
if (!comments) return false;
|
|
29
|
+
return comments.some(comment => comment.type === "CommentBlock" && (comment.value.includes("@__PURE__") || comment.value.includes("#__PURE__")));
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Checks if a call is to an Effect module method.
|
|
33
|
+
*/
|
|
34
|
+
function isEffectModuleCall(node) {
|
|
35
|
+
const callee = node.callee;
|
|
36
|
+
// Effect.succeed(...) or Option.some(...)
|
|
37
|
+
if (t.isMemberExpression(callee) && t.isIdentifier(callee.object) && EFFECT_MODULES.has(callee.object.name)) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Checks if a CallExpression is in a context where pure annotation is useful.
|
|
44
|
+
* Only annotate calls in variable declarations or export declarations.
|
|
45
|
+
*/
|
|
46
|
+
function isInAnnotatableContext(path) {
|
|
47
|
+
let parent = path.parentPath;
|
|
48
|
+
while (parent !== null) {
|
|
49
|
+
const node = parent.node;
|
|
50
|
+
// Variable declaration: const x = Effect.succeed(...)
|
|
51
|
+
if (t.isVariableDeclarator(node)) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
// Export: export const x = Effect.succeed(...)
|
|
55
|
+
if (t.isExportDefaultDeclaration(node) || t.isExportNamedDeclaration(node)) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
// Return statement: return Effect.succeed(...)
|
|
59
|
+
if (t.isReturnStatement(node)) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
// Arrow function body: () => Effect.succeed(...)
|
|
63
|
+
if (t.isArrowFunctionExpression(node)) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
// Stop at block statements
|
|
67
|
+
if (t.isBlockStatement(node) || t.isProgram(node)) {
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
parent = parent.parentPath;
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Transforms source code to add pure annotations to Effect calls.
|
|
76
|
+
*
|
|
77
|
+
* @since 0.0.1
|
|
78
|
+
* @category transform
|
|
79
|
+
*/
|
|
80
|
+
export function annotateEffects(code, id) {
|
|
81
|
+
let ast;
|
|
82
|
+
try {
|
|
83
|
+
ast = parse(code, {
|
|
84
|
+
sourceType: "module",
|
|
85
|
+
plugins: ["typescript", "jsx"],
|
|
86
|
+
sourceFilename: id
|
|
87
|
+
});
|
|
88
|
+
} catch {
|
|
89
|
+
return {
|
|
90
|
+
code,
|
|
91
|
+
transformed: false
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
let hasTransformed = false;
|
|
95
|
+
traverse(ast, {
|
|
96
|
+
CallExpression(path) {
|
|
97
|
+
// Skip if already has pure annotation
|
|
98
|
+
if (hasPureAnnotation(path.node)) return;
|
|
99
|
+
// Only annotate Effect module calls
|
|
100
|
+
if (!isEffectModuleCall(path.node)) return;
|
|
101
|
+
// Only annotate in useful contexts
|
|
102
|
+
if (!isInAnnotatableContext(path)) return;
|
|
103
|
+
// Add @__PURE__ annotation
|
|
104
|
+
t.addComment(path.node, "leading", "#__PURE__", false);
|
|
105
|
+
hasTransformed = true;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
if (!hasTransformed) {
|
|
109
|
+
return {
|
|
110
|
+
code,
|
|
111
|
+
transformed: false
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const result = generate(ast, {
|
|
115
|
+
sourceMaps: true,
|
|
116
|
+
sourceFileName: id,
|
|
117
|
+
comments: true
|
|
118
|
+
}, code);
|
|
119
|
+
return {
|
|
120
|
+
code: result.code,
|
|
121
|
+
map: result.map,
|
|
122
|
+
transformed: true
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=annotateEffects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"annotateEffects.js","names":["parse","_traverse","_generate","t","traverseModule","traverse","default","generateModule","generate","EFFECT_MODULES","Set","hasPureAnnotation","node","comments","leadingComments","some","comment","type","value","includes","isEffectModuleCall","callee","isMemberExpression","isIdentifier","object","has","name","isInAnnotatableContext","path","parent","parentPath","isVariableDeclarator","isExportDefaultDeclaration","isExportNamedDeclaration","isReturnStatement","isArrowFunctionExpression","isBlockStatement","isProgram","annotateEffects","code","id","ast","sourceType","plugins","sourceFilename","transformed","hasTransformed","CallExpression","addComment","result","sourceMaps","sourceFileName","map"],"sources":["../src/annotateEffects.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"esbuild.d.ts","sourceRoot":"","sources":["../src/esbuild.ts"],"names":[],"mappings":";AAiBA,wBAA+B"}
|
package/dist/esbuild.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* esbuild plugin for Effect source location tracing.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import esbuild from "esbuild"
|
|
7
|
+
* import effectSourceTrace from "@effect/unplugin/esbuild"
|
|
8
|
+
*
|
|
9
|
+
* esbuild.build({
|
|
10
|
+
* plugins: [effectSourceTrace()]
|
|
11
|
+
* })
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* @since 0.0.1
|
|
15
|
+
*/
|
|
16
|
+
import unplugin from "./index.js";
|
|
17
|
+
export default unplugin.esbuild;
|
|
18
|
+
//# sourceMappingURL=esbuild.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"esbuild.js","names":["unplugin","esbuild"],"sources":["../src/esbuild.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;;;;;;;;;;;AAeA,OAAOA,QAAQ,MAAM,YAAY;AAEjC,eAAeA,QAAQ,CAACC,OAAO","ignoreList":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SourceTraceOptions } from "./types.ts";
|
|
2
|
+
export { type TransformResult } from "./sourceTrace.ts";
|
|
3
|
+
export type { FilterPattern, InstrumentableEffect, SourceTraceOptions, SpanInstrumentationOptions } from "./types.ts";
|
|
4
|
+
/**
|
|
5
|
+
* Creates the Effect source trace unplugin.
|
|
6
|
+
*
|
|
7
|
+
* @since 0.0.1
|
|
8
|
+
* @category unplugin
|
|
9
|
+
*/
|
|
10
|
+
export declare const unplugin: import("unplugin").UnpluginInstance<SourceTraceOptions | undefined, boolean>;
|
|
11
|
+
export default unplugin;
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAAiB,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAEnE,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAA;AACvD,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAA;AAiCrH;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,8EAmBnB,CAAA;AAEF,eAAe,QAAQ,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build-time AST transformer for Effect source location tracing and auto-instrumentation.
|
|
3
|
+
*
|
|
4
|
+
* Provides two features:
|
|
5
|
+
* 1. **Source Tracing**: Transforms `yield*` expressions inside `Effect.gen()` to
|
|
6
|
+
* inject source location information via `CurrentStackFrame`.
|
|
7
|
+
* 2. **Span Instrumentation**: Wraps Effect combinators with `withSpan()` for
|
|
8
|
+
* automatic distributed tracing with OpenTelemetry semantic conventions.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* // vite.config.ts
|
|
13
|
+
* import { defineConfig } from "vite"
|
|
14
|
+
* import effectSourceTrace from "@effect/unplugin/vite"
|
|
15
|
+
*
|
|
16
|
+
* export default defineConfig({
|
|
17
|
+
* plugins: [effectSourceTrace({
|
|
18
|
+
* // Enable source tracing (default: true)
|
|
19
|
+
* sourceTrace: true,
|
|
20
|
+
* // Enable span instrumentation
|
|
21
|
+
* spans: {
|
|
22
|
+
* enabled: true,
|
|
23
|
+
* include: ["gen", "fork", "all", "forEach"],
|
|
24
|
+
* nameFormat: "function" // "function" | "location" | "full"
|
|
25
|
+
* }
|
|
26
|
+
* })]
|
|
27
|
+
* })
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @since 0.0.1
|
|
31
|
+
*/
|
|
32
|
+
import { createUnplugin } from "unplugin";
|
|
33
|
+
import { transform } from "./sourceTrace.js";
|
|
34
|
+
export {} from "./sourceTrace.js";
|
|
35
|
+
const defaultInclude = [/\.[jt]sx?$/];
|
|
36
|
+
const defaultExclude = [/node_modules/];
|
|
37
|
+
function toArray(value) {
|
|
38
|
+
if (value === undefined) return [];
|
|
39
|
+
if (Array.isArray(value)) return value;
|
|
40
|
+
return [value];
|
|
41
|
+
}
|
|
42
|
+
function createFilter(include, exclude) {
|
|
43
|
+
const includePatterns = toArray(include ?? defaultInclude);
|
|
44
|
+
const excludePatterns = toArray(exclude ?? defaultExclude);
|
|
45
|
+
return id => {
|
|
46
|
+
for (const pattern of excludePatterns) {
|
|
47
|
+
if (typeof pattern === "string" ? id.includes(pattern) : pattern.test(id)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for (const pattern of includePatterns) {
|
|
52
|
+
if (typeof pattern === "string" ? id.includes(pattern) : pattern.test(id)) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Creates the Effect source trace unplugin.
|
|
61
|
+
*
|
|
62
|
+
* @since 0.0.1
|
|
63
|
+
* @category unplugin
|
|
64
|
+
*/
|
|
65
|
+
export const unplugin = /*#__PURE__*/createUnplugin((options = {}) => {
|
|
66
|
+
const filter = createFilter(options.include, options.exclude);
|
|
67
|
+
return {
|
|
68
|
+
name: "effect-source-trace",
|
|
69
|
+
enforce: "pre",
|
|
70
|
+
transformInclude(id) {
|
|
71
|
+
return filter(id);
|
|
72
|
+
},
|
|
73
|
+
transform(code, id) {
|
|
74
|
+
const result = transform(code, id, options);
|
|
75
|
+
if (!result.transformed) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
code: result.code,
|
|
80
|
+
map: result.map
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
export default unplugin;
|
|
86
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["createUnplugin","transform","defaultInclude","defaultExclude","toArray","value","undefined","Array","isArray","createFilter","include","exclude","includePatterns","excludePatterns","id","pattern","includes","test","unplugin","options","filter","name","enforce","transformInclude","code","result","transformed","map"],"sources":["../src/index.ts"],"sourcesContent":[null],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAASA,cAAc,QAAyD,UAAU;AAC1F,SAASC,SAAS,QAAQ,kBAAkB;AAG5C,eAAqC,kBAAkB;AAGvD,MAAMC,cAAc,GAAmC,CAAC,YAAY,CAAC;AACrE,MAAMC,cAAc,GAAmC,CAAC,cAAc,CAAC;AAEvE,SAASC,OAAOA,CAACC,KAAgC;EAC/C,IAAIA,KAAK,KAAKC,SAAS,EAAE,OAAO,EAAE;EAClC,IAAIC,KAAK,CAACC,OAAO,CAACH,KAAK,CAAC,EAAE,OAAOA,KAAK;EACtC,OAAO,CAACA,KAAwB,CAAC;AACnC;AAEA,SAASI,YAAYA,CACnBC,OAAkC,EAClCC,OAAkC;EAElC,MAAMC,eAAe,GAAGR,OAAO,CAACM,OAAO,IAAIR,cAAc,CAAC;EAC1D,MAAMW,eAAe,GAAGT,OAAO,CAACO,OAAO,IAAIR,cAAc,CAAC;EAE1D,OAAQW,EAAU,IAAa;IAC7B,KAAK,MAAMC,OAAO,IAAIF,eAAe,EAAE;MACrC,IAAI,OAAOE,OAAO,KAAK,QAAQ,GAAGD,EAAE,CAACE,QAAQ,CAACD,OAAO,CAAC,GAAGA,OAAO,CAACE,IAAI,CAACH,EAAE,CAAC,EAAE;QACzE,OAAO,KAAK;MACd;IACF;IACA,KAAK,MAAMC,OAAO,IAAIH,eAAe,EAAE;MACrC,IAAI,OAAOG,OAAO,KAAK,QAAQ,GAAGD,EAAE,CAACE,QAAQ,CAACD,OAAO,CAAC,GAAGA,OAAO,CAACE,IAAI,CAACH,EAAE,CAAC,EAAE;QACzE,OAAO,IAAI;MACb;IACF;IACA,OAAO,KAAK;EACd,CAAC;AACH;AAEA;;;;;;AAMA,OAAO,MAAMI,QAAQ,gBAAGlB,cAAc,CAAiC,CAACmB,OAAO,GAAG,EAAE,KAAI;EACtF,MAAMC,MAAM,GAAGX,YAAY,CAACU,OAAO,CAACT,OAAO,EAAES,OAAO,CAACR,OAAO,CAAC;EAE7D,OAAO;IACLU,IAAI,EAAE,qBAAqB;IAC3BC,OAAO,EAAE,KAAK;IAEdC,gBAAgBA,CAACT,EAAE;MACjB,OAAOM,MAAM,CAACN,EAAE,CAAC;IACnB,CAAC;IAEDb,SAASA,CAACuB,IAAI,EAAEV,EAAE;MAChB,MAAMW,MAAM,GAAGxB,SAAS,CAACuB,IAAI,EAAEV,EAAE,EAAEK,OAAO,CAAC;MAC3C,IAAI,CAACM,MAAM,CAACC,WAAW,EAAE;QACvB,OAAO,IAAI;MACb;MACA,OAAO;QAAEF,IAAI,EAAEC,MAAM,CAACD,IAAI;QAAEG,GAAG,EAAEF,MAAM,CAACE;MAAG,CAA6B;IAC1E;GACD;AACH,CAAC,CAAC;AAEF,eAAeT,QAAQ","ignoreList":[]}
|
package/dist/rollup.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rollup.d.ts","sourceRoot":"","sources":["../src/rollup.ts"],"names":[],"mappings":";AAiBA,wBAA8B"}
|