fcis 0.1.0

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.
Files changed (151) hide show
  1. package/.plans/001-fcis-analyzer.md +832 -0
  2. package/.plans/002-fcis-analyzer-improvements.md +205 -0
  3. package/README.md +272 -0
  4. package/TECHNICAL.md +386 -0
  5. package/dist/cli.d.ts +1 -0
  6. package/dist/cli.js +1836 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/index.d.ts +709 -0
  9. package/dist/index.js +1845 -0
  10. package/dist/index.js.map +1 -0
  11. package/package.json +47 -0
  12. package/pnpm-workspace.yaml +0 -0
  13. package/src/analyzer.ts +266 -0
  14. package/src/classification/classifier.ts +156 -0
  15. package/src/classification/derive-status.ts +171 -0
  16. package/src/classification/quality-scorer.ts +481 -0
  17. package/src/cli.ts +286 -0
  18. package/src/detection/detect-markers.ts +480 -0
  19. package/src/detection/markers.ts +332 -0
  20. package/src/extraction/extract-functions.ts +570 -0
  21. package/src/extraction/extractor.ts +188 -0
  22. package/src/index.ts +111 -0
  23. package/src/reporting/report-console.ts +416 -0
  24. package/src/reporting/report-json.ts +232 -0
  25. package/src/scoring/scorer.ts +504 -0
  26. package/src/types.ts +248 -0
  27. package/tests/classifier.test.ts +480 -0
  28. package/tests/derive-status.test.ts +464 -0
  29. package/tests/detect-markers.test.ts +639 -0
  30. package/tests/extractor.test.ts +155 -0
  31. package/tests/integration.test.ts +706 -0
  32. package/tests/quality-scorer.test.ts +650 -0
  33. package/tests/scorer.test.ts +768 -0
  34. package/tsconfig.json +34 -0
  35. package/tsup.config.ts +17 -0
  36. package/vendor/ts-morph/.editorconfig +10 -0
  37. package/vendor/ts-morph/.gitattributes +11 -0
  38. package/vendor/ts-morph/.github/CODE_OF_CONDUCT.md +77 -0
  39. package/vendor/ts-morph/.github/ISSUE_TEMPLATE/bug_report.md +29 -0
  40. package/vendor/ts-morph/.github/ISSUE_TEMPLATE/custom.md +4 -0
  41. package/vendor/ts-morph/.github/ISSUE_TEMPLATE/feature_request.md +18 -0
  42. package/vendor/ts-morph/.github/workflows/ci.yml +50 -0
  43. package/vendor/ts-morph/.github/workflows/publish.yml +53 -0
  44. package/vendor/ts-morph/.vscode/settings.json +10 -0
  45. package/vendor/ts-morph/CONTRIBUTING.md +23 -0
  46. package/vendor/ts-morph/DEVELOPMENT.md +32 -0
  47. package/vendor/ts-morph/LICENSE +21 -0
  48. package/vendor/ts-morph/deno.json +8 -0
  49. package/vendor/ts-morph/deno.lock +1233 -0
  50. package/vendor/ts-morph/docs/CNAME +1 -0
  51. package/vendor/ts-morph/docs/Gemfile +2 -0
  52. package/vendor/ts-morph/docs/_config.yml +5 -0
  53. package/vendor/ts-morph/docs/_layouts/default.html +159 -0
  54. package/vendor/ts-morph/docs/_script-templates/main.ts +116 -0
  55. package/vendor/ts-morph/docs/assets/css/style.scss +212 -0
  56. package/vendor/ts-morph/docs/details/ambient.md +38 -0
  57. package/vendor/ts-morph/docs/details/async.md +31 -0
  58. package/vendor/ts-morph/docs/details/classes.md +314 -0
  59. package/vendor/ts-morph/docs/details/comment-ranges.md +7 -0
  60. package/vendor/ts-morph/docs/details/comments.md +122 -0
  61. package/vendor/ts-morph/docs/details/decorators.md +119 -0
  62. package/vendor/ts-morph/docs/details/documentation.md +73 -0
  63. package/vendor/ts-morph/docs/details/enums.md +117 -0
  64. package/vendor/ts-morph/docs/details/exports.md +308 -0
  65. package/vendor/ts-morph/docs/details/expressions.md +46 -0
  66. package/vendor/ts-morph/docs/details/functions.md +150 -0
  67. package/vendor/ts-morph/docs/details/generators.md +27 -0
  68. package/vendor/ts-morph/docs/details/identifiers.md +79 -0
  69. package/vendor/ts-morph/docs/details/imports.md +191 -0
  70. package/vendor/ts-morph/docs/details/index.md +52 -0
  71. package/vendor/ts-morph/docs/details/initializers.md +40 -0
  72. package/vendor/ts-morph/docs/details/interfaces.md +218 -0
  73. package/vendor/ts-morph/docs/details/literals.md +20 -0
  74. package/vendor/ts-morph/docs/details/modifiers.md +38 -0
  75. package/vendor/ts-morph/docs/details/modules.md +113 -0
  76. package/vendor/ts-morph/docs/details/namespaces.md +7 -0
  77. package/vendor/ts-morph/docs/details/object-literal-expressions.md +106 -0
  78. package/vendor/ts-morph/docs/details/parameters.md +64 -0
  79. package/vendor/ts-morph/docs/details/signatures.md +41 -0
  80. package/vendor/ts-morph/docs/details/source-files.md +292 -0
  81. package/vendor/ts-morph/docs/details/type-aliases.md +34 -0
  82. package/vendor/ts-morph/docs/details/type-parameters.md +72 -0
  83. package/vendor/ts-morph/docs/details/types.md +254 -0
  84. package/vendor/ts-morph/docs/details/variables.md +110 -0
  85. package/vendor/ts-morph/docs/emitting.md +151 -0
  86. package/vendor/ts-morph/docs/index.md +25 -0
  87. package/vendor/ts-morph/docs/manipulation/code-writer.md +20 -0
  88. package/vendor/ts-morph/docs/manipulation/formatting.md +76 -0
  89. package/vendor/ts-morph/docs/manipulation/index.md +136 -0
  90. package/vendor/ts-morph/docs/manipulation/order.md +14 -0
  91. package/vendor/ts-morph/docs/manipulation/performance.md +222 -0
  92. package/vendor/ts-morph/docs/manipulation/removing.md +31 -0
  93. package/vendor/ts-morph/docs/manipulation/renaming.md +106 -0
  94. package/vendor/ts-morph/docs/manipulation/settings.md +76 -0
  95. package/vendor/ts-morph/docs/manipulation/structures.md +117 -0
  96. package/vendor/ts-morph/docs/manipulation/transforms.md +84 -0
  97. package/vendor/ts-morph/docs/metrics/performance.json +4 -0
  98. package/vendor/ts-morph/docs/navigation/ambient-modules.md +22 -0
  99. package/vendor/ts-morph/docs/navigation/compiler-nodes.md +82 -0
  100. package/vendor/ts-morph/docs/navigation/directories.md +287 -0
  101. package/vendor/ts-morph/docs/navigation/example.md +50 -0
  102. package/vendor/ts-morph/docs/navigation/finding-references.md +53 -0
  103. package/vendor/ts-morph/docs/navigation/getting-source-files.md +59 -0
  104. package/vendor/ts-morph/docs/navigation/images/getChildrenVsForEachChild.gif +0 -0
  105. package/vendor/ts-morph/docs/navigation/index.md +94 -0
  106. package/vendor/ts-morph/docs/navigation/language-service.md +23 -0
  107. package/vendor/ts-morph/docs/navigation/program.md +25 -0
  108. package/vendor/ts-morph/docs/navigation/type-checker.md +33 -0
  109. package/vendor/ts-morph/docs/setup/adding-source-files.md +145 -0
  110. package/vendor/ts-morph/docs/setup/ast-viewers.md +46 -0
  111. package/vendor/ts-morph/docs/setup/diagnostics.md +109 -0
  112. package/vendor/ts-morph/docs/setup/file-system.md +106 -0
  113. package/vendor/ts-morph/docs/setup/images/atom-ast.png +0 -0
  114. package/vendor/ts-morph/docs/setup/images/atom-ast_small.png +0 -0
  115. package/vendor/ts-morph/docs/setup/images/atom-command-palette.png +0 -0
  116. package/vendor/ts-morph/docs/setup/images/atom-file.png +0 -0
  117. package/vendor/ts-morph/docs/setup/images/ts-ast-viewer.png +0 -0
  118. package/vendor/ts-morph/docs/setup/index.md +94 -0
  119. package/vendor/ts-morph/docs/utilities.md +55 -0
  120. package/vendor/ts-morph/dprint.json +23 -0
  121. package/vendor/ts-morph/package.json +30 -0
  122. package/vendor/ts-morph/packages/bootstrap/LICENSE +21 -0
  123. package/vendor/ts-morph/packages/bootstrap/lib/ts-morph-bootstrap.d.ts +397 -0
  124. package/vendor/ts-morph/packages/bootstrap/package.json +46 -0
  125. package/vendor/ts-morph/packages/bootstrap/readme.md +200 -0
  126. package/vendor/ts-morph/packages/common/LICENSE +21 -0
  127. package/vendor/ts-morph/packages/common/lib/ts-morph-common.d.ts +1082 -0
  128. package/vendor/ts-morph/packages/common/lib/typescript.d.ts +11439 -0
  129. package/vendor/ts-morph/packages/common/package.json +65 -0
  130. package/vendor/ts-morph/packages/common/readme.md +5 -0
  131. package/vendor/ts-morph/packages/scripts/changeTypeScriptVersion.ts +28 -0
  132. package/vendor/ts-morph/packages/scripts/createDeclarationProject.ts +47 -0
  133. package/vendor/ts-morph/packages/scripts/deps.ts +2 -0
  134. package/vendor/ts-morph/packages/scripts/execScript.ts +31 -0
  135. package/vendor/ts-morph/packages/scripts/folders.ts +11 -0
  136. package/vendor/ts-morph/packages/scripts/getDevCompilerVersions.ts +19 -0
  137. package/vendor/ts-morph/packages/scripts/mod.ts +7 -0
  138. package/vendor/ts-morph/packages/scripts/utils/Memoize.ts +36 -0
  139. package/vendor/ts-morph/packages/scripts/utils/forEachTypeText.ts +23 -0
  140. package/vendor/ts-morph/packages/scripts/utils/makeConstructorsPrivate.ts +26 -0
  141. package/vendor/ts-morph/packages/scripts/utils/mod.ts +4 -0
  142. package/vendor/ts-morph/packages/scripts/utils/printDiagnostics.ts +10 -0
  143. package/vendor/ts-morph/packages/ts-morph/LICENSE +21 -0
  144. package/vendor/ts-morph/packages/ts-morph/lib/ts-morph.d.ts +11198 -0
  145. package/vendor/ts-morph/packages/ts-morph/package.json +78 -0
  146. package/vendor/ts-morph/packages/ts-morph/readme.md +111 -0
  147. package/vendor/ts-morph/readme.md +14 -0
  148. package/vendor/ts-morph/rfcs/README.md +13 -0
  149. package/vendor/ts-morph/rfcs/RFC-0001 - Inserting Into Statements Handling Comments.md +181 -0
  150. package/vendor/ts-morph/tsconfig.common.json +17 -0
  151. package/vitest.config.ts +16 -0
@@ -0,0 +1,480 @@
1
+ /**
2
+ * Marker Detection - Pure Core
3
+ *
4
+ * Detects impurity markers in extracted functions. This is a PURE module -
5
+ * all functions take data in and return data out with no I/O.
6
+ *
7
+ * Marker categories:
8
+ * - Async/Await: await expressions indicate I/O suspension
9
+ * - Database: Prisma/db calls
10
+ * - Network: fetch, axios, http calls
11
+ * - File System: fs operations
12
+ * - Environment: process.env access
13
+ * - Logging: console, logger calls
14
+ * - Queue/Events: enqueue, emit calls
15
+ */
16
+
17
+ import type {
18
+ DetectionContext,
19
+ ExtractedFunction,
20
+ ImpurityMarker,
21
+ } from '../types.js'
22
+
23
+ /**
24
+ * Database operations that indicate impurity
25
+ */
26
+ const DATABASE_OPERATIONS = new Set([
27
+ 'findFirst',
28
+ 'findMany',
29
+ 'findUnique',
30
+ 'findUniqueOrThrow',
31
+ 'findFirstOrThrow',
32
+ 'create',
33
+ 'createMany',
34
+ 'update',
35
+ 'updateMany',
36
+ 'delete',
37
+ 'deleteMany',
38
+ 'upsert',
39
+ 'aggregate',
40
+ 'count',
41
+ 'groupBy',
42
+ '$transaction',
43
+ '$queryRaw',
44
+ '$executeRaw',
45
+ ])
46
+
47
+ /**
48
+ * Telemetry/analytics patterns
49
+ */
50
+ const TELEMETRY_PATTERNS = [
51
+ /^track[A-Z]/,
52
+ /^analytics\./,
53
+ /^segment\./,
54
+ /^mixpanel\./,
55
+ /^amplitude\./,
56
+ /^posthog\./,
57
+ /^gtag\(/,
58
+ /^ga\(/,
59
+ ]
60
+
61
+ /**
62
+ * Detect all impurity markers in a function
63
+ *
64
+ * @param fn - The extracted function to analyze
65
+ * @param context - Detection context including imports
66
+ * @returns Array of detected impurity markers
67
+ */
68
+ export function detectMarkers(
69
+ fn: ExtractedFunction,
70
+ context: DetectionContext,
71
+ ): ImpurityMarker[] {
72
+ const markers: ImpurityMarker[] = []
73
+
74
+ // Detect await expressions
75
+ detectAwaitMarkers(fn, markers)
76
+
77
+ // Detect database calls
78
+ detectDatabaseMarkers(fn, markers)
79
+
80
+ // Detect network calls
81
+ detectNetworkMarkers(fn, context, markers)
82
+
83
+ // Detect file system usage
84
+ detectFileSystemMarkers(fn, context, markers)
85
+
86
+ // Detect environment access
87
+ detectEnvMarkers(fn, markers)
88
+
89
+ // Detect logging
90
+ detectLoggingMarkers(fn, context, markers)
91
+
92
+ // Detect telemetry
93
+ detectTelemetryMarkers(fn, markers)
94
+
95
+ // Detect queue operations
96
+ detectQueueMarkers(fn, markers)
97
+
98
+ // Detect event emissions
99
+ detectEventMarkers(fn, markers)
100
+
101
+ return markers
102
+ }
103
+
104
+ /**
105
+ * Detect await expressions
106
+ */
107
+ function detectAwaitMarkers(
108
+ fn: ExtractedFunction,
109
+ markers: ImpurityMarker[],
110
+ ): void {
111
+ if (fn.hasAwait) {
112
+ // Find awaited call sites for details
113
+ const awaitedCalls = fn.callSites.filter(cs => cs.isAwaited)
114
+ if (awaitedCalls.length > 0) {
115
+ for (const call of awaitedCalls) {
116
+ markers.push({
117
+ type: 'await-expression',
118
+ detail: `await ${call.expression}`,
119
+ line: call.line,
120
+ })
121
+ }
122
+ } else {
123
+ // Await on something other than a call (e.g., await promise variable)
124
+ markers.push({
125
+ type: 'await-expression',
126
+ detail: 'await expression',
127
+ })
128
+ }
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Detect database calls (Prisma pattern: db.<entity>.<operation>)
134
+ */
135
+ function detectDatabaseMarkers(
136
+ fn: ExtractedFunction,
137
+ markers: ImpurityMarker[],
138
+ ): void {
139
+ for (const callSite of fn.callSites) {
140
+ if (isDatabaseCall(callSite.expression)) {
141
+ markers.push({
142
+ type: 'database-call',
143
+ detail: callSite.expression,
144
+ line: callSite.line,
145
+ })
146
+ }
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Check if an expression is a database call
152
+ */
153
+ function isDatabaseCall(expression: string): boolean {
154
+ // Match patterns like: db.user.findFirst, prisma.user.create, ctx.db.user.findMany
155
+ const parts = expression.split('.')
156
+
157
+ // Need at least 3 parts: db.entity.operation
158
+ if (parts.length < 3) {
159
+ // Check for direct prisma operations like prisma.$transaction
160
+ if (parts.length === 2) {
161
+ const [base, operation] = parts
162
+ if (
163
+ (base === 'db' || base === 'prisma') &&
164
+ operation &&
165
+ DATABASE_OPERATIONS.has(operation)
166
+ ) {
167
+ return true
168
+ }
169
+ }
170
+ return false
171
+ }
172
+
173
+ // Check if any part is 'db' or 'prisma' and the last part is a database operation
174
+ const lastPart = parts[parts.length - 1]
175
+ if (!lastPart || !DATABASE_OPERATIONS.has(lastPart)) {
176
+ return false
177
+ }
178
+
179
+ // Check for db or prisma in the chain
180
+ return parts.some(
181
+ part => part === 'db' || part === 'prisma' || part === 'database',
182
+ )
183
+ }
184
+
185
+ /**
186
+ * Detect network/HTTP calls
187
+ *
188
+ * Note: We only detect actual call sites, not file-level imports.
189
+ * A function is only marked with network markers if it actually calls
190
+ * fetch, axios, or other HTTP client functions.
191
+ */
192
+ function detectNetworkMarkers(
193
+ fn: ExtractedFunction,
194
+ _context: DetectionContext,
195
+ markers: ImpurityMarker[],
196
+ ): void {
197
+ // Check for fetch calls
198
+ for (const callSite of fn.callSites) {
199
+ if (
200
+ callSite.expression === 'fetch' ||
201
+ callSite.expression.endsWith('.fetch')
202
+ ) {
203
+ markers.push({
204
+ type: 'network-fetch',
205
+ detail: callSite.expression,
206
+ line: callSite.line,
207
+ })
208
+ }
209
+
210
+ // Check for axios calls
211
+ if (
212
+ callSite.expression.startsWith('axios') ||
213
+ callSite.expression === 'axios'
214
+ ) {
215
+ markers.push({
216
+ type: 'network-http',
217
+ detail: callSite.expression,
218
+ line: callSite.line,
219
+ })
220
+ }
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Detect file system usage
226
+ *
227
+ * Note: We only detect actual call sites, not file-level imports.
228
+ * A function is only marked with fs markers if it actually calls
229
+ * fs functions like readFile, writeFile, etc.
230
+ */
231
+ function detectFileSystemMarkers(
232
+ fn: ExtractedFunction,
233
+ _context: DetectionContext,
234
+ markers: ImpurityMarker[],
235
+ ): void {
236
+ // Check for fs calls in the function
237
+ for (const callSite of fn.callSites) {
238
+ if (isFsCall(callSite.expression)) {
239
+ markers.push({
240
+ type: 'fs-call',
241
+ detail: callSite.expression,
242
+ line: callSite.line,
243
+ })
244
+ }
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Check if an expression is a file system call
250
+ */
251
+ function isFsCall(expression: string): boolean {
252
+ const parts = expression.split('.')
253
+ return parts.some(
254
+ part => part === 'fs' || part === 'readFile' || part === 'writeFile',
255
+ )
256
+ }
257
+
258
+ /**
259
+ * Detect environment variable access
260
+ */
261
+ function detectEnvMarkers(
262
+ fn: ExtractedFunction,
263
+ markers: ImpurityMarker[],
264
+ ): void {
265
+ for (const chain of fn.propertyAccessChains) {
266
+ if (chain.startsWith('process.env')) {
267
+ markers.push({
268
+ type: 'env-access',
269
+ detail: chain,
270
+ })
271
+ }
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Detect logging calls
277
+ *
278
+ * Note: We only detect actual call sites, not file-level imports.
279
+ * A function is only marked with logging markers if it actually calls
280
+ * console.log, logger.info, log(), etc.
281
+ */
282
+ function detectLoggingMarkers(
283
+ fn: ExtractedFunction,
284
+ _context: DetectionContext,
285
+ markers: ImpurityMarker[],
286
+ ): void {
287
+ for (const callSite of fn.callSites) {
288
+ // Console logging
289
+ if (isConsoleLog(callSite.expression)) {
290
+ markers.push({
291
+ type: 'console-log',
292
+ detail: callSite.expression,
293
+ line: callSite.line,
294
+ })
295
+ }
296
+
297
+ // Logger calls
298
+ if (isLoggerCall(callSite.expression)) {
299
+ markers.push({
300
+ type: 'logging',
301
+ detail: callSite.expression,
302
+ line: callSite.line,
303
+ })
304
+ }
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Check if an expression is a console.log call
310
+ */
311
+ function isConsoleLog(expression: string): boolean {
312
+ return (
313
+ expression === 'console.log' ||
314
+ expression === 'console.error' ||
315
+ expression === 'console.warn' ||
316
+ expression === 'console.info' ||
317
+ expression === 'console.debug'
318
+ )
319
+ }
320
+
321
+ /**
322
+ * Check if an expression is a logger call
323
+ */
324
+ function isLoggerCall(expression: string): boolean {
325
+ const parts = expression.split('.')
326
+ if (parts.length < 2) {
327
+ return expression === 'log'
328
+ }
329
+
330
+ const base = parts[0]
331
+ const method = parts[parts.length - 1]
332
+
333
+ if (base === 'logger' || base === 'log') {
334
+ return true
335
+ }
336
+
337
+ // Check for common logger methods
338
+ const logMethods = new Set([
339
+ 'info',
340
+ 'warn',
341
+ 'error',
342
+ 'debug',
343
+ 'trace',
344
+ 'fatal',
345
+ ])
346
+ return (
347
+ (parts.includes('logger') || parts.includes('log')) &&
348
+ method !== undefined &&
349
+ logMethods.has(method)
350
+ )
351
+ }
352
+
353
+ /**
354
+ * Detect telemetry/analytics calls
355
+ */
356
+ function detectTelemetryMarkers(
357
+ fn: ExtractedFunction,
358
+ markers: ImpurityMarker[],
359
+ ): void {
360
+ for (const callSite of fn.callSites) {
361
+ if (isTelemetryCall(callSite.expression)) {
362
+ markers.push({
363
+ type: 'telemetry',
364
+ detail: callSite.expression,
365
+ line: callSite.line,
366
+ })
367
+ }
368
+ }
369
+ }
370
+
371
+ /**
372
+ * Check if an expression is a telemetry call
373
+ */
374
+ function isTelemetryCall(expression: string): boolean {
375
+ return TELEMETRY_PATTERNS.some(pattern => pattern.test(expression))
376
+ }
377
+
378
+ /**
379
+ * Detect queue operations
380
+ */
381
+ function detectQueueMarkers(
382
+ fn: ExtractedFunction,
383
+ markers: ImpurityMarker[],
384
+ ): void {
385
+ for (const callSite of fn.callSites) {
386
+ if (isQueueCall(callSite.expression)) {
387
+ markers.push({
388
+ type: 'queue-enqueue',
389
+ detail: callSite.expression,
390
+ line: callSite.line,
391
+ })
392
+ }
393
+ }
394
+ }
395
+
396
+ /**
397
+ * Check if an expression is a queue operation
398
+ */
399
+ function isQueueCall(expression: string): boolean {
400
+ return (
401
+ expression.endsWith('.enqueue') ||
402
+ expression.endsWith('.add') ||
403
+ expression.endsWith('.publish') ||
404
+ expression.includes('queue.') ||
405
+ expression.includes('Queue.')
406
+ )
407
+ }
408
+
409
+ /**
410
+ * Detect event emissions
411
+ */
412
+ function detectEventMarkers(
413
+ fn: ExtractedFunction,
414
+ markers: ImpurityMarker[],
415
+ ): void {
416
+ for (const callSite of fn.callSites) {
417
+ if (isEventEmit(callSite.expression)) {
418
+ markers.push({
419
+ type: 'event-emit',
420
+ detail: callSite.expression,
421
+ line: callSite.line,
422
+ })
423
+ }
424
+ }
425
+ }
426
+
427
+ /**
428
+ * Check if an expression is an event emission
429
+ */
430
+ function isEventEmit(expression: string): boolean {
431
+ return (
432
+ expression.endsWith('.emit') ||
433
+ expression.endsWith('.dispatch') ||
434
+ expression.endsWith('.trigger') ||
435
+ expression.endsWith('.fire')
436
+ )
437
+ }
438
+
439
+ /**
440
+ * Create detection context from file imports
441
+ */
442
+ export function createDetectionContext(
443
+ imports: import('../types.js').FileImports,
444
+ ): DetectionContext {
445
+ // Identify imports from .pure files
446
+ const pureFileImports = new Set<string>()
447
+
448
+ for (const imp of imports.imports) {
449
+ if (
450
+ imp.moduleSpecifier.endsWith('.pure') ||
451
+ imp.moduleSpecifier.includes('.pure/')
452
+ ) {
453
+ pureFileImports.add(imp.moduleSpecifier)
454
+ }
455
+ }
456
+
457
+ return {
458
+ imports,
459
+ pureFileImports,
460
+ }
461
+ }
462
+
463
+ /**
464
+ * Get the names of functions imported from .pure files
465
+ */
466
+ export function getPureImportedFunctions(
467
+ context: DetectionContext,
468
+ ): Set<string> {
469
+ const pureFunctions = new Set<string>()
470
+
471
+ for (const imp of context.imports.imports) {
472
+ if (context.pureFileImports.has(imp.moduleSpecifier)) {
473
+ for (const name of imp.namedImports) {
474
+ pureFunctions.add(name)
475
+ }
476
+ }
477
+ }
478
+
479
+ return pureFunctions
480
+ }